@liustack/pptfast 0.3.0 → 0.6.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 +34 -28
- package/README.zh-CN.md +33 -27
- package/dist/chunk-2ZOMS6G3.js +564 -0
- package/dist/chunk-2ZOMS6G3.js.map +1 -0
- package/dist/{chunk-ZXWCOWJI.js → chunk-7V73LHUQ.js} +3660 -1745
- package/dist/chunk-7V73LHUQ.js.map +1 -0
- package/dist/chunk-HFRNKYZ6.js +54 -0
- package/dist/chunk-HFRNKYZ6.js.map +1 -0
- package/dist/chunk-L524YK63.js +19 -0
- package/dist/chunk-L524YK63.js.map +1 -0
- package/dist/cli.js +340 -72
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +992 -210
- package/dist/index.js +42 -34
- package/dist/node.d.ts +3 -2
- package/dist/node.js +2 -2
- package/dist/pixel-audit-ZRFTV4V7.js +218 -0
- package/dist/pixel-audit-ZRFTV4V7.js.map +1 -0
- package/dist/registry-V079Jkry.d.ts +40 -0
- package/package.json +10 -3
- package/dist/chunk-G76EVNVT.js +0 -14
- package/dist/chunk-G76EVNVT.js.map +0 -1
- package/dist/chunk-SROEW6BH.js +0 -34
- package/dist/chunk-SROEW6BH.js.map +0 -1
- package/dist/chunk-ZXWCOWJI.js.map +0 -1
- package/dist/registry-CYKxZ0-U.d.ts +0 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export { P as PptfastPlatform, i as installPlatform } from './registry-
|
|
2
|
+
export { P as PptfastPlatform, R as RasterizedImage, i as installPlatform } from './registry-V079Jkry.js';
|
|
3
3
|
|
|
4
|
-
declare const VERSION = "0.
|
|
4
|
+
declare const VERSION = "0.6.0";
|
|
5
5
|
|
|
6
6
|
/** Error class thrown by pptfast's public API surface (validation, rendering, export). */
|
|
7
7
|
declare class PptfastError extends Error {
|
|
@@ -392,9 +392,51 @@ declare const ComponentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
392
392
|
vs: "vs";
|
|
393
393
|
before_after: "before_after";
|
|
394
394
|
}>>;
|
|
395
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
396
|
+
type: z.ZodLiteral<"swot">;
|
|
397
|
+
strengths: z.ZodArray<z.ZodString>;
|
|
398
|
+
weaknesses: z.ZodArray<z.ZodString>;
|
|
399
|
+
opportunities: z.ZodArray<z.ZodString>;
|
|
400
|
+
threats: z.ZodArray<z.ZodString>;
|
|
401
|
+
labels: z.ZodOptional<z.ZodObject<{
|
|
402
|
+
strengths: z.ZodOptional<z.ZodString>;
|
|
403
|
+
weaknesses: z.ZodOptional<z.ZodString>;
|
|
404
|
+
opportunities: z.ZodOptional<z.ZodString>;
|
|
405
|
+
threats: z.ZodOptional<z.ZodString>;
|
|
406
|
+
}, z.core.$strict>>;
|
|
407
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
408
|
+
type: z.ZodLiteral<"bmc">;
|
|
409
|
+
key_partners: z.ZodArray<z.ZodString>;
|
|
410
|
+
key_activities: z.ZodArray<z.ZodString>;
|
|
411
|
+
key_resources: z.ZodArray<z.ZodString>;
|
|
412
|
+
value_propositions: z.ZodArray<z.ZodString>;
|
|
413
|
+
customer_relationships: z.ZodArray<z.ZodString>;
|
|
414
|
+
channels: z.ZodArray<z.ZodString>;
|
|
415
|
+
customer_segments: z.ZodArray<z.ZodString>;
|
|
416
|
+
cost_structure: z.ZodArray<z.ZodString>;
|
|
417
|
+
revenue_streams: z.ZodArray<z.ZodString>;
|
|
418
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
419
|
+
type: z.ZodLiteral<"waterfall">;
|
|
420
|
+
items: z.ZodArray<z.ZodObject<{
|
|
421
|
+
label: z.ZodString;
|
|
422
|
+
value: z.ZodNumber;
|
|
423
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
424
|
+
delta: "delta";
|
|
425
|
+
total: "total";
|
|
426
|
+
}>>;
|
|
427
|
+
}, z.core.$strict>>;
|
|
428
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
429
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
430
|
+
type: z.ZodLiteral<"gantt">;
|
|
431
|
+
items: z.ZodArray<z.ZodObject<{
|
|
432
|
+
label: z.ZodString;
|
|
433
|
+
start: z.ZodNumber;
|
|
434
|
+
end: z.ZodNumber;
|
|
435
|
+
}, z.core.$strict>>;
|
|
436
|
+
axis_labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
395
437
|
}, z.core.$strict>], "type">;
|
|
396
438
|
/**
|
|
397
|
-
* All
|
|
439
|
+
* All 28 component `type` discriminant values, derived from `ComponentSchema`
|
|
398
440
|
* itself (never hand-copied) so this list can't drift from the union above.
|
|
399
441
|
* Typed as plain `readonly string[]` rather than `Component["type"][]` —
|
|
400
442
|
* every consumer of this list (W5's plan `focus` vocabulary gate,
|
|
@@ -671,6 +713,48 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
671
713
|
vs: "vs";
|
|
672
714
|
before_after: "before_after";
|
|
673
715
|
}>>;
|
|
716
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
717
|
+
type: z.ZodLiteral<"swot">;
|
|
718
|
+
strengths: z.ZodArray<z.ZodString>;
|
|
719
|
+
weaknesses: z.ZodArray<z.ZodString>;
|
|
720
|
+
opportunities: z.ZodArray<z.ZodString>;
|
|
721
|
+
threats: z.ZodArray<z.ZodString>;
|
|
722
|
+
labels: z.ZodOptional<z.ZodObject<{
|
|
723
|
+
strengths: z.ZodOptional<z.ZodString>;
|
|
724
|
+
weaknesses: z.ZodOptional<z.ZodString>;
|
|
725
|
+
opportunities: z.ZodOptional<z.ZodString>;
|
|
726
|
+
threats: z.ZodOptional<z.ZodString>;
|
|
727
|
+
}, z.core.$strict>>;
|
|
728
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
729
|
+
type: z.ZodLiteral<"bmc">;
|
|
730
|
+
key_partners: z.ZodArray<z.ZodString>;
|
|
731
|
+
key_activities: z.ZodArray<z.ZodString>;
|
|
732
|
+
key_resources: z.ZodArray<z.ZodString>;
|
|
733
|
+
value_propositions: z.ZodArray<z.ZodString>;
|
|
734
|
+
customer_relationships: z.ZodArray<z.ZodString>;
|
|
735
|
+
channels: z.ZodArray<z.ZodString>;
|
|
736
|
+
customer_segments: z.ZodArray<z.ZodString>;
|
|
737
|
+
cost_structure: z.ZodArray<z.ZodString>;
|
|
738
|
+
revenue_streams: z.ZodArray<z.ZodString>;
|
|
739
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
740
|
+
type: z.ZodLiteral<"waterfall">;
|
|
741
|
+
items: z.ZodArray<z.ZodObject<{
|
|
742
|
+
label: z.ZodString;
|
|
743
|
+
value: z.ZodNumber;
|
|
744
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
745
|
+
delta: "delta";
|
|
746
|
+
total: "total";
|
|
747
|
+
}>>;
|
|
748
|
+
}, z.core.$strict>>;
|
|
749
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
750
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
751
|
+
type: z.ZodLiteral<"gantt">;
|
|
752
|
+
items: z.ZodArray<z.ZodObject<{
|
|
753
|
+
label: z.ZodString;
|
|
754
|
+
start: z.ZodNumber;
|
|
755
|
+
end: z.ZodNumber;
|
|
756
|
+
}, z.core.$strict>>;
|
|
757
|
+
axis_labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
674
758
|
}, z.core.$strict>], "type">>>;
|
|
675
759
|
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
676
760
|
kind: z.ZodLiteral<"color">;
|
|
@@ -715,11 +799,12 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
715
799
|
right: "right";
|
|
716
800
|
}>>;
|
|
717
801
|
footnote: z.ZodOptional<z.ZodString>;
|
|
802
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
718
803
|
}, z.core.$strict>;
|
|
719
804
|
declare const PptxIRSchema: z.ZodObject<{
|
|
720
|
-
version: z.ZodDefault<z.ZodLiteral<"
|
|
805
|
+
version: z.ZodDefault<z.ZodLiteral<"4">>;
|
|
721
806
|
filename: z.ZodDefault<z.ZodString>;
|
|
722
|
-
|
|
807
|
+
narrative: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
723
808
|
theme: z.ZodDefault<z.ZodObject<{
|
|
724
809
|
id: z.ZodDefault<z.ZodString>;
|
|
725
810
|
style: z.ZodOptional<z.ZodObject<{
|
|
@@ -1071,6 +1156,48 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
1071
1156
|
vs: "vs";
|
|
1072
1157
|
before_after: "before_after";
|
|
1073
1158
|
}>>;
|
|
1159
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1160
|
+
type: z.ZodLiteral<"swot">;
|
|
1161
|
+
strengths: z.ZodArray<z.ZodString>;
|
|
1162
|
+
weaknesses: z.ZodArray<z.ZodString>;
|
|
1163
|
+
opportunities: z.ZodArray<z.ZodString>;
|
|
1164
|
+
threats: z.ZodArray<z.ZodString>;
|
|
1165
|
+
labels: z.ZodOptional<z.ZodObject<{
|
|
1166
|
+
strengths: z.ZodOptional<z.ZodString>;
|
|
1167
|
+
weaknesses: z.ZodOptional<z.ZodString>;
|
|
1168
|
+
opportunities: z.ZodOptional<z.ZodString>;
|
|
1169
|
+
threats: z.ZodOptional<z.ZodString>;
|
|
1170
|
+
}, z.core.$strict>>;
|
|
1171
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1172
|
+
type: z.ZodLiteral<"bmc">;
|
|
1173
|
+
key_partners: z.ZodArray<z.ZodString>;
|
|
1174
|
+
key_activities: z.ZodArray<z.ZodString>;
|
|
1175
|
+
key_resources: z.ZodArray<z.ZodString>;
|
|
1176
|
+
value_propositions: z.ZodArray<z.ZodString>;
|
|
1177
|
+
customer_relationships: z.ZodArray<z.ZodString>;
|
|
1178
|
+
channels: z.ZodArray<z.ZodString>;
|
|
1179
|
+
customer_segments: z.ZodArray<z.ZodString>;
|
|
1180
|
+
cost_structure: z.ZodArray<z.ZodString>;
|
|
1181
|
+
revenue_streams: z.ZodArray<z.ZodString>;
|
|
1182
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1183
|
+
type: z.ZodLiteral<"waterfall">;
|
|
1184
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1185
|
+
label: z.ZodString;
|
|
1186
|
+
value: z.ZodNumber;
|
|
1187
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1188
|
+
delta: "delta";
|
|
1189
|
+
total: "total";
|
|
1190
|
+
}>>;
|
|
1191
|
+
}, z.core.$strict>>;
|
|
1192
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
1193
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1194
|
+
type: z.ZodLiteral<"gantt">;
|
|
1195
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1196
|
+
label: z.ZodString;
|
|
1197
|
+
start: z.ZodNumber;
|
|
1198
|
+
end: z.ZodNumber;
|
|
1199
|
+
}, z.core.$strict>>;
|
|
1200
|
+
axis_labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1074
1201
|
}, z.core.$strict>], "type">>>;
|
|
1075
1202
|
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1076
1203
|
kind: z.ZodLiteral<"color">;
|
|
@@ -1115,6 +1242,7 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
1115
1242
|
right: "right";
|
|
1116
1243
|
}>>;
|
|
1117
1244
|
footnote: z.ZodOptional<z.ZodString>;
|
|
1245
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
1118
1246
|
}, z.core.$strict>>;
|
|
1119
1247
|
}, z.core.$strict>;
|
|
1120
1248
|
type PptxIR = z.infer<typeof PptxIRSchema>;
|
|
@@ -1134,8 +1262,9 @@ interface ValidationIssue {
|
|
|
1134
1262
|
* whole-branch review finding 2: the README already claimed "validation
|
|
1135
1263
|
* error messages reference [a slide] by [its] id"; this is what makes
|
|
1136
1264
|
* that true. Set by every page-scoped issue producer
|
|
1137
|
-
* ({@link checkLayoutApplicability},
|
|
1138
|
-
* translation in {@link validateIr},
|
|
1265
|
+
* ({@link checkLayoutApplicability}, {@link checkBoundaryPageContent},
|
|
1266
|
+
* the content-quality-gate translation in {@link validateIr},
|
|
1267
|
+
* {@link checkDuplicateSlideIds})
|
|
1139
1268
|
* when the slide in question has an `id` — absent when the slide has
|
|
1140
1269
|
* none (bare, pre-W5 IR) or the issue is deck-level, not scoped to any
|
|
1141
1270
|
* single slide. {@link formatIssues} appends it in parens after the page
|
|
@@ -1155,34 +1284,86 @@ interface ValidateResult {
|
|
|
1155
1284
|
* least one rewrite happened; informational, never gates `ok` on its own.
|
|
1156
1285
|
*/
|
|
1157
1286
|
normalized?: string[];
|
|
1287
|
+
/**
|
|
1288
|
+
* Backward-compatible addition (borrow wave, Task 2 — dual-threshold
|
|
1289
|
+
* severity recalibration): warn-severity {@link checkIrQuality} findings
|
|
1290
|
+
* (editorial-budget codes — `missing_heading`/`long_heading`/`density`/
|
|
1291
|
+
* `bullets_overflow`/`bullet_item_long`/`big_number_no_kpi`), formatted the
|
|
1292
|
+
* same shape {@link formatIssues} already prints for `errors` (page/id/path
|
|
1293
|
+
* all included). Present only when at least one warn-severity finding
|
|
1294
|
+
* exists, and never gates `ok` on its own — that is exactly what moved
|
|
1295
|
+
* these findings off `errors` and onto here. Can be present alongside a
|
|
1296
|
+
* failing (`ok:false`) result too, whenever `checkIrQuality` itself ran
|
|
1297
|
+
* (an *earlier* hard gate — schema, theme id, layout applicability,
|
|
1298
|
+
* full-body exclusivity, boundary-page content, duplicate ids, narrative —
|
|
1299
|
+
* short-circuits before `checkIrQuality` runs at all, so a deck rejected
|
|
1300
|
+
* by one of those never reaches this field either way, see `validateIr`'s
|
|
1301
|
+
* own body for the exact gate order). {@link formatWarnings} renders this
|
|
1302
|
+
* array as CLI-ready `"warning: ..."` lines.
|
|
1303
|
+
*/
|
|
1304
|
+
warnings?: ValidationIssue[];
|
|
1158
1305
|
}
|
|
1159
1306
|
/**
|
|
1160
1307
|
* Validate raw JSON against the IR schema, then — once it parses — resolve
|
|
1161
|
-
* `
|
|
1162
|
-
* `
|
|
1163
|
-
* (`checkIrQuality`, passed the resolved axes) against the parsed IR.
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
* its own docstring) — here it is the pre-generation hard gate, so any
|
|
1169
|
-
* finding blocks (`ok: false`), not only "error"-severity ones. Treating
|
|
1170
|
-
* "warn" findings (e.g. a cover with no heading) as advisory-only would
|
|
1171
|
-
* defeat the point of wiring this in: the spec's core principle is a hard
|
|
1172
|
-
* protocol gate, not hoped-for prompt compliance.
|
|
1308
|
+
* `narrative` (`resolveNarrative`, spec §5: an unrecognized preset name is a
|
|
1309
|
+
* `narrative`-path error, page-less) and run the content-quality gate
|
|
1310
|
+
* (`checkIrQuality`, passed the resolved axes) against the parsed IR. Every
|
|
1311
|
+
* hard-gate stage (schema, theme id, layout applicability, full-body
|
|
1312
|
+
* exclusivity, boundary-page content, duplicate ids, narrative) must pass for
|
|
1313
|
+
* `ok: true`, same as before. Quality findings are reported the same way as
|
|
1314
|
+
* schema errors (page-scoped, 1-based).
|
|
1173
1315
|
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
1180
|
-
*
|
|
1181
|
-
*
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1316
|
+
* Dual-threshold severity (borrow wave, Task 2 — recalibrated from the prior
|
|
1317
|
+
* "any finding blocks" design): `checkIrQuality`'s own "warn" vs "error" tag
|
|
1318
|
+
* is now respected, not flattened. Only "error"-severity findings
|
|
1319
|
+
* (`empty_deck`, `bullet_item_overflow` — content genuinely lost: an empty
|
|
1320
|
+
* deck, or a bullet item long enough to hit `bullets.tsx`'s MIN_FONT floor
|
|
1321
|
+
* and actually get truncated) gate `ok`. "warn"-severity findings — the
|
|
1322
|
+
* editorial-budget codes (`missing_heading`/`long_heading`/`density`/
|
|
1323
|
+
* `bullets_overflow`/`bullet_item_long`/`big_number_no_kpi`) — surface on
|
|
1324
|
+
* {@link ValidateResult.warnings} instead: visible to every caller (the SDK,
|
|
1325
|
+
* the CLI's `validate`/`render` pre-flight), never blocking. This reverses
|
|
1326
|
+
* the prior posture (this comment used to read "any finding blocks... not
|
|
1327
|
+
* only 'error'-severity ones") because the evidence behind it did not hold:
|
|
1328
|
+
* a boundary scan (borrow-wave fact-report, Q3) found the editorial bullets
|
|
1329
|
+
* threshold blocking content roughly 3.5x below where `bullets.tsx`'s own
|
|
1330
|
+
* render safety net (2-line wrap, shrink to a 14px floor) actually starts
|
|
1331
|
+
* losing characters — so the hard gate was rejecting deck content that
|
|
1332
|
+
* would have rendered with zero visible defect, and a tight hard gate on an
|
|
1333
|
+
* editorial (not geometric) threshold taught truncate-content-to-pass
|
|
1334
|
+
* workarounds rather than catching real loss. `generatePptx`'s default path
|
|
1335
|
+
* inherits this unchanged (`if (!v.ok) throw`) — it already only ever
|
|
1336
|
+
* blocked on `ok`, never inspected `errors`/`warnings` directly.
|
|
1337
|
+
*
|
|
1338
|
+
* Before any of that, one deterministic alias pass runs
|
|
1339
|
+
* ({@link normalizeComponentAliases}, W5 task 4) for the component
|
|
1340
|
+
* field-name synonym rescue only (kpi `title`→`label`, quote
|
|
1341
|
+
* `content`→`text`, …) — a weak-model rescue for schema-internal synonym
|
|
1342
|
+
* drift, scoped to `slides[]`. It only rewrites where the canonical key is
|
|
1343
|
+
* absent, so the schema parse below never sees an alias as an "unrecognized
|
|
1344
|
+
* key" in the first place. Purely informational: every rewrite is recorded
|
|
1345
|
+
* as a human-readable `path: alias → canonical` string and threaded onto
|
|
1184
1346
|
* `ValidateResult.normalized` on *every* return path below via
|
|
1185
1347
|
* `withNormalized`, success or failure alike — it never itself gates `ok`.
|
|
1348
|
+
*
|
|
1349
|
+
* There is deliberately no root/narrative-level alias pass (spec §16,
|
|
1350
|
+
* reversing the now-superseded §15.4): a v4 document that still spells its
|
|
1351
|
+
* pre-rename vocabulary — `scenario` instead of `narrative`, `mode`/
|
|
1352
|
+
* `delivery` instead of `strategy`/`pacing`, or the old enum values
|
|
1353
|
+
* `"text"`/`"presentation"`/`"narrative"` — is not old-vocabulary
|
|
1354
|
+
* *compatibility*, it is exactly the vocabulary this rename retired, so it
|
|
1355
|
+
* hard-errors like any other unknown key or value: `scenario` fails the
|
|
1356
|
+
* schema's `.strict()` parse below as an unrecognized key, and an old enum
|
|
1357
|
+
* value (or the axis-key names `mode`/`delivery` inside `narrative`, which
|
|
1358
|
+
* the schema itself leaves open) fails `resolveNarrative`'s own runtime
|
|
1359
|
+
* check, listing the current values. `pptfast migrate` (`ir/migrate.ts`)
|
|
1360
|
+
* remains the sanctioned bridge for a genuine v3 document — see the v3 hard
|
|
1361
|
+
* reject below, which points there.
|
|
1362
|
+
*
|
|
1363
|
+
* The component-alias pass only ever runs for a document already headed for
|
|
1364
|
+
* the v4 schema — an explicit `version: "2"` or `version: "3"` is
|
|
1365
|
+
* hard-rejected first, below, before the alias pass or any schema parse
|
|
1366
|
+
* (spec §9.3: a v2/v3 document is never silently reinterpreted as v4).
|
|
1186
1367
|
*/
|
|
1187
1368
|
declare function validateIr(input: unknown): ValidateResult;
|
|
1188
1369
|
/**
|
|
@@ -1215,6 +1396,494 @@ declare function irJsonSchema(): Record<string, unknown>;
|
|
|
1215
1396
|
/** JSON Schema for style-token overrides (IR theme.style, --style files, config "style"). */
|
|
1216
1397
|
declare function styleJsonSchema(): Record<string, unknown>;
|
|
1217
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* The frozen IR v3 top-level shape (vocabulary-v4 rename, task 1 — spec
|
|
1401
|
+
* §9.3: "v3 已冻结... 顶层字段和枚举改名必须进入新的 IR 版本,不能在 v3
|
|
1402
|
+
* 内静默改变含义"). `./index.ts`'s `PptxIRSchema` is v4 now — this module
|
|
1403
|
+
* exists only so a genuinely v3-shaped document still has somewhere to parse
|
|
1404
|
+
* against: `migrateIrV3ToV4`'s input type (`./migrate.ts`), and the
|
|
1405
|
+
* v3-hard-reject path's own tests (constructing a *valid* v3 IR to prove the
|
|
1406
|
+
* reject fires on version alone, not on some other schema defect).
|
|
1407
|
+
*
|
|
1408
|
+
* `validateIr` (`src/api.ts`) never calls this schema itself — an incoming
|
|
1409
|
+
* v3 document (`version === "3"`) is hard-rejected before any schema parse
|
|
1410
|
+
* runs at all (spec §9.3), full stop. This schema is a migration-tooling and
|
|
1411
|
+
* test fixture, not a second accepted input shape.
|
|
1412
|
+
*
|
|
1413
|
+
* Every field but `version` and `scenario` is byte-identical to `./index.ts`'s
|
|
1414
|
+
* v4 `PptxIRSchema` (spec §9.1: "其余 IR 字段保持不变") — reuses the exact
|
|
1415
|
+
* same `ThemeSchema`/`MetaSchema`/`AssetsSchema`/`BrandSchema`/`SlideSchema`
|
|
1416
|
+
* instances rather than redefining them, so there is no way for this frozen
|
|
1417
|
+
* shape to silently drift from the fields it shares with v4.
|
|
1418
|
+
*/
|
|
1419
|
+
declare const PptxIRV3Schema: z.ZodObject<{
|
|
1420
|
+
version: z.ZodDefault<z.ZodLiteral<"3">>;
|
|
1421
|
+
filename: z.ZodDefault<z.ZodString>;
|
|
1422
|
+
scenario: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
1423
|
+
theme: z.ZodDefault<z.ZodObject<{
|
|
1424
|
+
id: z.ZodDefault<z.ZodString>;
|
|
1425
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
1426
|
+
colors: z.ZodOptional<z.ZodObject<{
|
|
1427
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
1428
|
+
surface: z.ZodOptional<z.ZodString>;
|
|
1429
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
1430
|
+
primary: z.ZodOptional<z.ZodString>;
|
|
1431
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
1432
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1433
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
1434
|
+
border: z.ZodOptional<z.ZodString>;
|
|
1435
|
+
chartPalette: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1436
|
+
accentPool: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1437
|
+
cardStroke: z.ZodOptional<z.ZodString>;
|
|
1438
|
+
}, z.core.$strict>>;
|
|
1439
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
1440
|
+
heading: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1441
|
+
body: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1442
|
+
mono: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1443
|
+
}, z.core.$strict>>;
|
|
1444
|
+
shape: z.ZodOptional<z.ZodObject<{
|
|
1445
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
1446
|
+
gapScale: z.ZodOptional<z.ZodNumber>;
|
|
1447
|
+
}, z.core.$strict>>;
|
|
1448
|
+
}, z.core.$strict>>;
|
|
1449
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
1450
|
+
suppressFooterOnCardContent: z.ZodOptional<z.ZodBoolean>;
|
|
1451
|
+
suppressFooterRule: z.ZodOptional<z.ZodBoolean>;
|
|
1452
|
+
}, z.core.$strict>>;
|
|
1453
|
+
}, z.core.$strict>>;
|
|
1454
|
+
meta: z.ZodDefault<z.ZodObject<{
|
|
1455
|
+
organization: z.ZodOptional<z.ZodString>;
|
|
1456
|
+
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1457
|
+
name: z.ZodString;
|
|
1458
|
+
role: z.ZodOptional<z.ZodString>;
|
|
1459
|
+
org: z.ZodOptional<z.ZodString>;
|
|
1460
|
+
}, z.core.$strict>>>;
|
|
1461
|
+
date: z.ZodOptional<z.ZodString>;
|
|
1462
|
+
version: z.ZodOptional<z.ZodString>;
|
|
1463
|
+
confidentiality: z.ZodOptional<z.ZodEnum<{
|
|
1464
|
+
public: "public";
|
|
1465
|
+
internal: "internal";
|
|
1466
|
+
confidential: "confidential";
|
|
1467
|
+
restricted: "restricted";
|
|
1468
|
+
}>>;
|
|
1469
|
+
contact: z.ZodOptional<z.ZodObject<{
|
|
1470
|
+
name: z.ZodOptional<z.ZodString>;
|
|
1471
|
+
email: z.ZodOptional<z.ZodString>;
|
|
1472
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
1473
|
+
website: z.ZodOptional<z.ZodString>;
|
|
1474
|
+
}, z.core.$strict>>;
|
|
1475
|
+
copyright: z.ZodOptional<z.ZodString>;
|
|
1476
|
+
animation: z.ZodOptional<z.ZodObject<{
|
|
1477
|
+
transition: z.ZodOptional<z.ZodEnum<{
|
|
1478
|
+
fade: "fade";
|
|
1479
|
+
push: "push";
|
|
1480
|
+
wipe: "wipe";
|
|
1481
|
+
none: "none";
|
|
1482
|
+
}>>;
|
|
1483
|
+
elements: z.ZodOptional<z.ZodEnum<{
|
|
1484
|
+
none: "none";
|
|
1485
|
+
auto: "auto";
|
|
1486
|
+
}>>;
|
|
1487
|
+
}, z.core.$strict>>;
|
|
1488
|
+
}, z.core.$strict>>;
|
|
1489
|
+
assets: z.ZodDefault<z.ZodObject<{
|
|
1490
|
+
images: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1491
|
+
src: z.ZodString;
|
|
1492
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
1493
|
+
error: z.ZodOptional<z.ZodString>;
|
|
1494
|
+
}, z.core.$strict>>>;
|
|
1495
|
+
}, z.core.$strict>>;
|
|
1496
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
1497
|
+
logo_asset_id: z.ZodOptional<z.ZodString>;
|
|
1498
|
+
position: z.ZodOptional<z.ZodEnum<{
|
|
1499
|
+
tl: "tl";
|
|
1500
|
+
tr: "tr";
|
|
1501
|
+
bl: "bl";
|
|
1502
|
+
br: "br";
|
|
1503
|
+
}>>;
|
|
1504
|
+
}, z.core.$strict>>;
|
|
1505
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
1506
|
+
slides: z.ZodArray<z.ZodObject<{
|
|
1507
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
1508
|
+
cover: "cover";
|
|
1509
|
+
chapter: "chapter";
|
|
1510
|
+
content: "content";
|
|
1511
|
+
ending: "ending";
|
|
1512
|
+
}>>;
|
|
1513
|
+
id: z.ZodOptional<z.ZodString>;
|
|
1514
|
+
placeholder: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1515
|
+
layout: z.ZodOptional<z.ZodString>;
|
|
1516
|
+
arrangement: z.ZodOptional<z.ZodEnum<{
|
|
1517
|
+
code: "code";
|
|
1518
|
+
quote: "quote";
|
|
1519
|
+
single: "single";
|
|
1520
|
+
two_column: "two_column";
|
|
1521
|
+
kpi_focus: "kpi_focus";
|
|
1522
|
+
image_focus: "image_focus";
|
|
1523
|
+
big_number: "big_number";
|
|
1524
|
+
assertion_evidence: "assertion_evidence";
|
|
1525
|
+
aside: "aside";
|
|
1526
|
+
}>>;
|
|
1527
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
1528
|
+
subheading: z.ZodOptional<z.ZodString>;
|
|
1529
|
+
components: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1530
|
+
type: z.ZodLiteral<"bullets">;
|
|
1531
|
+
items: z.ZodArray<z.ZodString>;
|
|
1532
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1533
|
+
default: "default";
|
|
1534
|
+
checklist: "checklist";
|
|
1535
|
+
numbered: "numbered";
|
|
1536
|
+
plain: "plain";
|
|
1537
|
+
divided: "divided";
|
|
1538
|
+
}>>;
|
|
1539
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1540
|
+
type: z.ZodLiteral<"paragraph">;
|
|
1541
|
+
text: z.ZodString;
|
|
1542
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1543
|
+
type: z.ZodLiteral<"quote">;
|
|
1544
|
+
text: z.ZodString;
|
|
1545
|
+
attribution: z.ZodOptional<z.ZodString>;
|
|
1546
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1547
|
+
type: z.ZodLiteral<"callout">;
|
|
1548
|
+
variant: z.ZodEnum<{
|
|
1549
|
+
info: "info";
|
|
1550
|
+
warn: "warn";
|
|
1551
|
+
tip: "tip";
|
|
1552
|
+
}>;
|
|
1553
|
+
text: z.ZodString;
|
|
1554
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1555
|
+
[x: string]: string;
|
|
1556
|
+
}>>;
|
|
1557
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1558
|
+
type: z.ZodLiteral<"code">;
|
|
1559
|
+
language: z.ZodString;
|
|
1560
|
+
code: z.ZodString;
|
|
1561
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1562
|
+
type: z.ZodLiteral<"kpi_cards">;
|
|
1563
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1564
|
+
value: z.ZodString;
|
|
1565
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
1566
|
+
label: z.ZodString;
|
|
1567
|
+
delta: z.ZodOptional<z.ZodEnum<{
|
|
1568
|
+
up: "up";
|
|
1569
|
+
down: "down";
|
|
1570
|
+
flat: "flat";
|
|
1571
|
+
}>>;
|
|
1572
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1573
|
+
[x: string]: string;
|
|
1574
|
+
}>>;
|
|
1575
|
+
source: z.ZodOptional<z.ZodString>;
|
|
1576
|
+
}, z.core.$strict>>;
|
|
1577
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1578
|
+
type: z.ZodLiteral<"chart">;
|
|
1579
|
+
chart_type: z.ZodEnum<{
|
|
1580
|
+
line: "line";
|
|
1581
|
+
dumbbell: "dumbbell";
|
|
1582
|
+
funnel: "funnel";
|
|
1583
|
+
bar: "bar";
|
|
1584
|
+
pie: "pie";
|
|
1585
|
+
}>;
|
|
1586
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1587
|
+
horizontal: "horizontal";
|
|
1588
|
+
vertical: "vertical";
|
|
1589
|
+
}>>;
|
|
1590
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1591
|
+
donut: "donut";
|
|
1592
|
+
}>>;
|
|
1593
|
+
axes: z.ZodOptional<z.ZodObject<{
|
|
1594
|
+
x_title: z.ZodOptional<z.ZodString>;
|
|
1595
|
+
y_title: z.ZodOptional<z.ZodString>;
|
|
1596
|
+
show_grid: z.ZodOptional<z.ZodBoolean>;
|
|
1597
|
+
}, z.core.$strict>>;
|
|
1598
|
+
series: z.ZodArray<z.ZodObject<{
|
|
1599
|
+
name: z.ZodString;
|
|
1600
|
+
data: z.ZodArray<z.ZodObject<{
|
|
1601
|
+
x: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1602
|
+
y: z.ZodNumber;
|
|
1603
|
+
}, z.core.$strict>>;
|
|
1604
|
+
}, z.core.$strict>>;
|
|
1605
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1606
|
+
type: z.ZodLiteral<"flowchart">;
|
|
1607
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
1608
|
+
id: z.ZodString;
|
|
1609
|
+
label: z.ZodString;
|
|
1610
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1611
|
+
rect: "rect";
|
|
1612
|
+
diamond: "diamond";
|
|
1613
|
+
round: "round";
|
|
1614
|
+
}>>;
|
|
1615
|
+
}, z.core.$strict>>;
|
|
1616
|
+
edges: z.ZodArray<z.ZodObject<{
|
|
1617
|
+
from: z.ZodString;
|
|
1618
|
+
to: z.ZodString;
|
|
1619
|
+
label: z.ZodOptional<z.ZodString>;
|
|
1620
|
+
}, z.core.$strict>>;
|
|
1621
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1622
|
+
TB: "TB";
|
|
1623
|
+
TD: "TD";
|
|
1624
|
+
BT: "BT";
|
|
1625
|
+
LR: "LR";
|
|
1626
|
+
RL: "RL";
|
|
1627
|
+
}>>;
|
|
1628
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1629
|
+
type: z.ZodLiteral<"architecture">;
|
|
1630
|
+
layers: z.ZodArray<z.ZodObject<{
|
|
1631
|
+
title: z.ZodString;
|
|
1632
|
+
items: z.ZodArray<z.ZodString>;
|
|
1633
|
+
}, z.core.$strict>>;
|
|
1634
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1635
|
+
type: z.ZodLiteral<"timeline">;
|
|
1636
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
1637
|
+
horizontal: "horizontal";
|
|
1638
|
+
vertical: "vertical";
|
|
1639
|
+
}>>;
|
|
1640
|
+
milestones: z.ZodArray<z.ZodObject<{
|
|
1641
|
+
date: z.ZodString;
|
|
1642
|
+
title: z.ZodString;
|
|
1643
|
+
desc: z.ZodOptional<z.ZodString>;
|
|
1644
|
+
highlight: z.ZodOptional<z.ZodBoolean>;
|
|
1645
|
+
}, z.core.$strict>>;
|
|
1646
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1647
|
+
type: z.ZodLiteral<"comparison">;
|
|
1648
|
+
columns: z.ZodArray<z.ZodString>;
|
|
1649
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
1650
|
+
label: z.ZodString;
|
|
1651
|
+
cells: z.ZodArray<z.ZodString>;
|
|
1652
|
+
}, z.core.$strict>>;
|
|
1653
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1654
|
+
type: z.ZodLiteral<"icon_cards">;
|
|
1655
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1656
|
+
icon: z.ZodEnum<{
|
|
1657
|
+
[x: string]: string;
|
|
1658
|
+
}>;
|
|
1659
|
+
title: z.ZodString;
|
|
1660
|
+
text: z.ZodString;
|
|
1661
|
+
}, z.core.$strict>>;
|
|
1662
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1663
|
+
type: z.ZodLiteral<"row_cards">;
|
|
1664
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1665
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1666
|
+
[x: string]: string;
|
|
1667
|
+
}>>;
|
|
1668
|
+
title: z.ZodString;
|
|
1669
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1670
|
+
sub: z.ZodOptional<z.ZodString>;
|
|
1671
|
+
highlight: z.ZodOptional<z.ZodBoolean>;
|
|
1672
|
+
}, z.core.$strict>>;
|
|
1673
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1674
|
+
type: z.ZodLiteral<"steps">;
|
|
1675
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1676
|
+
title: z.ZodString;
|
|
1677
|
+
text: z.ZodString;
|
|
1678
|
+
}, z.core.$strict>>;
|
|
1679
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1680
|
+
type: z.ZodLiteral<"rings">;
|
|
1681
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1682
|
+
label: z.ZodString;
|
|
1683
|
+
desc: z.ZodOptional<z.ZodString>;
|
|
1684
|
+
}, z.core.$strict>>;
|
|
1685
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1686
|
+
type: z.ZodLiteral<"numbered_cards">;
|
|
1687
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1688
|
+
title: z.ZodString;
|
|
1689
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1690
|
+
sub: z.ZodOptional<z.ZodString>;
|
|
1691
|
+
}, z.core.$strict>>;
|
|
1692
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1693
|
+
type: z.ZodLiteral<"roadmap">;
|
|
1694
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1695
|
+
title: z.ZodString;
|
|
1696
|
+
period: z.ZodOptional<z.ZodString>;
|
|
1697
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1698
|
+
label: z.ZodString;
|
|
1699
|
+
value: z.ZodString;
|
|
1700
|
+
}, z.core.$strict>>>;
|
|
1701
|
+
}, z.core.$strict>>;
|
|
1702
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1703
|
+
type: z.ZodLiteral<"matrix">;
|
|
1704
|
+
x_title: z.ZodOptional<z.ZodString>;
|
|
1705
|
+
y_title: z.ZodOptional<z.ZodString>;
|
|
1706
|
+
cols: z.ZodNumber;
|
|
1707
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1708
|
+
title: z.ZodString;
|
|
1709
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
1710
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
1711
|
+
info: "info";
|
|
1712
|
+
accent: "accent";
|
|
1713
|
+
neutral: "neutral";
|
|
1714
|
+
}>>;
|
|
1715
|
+
}, z.core.$strict>>;
|
|
1716
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1717
|
+
type: z.ZodLiteral<"insight_panel">;
|
|
1718
|
+
title: z.ZodString;
|
|
1719
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
1720
|
+
label: z.ZodString;
|
|
1721
|
+
text: z.ZodString;
|
|
1722
|
+
}, z.core.$strict>>;
|
|
1723
|
+
footnote: z.ZodOptional<z.ZodString>;
|
|
1724
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1725
|
+
type: z.ZodLiteral<"verdict_banner">;
|
|
1726
|
+
text: z.ZodString;
|
|
1727
|
+
tone: z.ZodEnum<{
|
|
1728
|
+
neutral: "neutral";
|
|
1729
|
+
positive: "positive";
|
|
1730
|
+
warning: "warning";
|
|
1731
|
+
}>;
|
|
1732
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1733
|
+
[x: string]: string;
|
|
1734
|
+
}>>;
|
|
1735
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1736
|
+
type: z.ZodLiteral<"citation">;
|
|
1737
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
1738
|
+
label: z.ZodString;
|
|
1739
|
+
url: z.ZodOptional<z.ZodString>;
|
|
1740
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
1741
|
+
}, z.core.$strict>>;
|
|
1742
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1743
|
+
type: z.ZodLiteral<"image">;
|
|
1744
|
+
asset_id: z.ZodString;
|
|
1745
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
1746
|
+
fit: z.ZodDefault<z.ZodEnum<{
|
|
1747
|
+
cover: "cover";
|
|
1748
|
+
contain: "contain";
|
|
1749
|
+
}>>;
|
|
1750
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1751
|
+
type: z.ZodLiteral<"image_grid">;
|
|
1752
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1753
|
+
asset_id: z.ZodString;
|
|
1754
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
1755
|
+
}, z.core.$strict>>;
|
|
1756
|
+
emphasis: z.ZodOptional<z.ZodEnum<{
|
|
1757
|
+
none: "none";
|
|
1758
|
+
first: "first";
|
|
1759
|
+
}>>;
|
|
1760
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1761
|
+
type: z.ZodLiteral<"image_compare">;
|
|
1762
|
+
left: z.ZodObject<{
|
|
1763
|
+
asset_id: z.ZodString;
|
|
1764
|
+
label: z.ZodString;
|
|
1765
|
+
}, z.core.$strict>;
|
|
1766
|
+
right: z.ZodObject<{
|
|
1767
|
+
asset_id: z.ZodString;
|
|
1768
|
+
label: z.ZodString;
|
|
1769
|
+
}, z.core.$strict>;
|
|
1770
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1771
|
+
vs: "vs";
|
|
1772
|
+
before_after: "before_after";
|
|
1773
|
+
}>>;
|
|
1774
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1775
|
+
type: z.ZodLiteral<"swot">;
|
|
1776
|
+
strengths: z.ZodArray<z.ZodString>;
|
|
1777
|
+
weaknesses: z.ZodArray<z.ZodString>;
|
|
1778
|
+
opportunities: z.ZodArray<z.ZodString>;
|
|
1779
|
+
threats: z.ZodArray<z.ZodString>;
|
|
1780
|
+
labels: z.ZodOptional<z.ZodObject<{
|
|
1781
|
+
strengths: z.ZodOptional<z.ZodString>;
|
|
1782
|
+
weaknesses: z.ZodOptional<z.ZodString>;
|
|
1783
|
+
opportunities: z.ZodOptional<z.ZodString>;
|
|
1784
|
+
threats: z.ZodOptional<z.ZodString>;
|
|
1785
|
+
}, z.core.$strict>>;
|
|
1786
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1787
|
+
type: z.ZodLiteral<"bmc">;
|
|
1788
|
+
key_partners: z.ZodArray<z.ZodString>;
|
|
1789
|
+
key_activities: z.ZodArray<z.ZodString>;
|
|
1790
|
+
key_resources: z.ZodArray<z.ZodString>;
|
|
1791
|
+
value_propositions: z.ZodArray<z.ZodString>;
|
|
1792
|
+
customer_relationships: z.ZodArray<z.ZodString>;
|
|
1793
|
+
channels: z.ZodArray<z.ZodString>;
|
|
1794
|
+
customer_segments: z.ZodArray<z.ZodString>;
|
|
1795
|
+
cost_structure: z.ZodArray<z.ZodString>;
|
|
1796
|
+
revenue_streams: z.ZodArray<z.ZodString>;
|
|
1797
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1798
|
+
type: z.ZodLiteral<"waterfall">;
|
|
1799
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1800
|
+
label: z.ZodString;
|
|
1801
|
+
value: z.ZodNumber;
|
|
1802
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1803
|
+
delta: "delta";
|
|
1804
|
+
total: "total";
|
|
1805
|
+
}>>;
|
|
1806
|
+
}, z.core.$strict>>;
|
|
1807
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
1808
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1809
|
+
type: z.ZodLiteral<"gantt">;
|
|
1810
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1811
|
+
label: z.ZodString;
|
|
1812
|
+
start: z.ZodNumber;
|
|
1813
|
+
end: z.ZodNumber;
|
|
1814
|
+
}, z.core.$strict>>;
|
|
1815
|
+
axis_labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1816
|
+
}, z.core.$strict>], "type">>>;
|
|
1817
|
+
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1818
|
+
kind: z.ZodLiteral<"color">;
|
|
1819
|
+
value: z.ZodString;
|
|
1820
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1821
|
+
kind: z.ZodLiteral<"gradient">;
|
|
1822
|
+
from: z.ZodString;
|
|
1823
|
+
to: z.ZodString;
|
|
1824
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1825
|
+
tb: "tb";
|
|
1826
|
+
lr: "lr";
|
|
1827
|
+
diagonal: "diagonal";
|
|
1828
|
+
}>>;
|
|
1829
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1830
|
+
kind: z.ZodLiteral<"asset">;
|
|
1831
|
+
asset_id: z.ZodString;
|
|
1832
|
+
overlay: z.ZodOptional<z.ZodObject<{
|
|
1833
|
+
color: z.ZodString;
|
|
1834
|
+
opacity: z.ZodNumber;
|
|
1835
|
+
}, z.core.$strict>>;
|
|
1836
|
+
fit: z.ZodOptional<z.ZodEnum<{
|
|
1837
|
+
cover: "cover";
|
|
1838
|
+
contain: "contain";
|
|
1839
|
+
}>>;
|
|
1840
|
+
}, z.core.$strict>], "kind">>;
|
|
1841
|
+
decor: z.ZodOptional<z.ZodObject<{
|
|
1842
|
+
kind: z.ZodEnum<{
|
|
1843
|
+
big_number: "big_number";
|
|
1844
|
+
corner_tag: "corner_tag";
|
|
1845
|
+
rule_line: "rule_line";
|
|
1846
|
+
quote_marks: "quote_marks";
|
|
1847
|
+
geo_dots: "geo_dots";
|
|
1848
|
+
}>;
|
|
1849
|
+
intensity: z.ZodOptional<z.ZodEnum<{
|
|
1850
|
+
subtle: "subtle";
|
|
1851
|
+
normal: "normal";
|
|
1852
|
+
}>>;
|
|
1853
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1854
|
+
}, z.core.$strict>>;
|
|
1855
|
+
image_side: z.ZodOptional<z.ZodEnum<{
|
|
1856
|
+
left: "left";
|
|
1857
|
+
right: "right";
|
|
1858
|
+
}>>;
|
|
1859
|
+
footnote: z.ZodOptional<z.ZodString>;
|
|
1860
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
1861
|
+
}, z.core.$strict>>;
|
|
1862
|
+
}, z.core.$strict>;
|
|
1863
|
+
type PptxIRV3 = z.infer<typeof PptxIRV3Schema>;
|
|
1864
|
+
|
|
1865
|
+
/**
|
|
1866
|
+
* Deterministic, pure IR v3 → v4 migration (spec §9.1). Field-for-field,
|
|
1867
|
+
* value-for-value per the mapping in {@link migrateNarrativeInput}'s
|
|
1868
|
+
* docstring — every field this function doesn't touch (`filename`, `theme`,
|
|
1869
|
+
* `meta`, `assets`, `brand`, `seed`, `slides`) carries across by the exact
|
|
1870
|
+
* same reference it came in with, unchanged (spec §9.1: "其余 IR 字段保持不
|
|
1871
|
+
* 变"; spec §10: no weight/budget/selection/render change is in scope for
|
|
1872
|
+
* this migration, ever).
|
|
1873
|
+
*
|
|
1874
|
+
* Exported from the SDK surface (`src/index.ts`) as the deterministic
|
|
1875
|
+
* migration primitive the `pptfast migrate` CLI command (task 2) wraps —
|
|
1876
|
+
* this function itself does no I/O and never runs a model, per spec §9.3's
|
|
1877
|
+
* "只做已声明的结构映射,不运行模型,不重写内容,不重新选择 layout".
|
|
1878
|
+
*
|
|
1879
|
+
* Takes an already-parsed `PptxIRV3` (i.e. `PptxIRV3Schema.parse(...)`'s
|
|
1880
|
+
* output, defaults already applied) rather than raw `unknown` JSON — schema
|
|
1881
|
+
* validation of the v3 input is the caller's job (the CLI parses-then-
|
|
1882
|
+
* migrates; `validateIr`'s own v3 path hard-rejects before ever reaching
|
|
1883
|
+
* this function, spec §9.3, so `validateIr` itself never calls this).
|
|
1884
|
+
*/
|
|
1885
|
+
declare function migrateIrV3ToV4(v3: PptxIRV3): PptxIR;
|
|
1886
|
+
|
|
1218
1887
|
type LayoutType = "cover" | "chapter" | "content" | "ending";
|
|
1219
1888
|
interface StyleColors {
|
|
1220
1889
|
bg: string;
|
|
@@ -1361,41 +2030,44 @@ declare function getInstalledThemeIds(): readonly string[];
|
|
|
1361
2030
|
*/
|
|
1362
2031
|
declare function getThemeDefinition(id: string): ThemeDefinition;
|
|
1363
2032
|
|
|
1364
|
-
declare const
|
|
1365
|
-
declare const
|
|
2033
|
+
declare const STRATEGY_VALUES: readonly ["pyramid", "storytelling", "instructional", "showcase", "briefing"];
|
|
2034
|
+
declare const PACING_VALUES: readonly ["dense", "balanced", "spacious"];
|
|
1366
2035
|
declare const AUDIENCE_VALUES: readonly ["executive", "technical", "customer", "public"];
|
|
1367
2036
|
|
|
1368
2037
|
/**
|
|
1369
|
-
* Narrative argument style (spec §5's five-way
|
|
1370
|
-
* mode
|
|
1371
|
-
* consumed by W4's
|
|
1372
|
-
*
|
|
2038
|
+
* Narrative argument style (spec §5's five-way strategy classification,
|
|
2039
|
+
* renamed from "mode" — spec §8.1). Each strategy carries a
|
|
2040
|
+
* component/layout tendency set (soft-weight material, consumed by W4's
|
|
2041
|
+
* weighted selection — nothing consumes it this wave) and a beat policy
|
|
2042
|
+
* (renamed from "rhythm policy" — spec §2.3's deck/page beat split; consumed
|
|
2043
|
+
* by W5's spec-validate rotation gate).
|
|
1373
2044
|
*/
|
|
1374
|
-
type
|
|
2045
|
+
type Strategy = (typeof STRATEGY_VALUES)[number];
|
|
1375
2046
|
/**
|
|
1376
|
-
* Content density budget + typographic baseline (spec §5
|
|
1377
|
-
* See {@link
|
|
1378
|
-
* is one half of.
|
|
2047
|
+
* Content density budget + typographic baseline (spec §5 pacing table,
|
|
2048
|
+
* renamed from "delivery" — spec §8.1). See {@link PacingBudget} for the
|
|
2049
|
+
* dual-attribute capacity split this axis is one half of.
|
|
1379
2050
|
*/
|
|
1380
|
-
type
|
|
2051
|
+
type Pacing = (typeof PACING_VALUES)[number];
|
|
1381
2052
|
/**
|
|
1382
2053
|
* Tone anchor only (spec §5: audience is tone-anchoring only, no rendering
|
|
1383
2054
|
* effect on the IR yet). Reserved for a future lint pass (e.g. executive ×
|
|
1384
2055
|
* long paragraphs → suggest kpi_cards/verdict_banner instead). The rule set
|
|
1385
2056
|
* itself is explicitly out of scope this wave (spec §10 open questions).
|
|
2057
|
+
* Name unchanged by the vocabulary-v4 rename (spec §4.3: "audience 保持不变").
|
|
1386
2058
|
*/
|
|
1387
2059
|
type Audience = (typeof AUDIENCE_VALUES)[number];
|
|
1388
|
-
interface
|
|
1389
|
-
readonly
|
|
1390
|
-
readonly
|
|
2060
|
+
interface NarrativeProfile {
|
|
2061
|
+
readonly strategy: Strategy;
|
|
2062
|
+
readonly pacing: Pacing;
|
|
1391
2063
|
readonly audience: Audience;
|
|
1392
2064
|
}
|
|
1393
2065
|
|
|
1394
|
-
interface
|
|
1395
|
-
id:
|
|
2066
|
+
interface StrategyDefinition {
|
|
2067
|
+
id: Strategy;
|
|
1396
2068
|
/**
|
|
1397
2069
|
* Layout/component tendency set (soft-weight material). Filled row-for-row
|
|
1398
|
-
* from spec §5's
|
|
2070
|
+
* from spec §5's strategy table for W4's weighted selection step (spec §6
|
|
1399
2071
|
* step 4: in-set candidates get ×3 weight, out-of-set ×1 floor — not
|
|
1400
2072
|
* implemented yet, this module only stores the data).
|
|
1401
2073
|
*
|
|
@@ -1416,48 +2088,50 @@ interface ModeDefinition {
|
|
|
1416
2088
|
* `svg/effective-layout.ts`, next to `resolveArchetypeId`, the sole
|
|
1417
2089
|
* consumer). Deliberately a separate field from {@link tendencies} above,
|
|
1418
2090
|
* not a reinterpretation of it: `tendencies` mixes component-type names and
|
|
1419
|
-
* layout ids drawn from spec §5's
|
|
1420
|
-
*
|
|
2091
|
+
* layout ids drawn from spec §5's strategy table verbatim and also feeds W5's
|
|
2092
|
+
* spec `focus` vocabulary gate, so narrowing its meaning here would be a
|
|
1421
2093
|
* breaking change to an existing consumer. This field holds only
|
|
1422
2094
|
* `LAYOUT_REGISTRY` content-archetype ids (`svg/layouts/registry.ts`'s
|
|
1423
2095
|
* `CONTENT_LAYOUTS` keys) — cover/chapter/ending ids never appear in any
|
|
1424
|
-
*
|
|
2096
|
+
* strategy's list here, which is exactly why `resolveArchetypeId`'s weighting
|
|
1425
2097
|
* is a no-op for those three slide types (spec: "身份页个性来自 theme 不来自
|
|
1426
|
-
*
|
|
2098
|
+
* strategy,均匀取样") without needing a slide-type special case — a weight
|
|
1427
2099
|
* lookup against an id that can never match falls through to the ×1 floor
|
|
1428
2100
|
* for every candidate, uniform by construction. `tone-adaptive-content`
|
|
1429
|
-
* appears in no
|
|
1430
|
-
* content archetype meant to read as
|
|
1431
|
-
* ×1 floor too).
|
|
2101
|
+
* appears in no strategy's list either (spec's "万金油" call-out: it is the
|
|
2102
|
+
* one content archetype meant to read as strategy-neutral, so it always
|
|
2103
|
+
* gets the ×1 floor too).
|
|
1432
2104
|
*/
|
|
1433
2105
|
layoutTendencies: readonly string[];
|
|
1434
2106
|
/**
|
|
1435
|
-
*
|
|
1436
|
-
* parameterized by
|
|
1437
|
-
* briefing is exempt from a generic
|
|
1438
|
-
* an error" rule because uniform-dense
|
|
1439
|
-
* not a violation of it (spec §5's
|
|
1440
|
-
* generic same-
|
|
1441
|
-
* Not consumed this wave.
|
|
2107
|
+
* Beat template descriptor (spec §5's per-strategy beat-default column,
|
|
2108
|
+
* renamed from "rhythm" — spec §2.3), parameterized by strategy for W5's
|
|
2109
|
+
* spec-validate rotation gate — e.g. briefing is exempt from a generic
|
|
2110
|
+
* "three same-beat pages in a row is an error" rule because uniform-dense
|
|
2111
|
+
* *is* briefing's correct default, not a violation of it (spec §5's
|
|
2112
|
+
* spec-gate section calls out that a generic same-beat-streak rule would
|
|
2113
|
+
* reject briefing's own default). Not consumed this wave.
|
|
1442
2114
|
*/
|
|
1443
|
-
|
|
2115
|
+
beatPolicy: "anchor-open" | "alternate" | "repetition-ok" | "anchor-sparse" | "uniform-dense";
|
|
1444
2116
|
}
|
|
1445
|
-
declare const
|
|
2117
|
+
declare const STRATEGY_DEFINITIONS: Record<Strategy, StrategyDefinition>;
|
|
1446
2118
|
|
|
1447
|
-
interface
|
|
2119
|
+
interface PacingBudget {
|
|
1448
2120
|
/**
|
|
1449
|
-
* Body-text baseline, in px, at 1280×720 slide geometry (spec §5
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
* `
|
|
2121
|
+
* Body-text baseline, in px, at 1280×720 slide geometry (spec §5 pacing
|
|
2122
|
+
* table's body-baseline column). Wired into rendering as of W4 task 3
|
|
2123
|
+
* (design decision 9): `src/svg/FullSlideSvg.tsx` resolves
|
|
2124
|
+
* `PACING_BUDGETS[resolveNarrative(ir.narrative).pacing].bodyBaselinePx`
|
|
1453
2125
|
* once and passes it into `buildCtx`, which stores it as
|
|
1454
2126
|
* `ComponentCtx.bodyFontPx` — the sole font-size input for the
|
|
1455
2127
|
* paragraph/bullets/callout trio ("正文" = continuous running text).
|
|
1456
2128
|
* Every other component's own bespoke type scale, the heading system, and
|
|
1457
2129
|
* quote's fixed 26px attribution line don't read this field. Since
|
|
1458
|
-
* `balanced` (24px) is the
|
|
1459
|
-
* renders body text at 24px, not the previous fixed 20px — the
|
|
1460
|
-
* spec-sanctioned snapshot re-pin that landed alongside this
|
|
2130
|
+
* `balanced` (24px) is the narrative default, an omitted-narrative deck
|
|
2131
|
+
* now renders body text at 24px, not the previous fixed 20px — the
|
|
2132
|
+
* one-time, spec-sanctioned snapshot re-pin that landed alongside this
|
|
2133
|
+
* wiring (W4 task 3, unchanged since — vocabulary-v4 only renames this
|
|
2134
|
+
* table, it doesn't touch any of its numbers, spec §10).
|
|
1461
2135
|
*/
|
|
1462
2136
|
bodyBaselinePx: number;
|
|
1463
2137
|
/**
|
|
@@ -1476,23 +2150,24 @@ interface DeliveryBudget {
|
|
|
1476
2150
|
* (`measureTextUnits`, CJK weight = 1.0) — a visual-width-weighted
|
|
1477
2151
|
* character count, not a raw `.length`. The old physical ceiling (53,
|
|
1478
2152
|
* derived from render geometry) was deleted in W3 without a replacement
|
|
1479
|
-
* check because every
|
|
2153
|
+
* check because every pacing's editorial budget here (30/40/48) is
|
|
1480
2154
|
* already strictly tighter than it — the quality gate now applies
|
|
1481
|
-
* exactly this one number per
|
|
2155
|
+
* exactly this one number per pacing.
|
|
1482
2156
|
*/
|
|
1483
2157
|
maxUnitsPerItem: number;
|
|
1484
2158
|
};
|
|
1485
2159
|
}
|
|
1486
2160
|
/**
|
|
1487
|
-
* Pinned to spec §5's
|
|
1488
|
-
* bullets budget columns):
|
|
1489
|
-
*
|
|
2161
|
+
* Pinned to spec §5's pacing table (`bodyBaselinePx` / editorial budget /
|
|
2162
|
+
* bullets budget columns): dense 20/5/6×48, balanced 24/4/5×40, spacious
|
|
2163
|
+
* 32/3/4×30 — same three rows as the pre-rename `text`/`balanced`/
|
|
2164
|
+
* `presentation` delivery table, values byte-for-byte unchanged (spec §10).
|
|
1490
2165
|
*/
|
|
1491
|
-
declare const
|
|
2166
|
+
declare const PACING_BUDGETS: Record<Pacing, PacingBudget>;
|
|
1492
2167
|
|
|
1493
|
-
interface
|
|
2168
|
+
interface NarrativePreset {
|
|
1494
2169
|
id: string;
|
|
1495
|
-
axes:
|
|
2170
|
+
axes: NarrativeProfile;
|
|
1496
2171
|
/**
|
|
1497
2172
|
* Soft theme recommendations — a suggestion, never a hard constraint
|
|
1498
2173
|
* (spec §5). Surfaced in workflow step ① so an agent can open with a
|
|
@@ -1502,27 +2177,33 @@ interface ScenarioPreset {
|
|
|
1502
2177
|
*/
|
|
1503
2178
|
themeRecommendations: readonly string[];
|
|
1504
2179
|
}
|
|
1505
|
-
/** Pinned to spec §5's 7 named presets and their theme recommendation table. */
|
|
1506
|
-
declare const SCENARIO_PRESETS: Record<string, ScenarioPreset>;
|
|
1507
2180
|
/**
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
2181
|
+
* Pinned to spec §5's 7 named presets and their theme recommendation table.
|
|
2182
|
+
* Preset ids are unchanged by the vocabulary-v4 rename (spec §5: "预设 ID
|
|
2183
|
+
* 保持不变") — only each preset's internal axes keys/values follow the
|
|
2184
|
+
* strategy/pacing rename (e.g. `annual-review`'s `mode: "narrative"` is now
|
|
2185
|
+
* `strategy: "storytelling"`, spec §5's own worked example).
|
|
2186
|
+
*/
|
|
2187
|
+
declare const NARRATIVE_PRESETS: Record<string, NarrativePreset>;
|
|
2188
|
+
/**
|
|
2189
|
+
* = `NARRATIVE_PRESETS.general.axes` (briefing × balanced × public) — the
|
|
2190
|
+
* global default when narrative is omitted entirely (spec §5's defaults
|
|
1510
2191
|
* chain).
|
|
1511
2192
|
*/
|
|
1512
|
-
declare const
|
|
2193
|
+
declare const DEFAULT_NARRATIVE: NarrativeProfile;
|
|
1513
2194
|
/**
|
|
1514
|
-
* Resolve a
|
|
2195
|
+
* Resolve a narrative input down to concrete axes, per spec §5's design
|
|
1515
2196
|
* principle "omission gets the default, a typo is a hard error" (weak-model
|
|
1516
2197
|
* friendly: a model that leaves a field out gets a sane deck, a model that
|
|
1517
2198
|
* misspells a value gets a loud, actionable error instead of a silently
|
|
1518
2199
|
* wrong deck):
|
|
1519
2200
|
*
|
|
1520
|
-
* - `undefined` → {@link
|
|
2201
|
+
* - `undefined` → {@link DEFAULT_NARRATIVE} (the `general` preset's axes)
|
|
1521
2202
|
* - a preset id string → that preset's axes (unknown id throws
|
|
1522
2203
|
* {@link PptfastError}, listing the available preset ids)
|
|
1523
2204
|
* - a partial axes object → each axis defaults independently
|
|
1524
|
-
* (
|
|
1525
|
-
* happen to equal `
|
|
2205
|
+
* (strategy → "briefing", pacing → "balanced", audience → "public" — these
|
|
2206
|
+
* happen to equal `DEFAULT_NARRATIVE`'s values because `general` *is* that
|
|
1526
2207
|
* exact combination, but the fallback here is per-axis, not "any omitted
|
|
1527
2208
|
* axis falls back to the whole default object")
|
|
1528
2209
|
*
|
|
@@ -1530,50 +2211,64 @@ declare const DEFAULT_SCENARIO: ScenarioAxes;
|
|
|
1530
2211
|
* always throws {@link PptfastError} (never silently ignored or dropped) —
|
|
1531
2212
|
* omission and a typo are different intents, and only the former has a
|
|
1532
2213
|
* reasonable default.
|
|
2214
|
+
*
|
|
2215
|
+
* Renamed from `resolveScenario` (spec §8.1). Callers that still hold a
|
|
2216
|
+
* pre-rename `mode`/`delivery` shaped input (e.g. a v3 IR's `scenario`
|
|
2217
|
+
* field) must migrate it first — see `migrateIrV3ToV4`
|
|
2218
|
+
* (`src/ir/migrate.ts`) for the deterministic field/value mapping. A
|
|
2219
|
+
* v4-track document that still writes the old field/value spelling gets no
|
|
2220
|
+
* such rescue (spec §16, reversing the now-superseded §15.4): this function
|
|
2221
|
+
* hard-errors on it, same as any other unknown axis key or value.
|
|
1533
2222
|
*/
|
|
1534
|
-
declare function
|
|
2223
|
+
declare function resolveNarrative(input: string | Partial<NarrativeProfile> | undefined): NarrativeProfile;
|
|
1535
2224
|
|
|
1536
2225
|
/**
|
|
1537
|
-
* Deck
|
|
2226
|
+
* Deck spec schema + validation (spec §5 "plan artifact and hard gates", W5
|
|
2227
|
+
* task 2 — renamed to "Deck Spec" per the vocabulary-v4 rename, spec §6/§8.1;
|
|
2228
|
+
* "spec §N" citations throughout this file that predate this rename still
|
|
2229
|
+
* point at that original W5 design doc, not this rename's own spec — left
|
|
2230
|
+
* as historical citations, not renumbered).
|
|
1538
2231
|
*
|
|
1539
|
-
* A
|
|
1540
|
-
* "escape hatch": a bare IR v3 renders directly,
|
|
1541
|
-
* optional gate for the
|
|
1542
|
-
* This module stays pure and Node-free — no `fs`, no CLI concerns
|
|
1543
|
-
* can sit in `src/index.ts`'s dependency closure exactly like
|
|
1544
|
-
* `src/
|
|
1545
|
-
* owns the one Node-touching wrapper
|
|
1546
|
-
* {@link
|
|
2232
|
+
* A deck spec is a workflow artifact, not a render prerequisite (spec §5's
|
|
2233
|
+
* "escape hatch": a bare IR v3 renders directly, `spec validate` is a
|
|
2234
|
+
* separate, optional gate for the spec-authoring stage of the six-phase
|
|
2235
|
+
* workflow). This module stays pure and Node-free — no `fs`, no CLI concerns
|
|
2236
|
+
* — so it can sit in `src/index.ts`'s dependency closure exactly like
|
|
2237
|
+
* `src/ir` and `src/narrative` already do (`AGENTS.md`'s layout rule).
|
|
2238
|
+
* `src/cli/commands.ts` owns the one Node-touching wrapper
|
|
2239
|
+
* (`runSpecValidate`: read file, call {@link validateSpec}, format the
|
|
2240
|
+
* result).
|
|
1547
2241
|
*
|
|
1548
2242
|
* Design mirrors `api.ts`'s `validateIr`/`ValidateResult` throughout
|
|
1549
2243
|
* (structural zod pass first, then a sequential chain of hard-gate
|
|
1550
2244
|
* categories, each short-circuiting the chain on its own failure — see
|
|
1551
|
-
* {@link
|
|
1552
|
-
* for
|
|
2245
|
+
* {@link validateSpec}'s own comment for why): same overall shape, adapted
|
|
2246
|
+
* for spec pages being keyed by an author-assigned `id` instead of IR's
|
|
1553
2247
|
* positional slide index.
|
|
1554
2248
|
*/
|
|
1555
2249
|
|
|
1556
2250
|
/**
|
|
1557
2251
|
* Mirrors `SlideSchema.type` / `SlideType` (`ir/index.ts`,
|
|
1558
2252
|
* `svg/layouts/registry.ts`) exactly. Kept as an independent literal tuple
|
|
1559
|
-
* here (not imported from either) — a
|
|
2253
|
+
* here (not imported from either) — a page spec's `type` is a 4-value enum
|
|
1560
2254
|
* on its own schema, not a re-export of IR's — but the `satisfies` clause
|
|
1561
2255
|
* makes any future drift between the two a compile error instead of a
|
|
1562
2256
|
* silent mismatch.
|
|
1563
2257
|
*/
|
|
1564
2258
|
declare const PAGE_TYPES: readonly ["cover", "chapter", "content", "ending"];
|
|
1565
|
-
declare const
|
|
1566
|
-
type
|
|
1567
|
-
type
|
|
2259
|
+
declare const BEAT_VALUES: readonly ["anchor", "dense", "breathing"];
|
|
2260
|
+
type PageSpecType = (typeof PAGE_TYPES)[number];
|
|
2261
|
+
type PageBeat = (typeof BEAT_VALUES)[number];
|
|
1568
2262
|
/**
|
|
1569
|
-
* A single
|
|
1570
|
-
* own `SlideSchema` (where both default/omit for weak-model
|
|
1571
|
-
* a
|
|
1572
|
-
* assemble time (W5 task 3), so leaving either implicit
|
|
1573
|
-
* the point. `
|
|
1574
|
-
* chain ("
|
|
2263
|
+
* A single page spec (spec §5, §6). `type`/`heading` are required — unlike
|
|
2264
|
+
* IR's own `SlideSchema` (where both default/omit for weak-model
|
|
2265
|
+
* friendliness), a deck spec is the authoring artifact those fields get
|
|
2266
|
+
* *locked* from at assemble time (W5 task 3), so leaving either implicit
|
|
2267
|
+
* here would defeat the point. `beat`/`focus`/`summary` stay optional per
|
|
2268
|
+
* spec §5's defaults chain ("beat omitted → auto-rotates by page position —
|
|
2269
|
+
* focus/summary/layout/slot can all be omitted").
|
|
1575
2270
|
*/
|
|
1576
|
-
declare const
|
|
2271
|
+
declare const PageSpecSchema: z.ZodObject<{
|
|
1577
2272
|
id: z.ZodString;
|
|
1578
2273
|
type: z.ZodEnum<{
|
|
1579
2274
|
cover: "cover";
|
|
@@ -1582,7 +2277,7 @@ declare const PlanPageSchema: z.ZodObject<{
|
|
|
1582
2277
|
ending: "ending";
|
|
1583
2278
|
}>;
|
|
1584
2279
|
heading: z.ZodString;
|
|
1585
|
-
|
|
2280
|
+
beat: z.ZodOptional<z.ZodEnum<{
|
|
1586
2281
|
anchor: "anchor";
|
|
1587
2282
|
dense: "dense";
|
|
1588
2283
|
breathing: "breathing";
|
|
@@ -1590,19 +2285,27 @@ declare const PlanPageSchema: z.ZodObject<{
|
|
|
1590
2285
|
focus: z.ZodOptional<z.ZodString>;
|
|
1591
2286
|
summary: z.ZodOptional<z.ZodString>;
|
|
1592
2287
|
}, z.core.$strict>;
|
|
1593
|
-
type
|
|
2288
|
+
type PageSpec = z.infer<typeof PageSpecSchema>;
|
|
1594
2289
|
/**
|
|
1595
|
-
* Top-level deck
|
|
1596
|
-
* no schema-level `.default(...)` — same reasoning as `PptxIRSchema`'s
|
|
1597
|
-
* `
|
|
1598
|
-
* into the parsed shape, {@link
|
|
1599
|
-
* (W5 task 3) each resolve it themselves. `seed` is accepted but
|
|
1600
|
-
* unexamined by this module — "not
|
|
1601
|
-
* assemble generates and suggests writing one back on first
|
|
2290
|
+
* Top-level deck spec shape (spec §5, §6). `narrative`/`theme` deliberately
|
|
2291
|
+
* have no schema-level `.default(...)` — same reasoning as `PptxIRSchema`'s
|
|
2292
|
+
* own `narrative` field (`ir/index.ts`): the resolved value is never baked
|
|
2293
|
+
* back into the parsed shape, {@link validateSpec} (here) and, later,
|
|
2294
|
+
* assemble (W5 task 3) each resolve it themselves. `seed` is accepted but
|
|
2295
|
+
* entirely unexamined by this module — "not validateSpec's concern" (spec's
|
|
2296
|
+
* own wording): assemble generates and suggests writing one back on first
|
|
2297
|
+
* materialization.
|
|
2298
|
+
*
|
|
2299
|
+
* `version` stays the literal `"1"` (unchanged value) but now carries an
|
|
2300
|
+
* independent Deck Spec versioning scheme (spec §6: "`deck.spec.json` 使用
|
|
2301
|
+
* 独立的 spec 版本 1。它是新工件,不继承 `deck.plan.json` 的版本号语义") —
|
|
2302
|
+
* this "1" is the Deck Spec artifact's own first version, not a continuation
|
|
2303
|
+
* of the old deck-plan artifact's version counter, even though the digit is
|
|
2304
|
+
* the same.
|
|
1602
2305
|
*/
|
|
1603
|
-
declare const
|
|
2306
|
+
declare const DeckSpecSchema: z.ZodObject<{
|
|
1604
2307
|
version: z.ZodDefault<z.ZodLiteral<"1">>;
|
|
1605
|
-
|
|
2308
|
+
narrative: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
1606
2309
|
theme: z.ZodOptional<z.ZodString>;
|
|
1607
2310
|
filename: z.ZodOptional<z.ZodString>;
|
|
1608
2311
|
seed: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1659,7 +2362,7 @@ declare const DeckPlanSchema: z.ZodObject<{
|
|
|
1659
2362
|
ending: "ending";
|
|
1660
2363
|
}>;
|
|
1661
2364
|
heading: z.ZodString;
|
|
1662
|
-
|
|
2365
|
+
beat: z.ZodOptional<z.ZodEnum<{
|
|
1663
2366
|
anchor: "anchor";
|
|
1664
2367
|
dense: "dense";
|
|
1665
2368
|
breathing: "breathing";
|
|
@@ -1668,10 +2371,10 @@ declare const DeckPlanSchema: z.ZodObject<{
|
|
|
1668
2371
|
summary: z.ZodOptional<z.ZodString>;
|
|
1669
2372
|
}, z.core.$strict>>;
|
|
1670
2373
|
}, z.core.$strict>;
|
|
1671
|
-
type
|
|
1672
|
-
/** JSON Schema for the deck
|
|
1673
|
-
declare function
|
|
1674
|
-
interface
|
|
2374
|
+
type DeckSpec = z.infer<typeof DeckSpecSchema>;
|
|
2375
|
+
/** JSON Schema for the deck spec — feed this to a model before it writes one (see `pptfast schema --spec`). */
|
|
2376
|
+
declare function specJsonSchema(): Record<string, unknown>;
|
|
2377
|
+
interface SpecValidationIssue {
|
|
1675
2378
|
path: string;
|
|
1676
2379
|
message: string;
|
|
1677
2380
|
/** The offending page's `id`, when the issue is scoped to one specific
|
|
@@ -1680,53 +2383,57 @@ interface PlanValidationIssue {
|
|
|
1680
2383
|
* whose own `id` itself failed to parse. */
|
|
1681
2384
|
pageId?: string;
|
|
1682
2385
|
}
|
|
1683
|
-
interface
|
|
2386
|
+
interface SpecValidateResult {
|
|
1684
2387
|
ok: boolean;
|
|
1685
|
-
|
|
1686
|
-
errors:
|
|
2388
|
+
spec?: DeckSpec;
|
|
2389
|
+
errors: SpecValidationIssue[];
|
|
1687
2390
|
}
|
|
1688
|
-
declare function
|
|
2391
|
+
declare function formatSpecIssues(errors: SpecValidationIssue[]): string;
|
|
1689
2392
|
/**
|
|
1690
|
-
*
|
|
2393
|
+
* Deck-spec-level theme default (spec §5's defaults chain: "theme omitted →
|
|
1691
2394
|
* consulting") — the same default IR's own `theme.id` field carries
|
|
1692
2395
|
* (`ThemeSchema` in `ir/index.ts`). Exported so a caller already holding a
|
|
1693
|
-
* validated {@link
|
|
2396
|
+
* validated {@link DeckSpec} (the CLI's OK-summary line) doesn't re-derive
|
|
1694
2397
|
* the fallback itself.
|
|
1695
2398
|
*/
|
|
1696
|
-
declare function
|
|
2399
|
+
declare function resolveSpecThemeId(spec: DeckSpec): string;
|
|
1697
2400
|
/**
|
|
1698
|
-
* Deck-level page-count range per
|
|
1699
|
-
* initial values — "
|
|
1700
|
-
* tuned against real usage). Independent of `
|
|
1701
|
-
* (`
|
|
2401
|
+
* Deck-level page-count range per pacing (spec §5's pacing table,
|
|
2402
|
+
* initial values — "dense 8-30 / balanced 6-24 / spacious 4-16", not yet
|
|
2403
|
+
* tuned against real usage). Independent of `PACING_BUDGETS`
|
|
2404
|
+
* (`narrative/index.ts`, per-slide component-count/bullets editorial
|
|
1702
2405
|
* budget) — this is a separate, deck-wide page-count concern the spec calls
|
|
1703
|
-
* out as its own hard gate ("page count vs.
|
|
2406
|
+
* out as its own hard gate ("page count vs. pacing recommended range").
|
|
2407
|
+
* Message wording renamed from "delivery" to "pacing" (vocabulary-v4
|
|
2408
|
+
* residual, routed from the task 1 review) — the axis itself was already
|
|
2409
|
+
* `Pacing` at the type level, this closes the last stale word in the
|
|
2410
|
+
* error text.
|
|
1704
2411
|
*/
|
|
1705
|
-
declare const
|
|
2412
|
+
declare const SPEC_PAGE_COUNT_RANGE: Record<Pacing, {
|
|
1706
2413
|
min: number;
|
|
1707
2414
|
max: number;
|
|
1708
2415
|
}>;
|
|
1709
2416
|
/**
|
|
1710
|
-
* Validate raw JSON against the
|
|
2417
|
+
* Validate raw JSON against the spec schema, then — once it parses — run the
|
|
1711
2418
|
* spec §5 hard-gate chain. Mirrors `validateIr`'s (`api.ts`) overall shape: a
|
|
1712
2419
|
* structural zod pass first, then a sequence of isolated hard-gate
|
|
1713
2420
|
* categories, each short-circuiting the whole chain on its own failure
|
|
1714
2421
|
* (rather than accumulating errors across categories) so a later category
|
|
1715
2422
|
* never has to guess at what an earlier, already-broken one would have
|
|
1716
|
-
* meant — e.g.
|
|
1717
|
-
*
|
|
1718
|
-
* resolves cleanly. Every
|
|
1719
|
-
* warning" (spec §5's "escape hatch" section — a
|
|
2423
|
+
* meant — e.g. beat-rotation and page-count both need a resolved
|
|
2424
|
+
* narrative, so nothing past the narrative/theme stage runs until that
|
|
2425
|
+
* resolves cleanly. Every spec-gate philosophy here is "hard block, no soft
|
|
2426
|
+
* warning" (spec §5's "escape hatch" section — a spec that doesn't fit this shape
|
|
1720
2427
|
* should be authored as bare IR instead, not warned-and-shipped).
|
|
1721
2428
|
*/
|
|
1722
|
-
declare function
|
|
2429
|
+
declare function validateSpec(input: unknown): SpecValidateResult;
|
|
1723
2430
|
|
|
1724
2431
|
/**
|
|
1725
|
-
* One page's fillable content — everything a
|
|
1726
|
-
* already lock in. Deliberately excludes `type`/`heading` (
|
|
2432
|
+
* One page's fillable content — everything a page spec's `id` does *not*
|
|
2433
|
+
* already lock in. Deliberately excludes `type`/`heading` (spec-owned, see
|
|
1727
2434
|
* {@link assembleDeck}'s locked-field gate) and `subheading`/`decor`
|
|
1728
2435
|
* (legitimate `Slide` fields, but outside this record's shape by spec §7's
|
|
1729
|
-
* own layout — "pages/<id>.json contains only components" — a
|
|
2436
|
+
* own layout — "pages/<id>.json contains only components" — a spec/pages deck can't
|
|
1730
2437
|
* author either one — a hand-authored bare IR still can). Every field here is
|
|
1731
2438
|
* a same-name, same-shape subset of `Slide`'s own optional fields
|
|
1732
2439
|
* (`../ir`'s `SlideSchema`) — reused, not redeclared, so the two can't drift.
|
|
@@ -1738,15 +2445,16 @@ interface PageContent {
|
|
|
1738
2445
|
background?: BackgroundSpec;
|
|
1739
2446
|
image_side?: "left" | "right";
|
|
1740
2447
|
footnote?: string;
|
|
2448
|
+
notes?: string;
|
|
1741
2449
|
}
|
|
1742
2450
|
interface AssembleResult {
|
|
1743
2451
|
ir: PptxIR;
|
|
1744
2452
|
/**
|
|
1745
|
-
* Set only when `
|
|
2453
|
+
* Set only when `spec.seed` was absent and {@link assembleDeck} generated
|
|
1746
2454
|
* one deterministically (see the seed section of this function's doc
|
|
1747
|
-
* comment) — `undefined` when `
|
|
2455
|
+
* comment) — `undefined` when `spec.seed` was already present and simply
|
|
1748
2456
|
* passed through. The CLI shell (W5 task 5) is the one that acts on this:
|
|
1749
|
-
* suggest writing the value back into the
|
|
2457
|
+
* suggest writing the value back into the spec file, never rewrite it
|
|
1750
2458
|
* itself (assemble stays a pure function, no fs side effects here).
|
|
1751
2459
|
*/
|
|
1752
2460
|
generatedSeed?: number;
|
|
@@ -1763,20 +2471,20 @@ interface AssembleResult {
|
|
|
1763
2471
|
materializedLayoutCount?: number;
|
|
1764
2472
|
}
|
|
1765
2473
|
/**
|
|
1766
|
-
* Assemble a validated
|
|
1767
|
-
* IR. See this module's own top comment for the overall shape.
|
|
1768
|
-
* below match the W5 task-3 brief's own numbered "inject
|
|
1769
|
-
* kept in that exact order because two of them
|
|
1770
|
-
* both throw and their relative order is
|
|
1771
|
-
* doc comment alone.
|
|
2474
|
+
* Assemble a validated deck spec plus a per-page content record into a
|
|
2475
|
+
* renderable IR. See this module's own top comment for the overall shape.
|
|
2476
|
+
* Step numbers below match the W5 task-3 brief's own numbered "inject
|
|
2477
|
+
* semantics" list verbatim — kept in that exact order because two of them
|
|
2478
|
+
* (locked-field vs. orphan) both throw and their relative order is
|
|
2479
|
+
* otherwise unobservable from either doc comment alone.
|
|
1772
2480
|
*
|
|
1773
|
-
* 1. `
|
|
2481
|
+
* 1. `spec` is `unknown` (same boundary `validateSpec` itself has — a spec
|
|
1774
2482
|
* is almost always freshly `JSON.parse`d off disk by the caller) —
|
|
1775
2483
|
* invalid shape or a failed hard gate throws {@link PptfastError} with
|
|
1776
|
-
* `
|
|
2484
|
+
* `validateSpec`'s own formatted issue list, not a re-derived message.
|
|
1777
2485
|
* 2. Shape guard + locked-field protection: a `pages[id]` entry must first be
|
|
1778
2486
|
* a plain object — not `null`, an array, or a primitive — else throws.
|
|
1779
|
-
* `pages` is `unknown`-shaped off disk same as `
|
|
2487
|
+
* `pages` is `unknown`-shaped off disk same as `spec` itself (step 1), so
|
|
1780
2488
|
* a JSON `null`/string/array content value is a real possibility, not
|
|
1781
2489
|
* just a type-system hole, and `Object.hasOwn` throws its own
|
|
1782
2490
|
* uninformative native `TypeError` on `null` (and silently no-ops on a
|
|
@@ -1790,63 +2498,69 @@ interface AssembleResult {
|
|
|
1790
2498
|
* `"heading": null`-turned-`undefined` or a copy-pasted empty key is
|
|
1791
2499
|
* exactly the drift this gate exists to catch instead of silently
|
|
1792
2500
|
* ignoring.
|
|
1793
|
-
* 3. Orphan keys: a `pages` entry whose id isn't any
|
|
2501
|
+
* 3. Orphan keys: a `pages` entry whose id isn't any spec page's id. Listed
|
|
1794
2502
|
* together with a fix suggestion, checked only after every present page
|
|
1795
2503
|
* has cleared the locked-field gate (step 2) — an orphan file that
|
|
1796
2504
|
* *also* happens to redeclare `heading` reports as locked-field first.
|
|
1797
|
-
* 4. Missing pages (a
|
|
2505
|
+
* 4. Missing pages (a spec id with no `pages` entry) become a placeholder
|
|
1798
2506
|
* slide — never an error. Spec §7's own words: "assemble's precise
|
|
1799
2507
|
* semantics — a missing page always succeeds (placeholder), a structural
|
|
1800
|
-
* contradiction (orphan file / bad
|
|
2508
|
+
* contradiction (orphan file / bad spec / id conflict) errors". A
|
|
1801
2509
|
* declared `summary` becomes the placeholder's `subheading` (the one spot
|
|
1802
|
-
* `summary` — otherwise a
|
|
2510
|
+
* `summary` — otherwise a spec-only anchor, see step 5 — does reach the
|
|
1803
2511
|
* IR) so a `--draft` preview of an unfilled deck still reads as more than
|
|
1804
2512
|
* a bare "Untitled".
|
|
1805
|
-
* 5. Present pages become a full slide: `id`/`type`/`heading` from the
|
|
1806
|
-
*
|
|
1807
|
-
* {@link PageContent}'s
|
|
1808
|
-
* `
|
|
1809
|
-
* they are
|
|
2513
|
+
* 5. Present pages become a full slide: `id`/`type`/`heading` from the page
|
|
2514
|
+
* spec (never the content record — see step 2), plus whichever of
|
|
2515
|
+
* {@link PageContent}'s seven fields the content record actually set.
|
|
2516
|
+
* `beat`/`focus`/`summary` never reach the IR for a present page —
|
|
2517
|
+
* they are spec-only authoring anchors (beat/focus steer a future
|
|
1810
2518
|
* fill/select step, summary is "for the fill step's own reading only"), not slide content.
|
|
1811
|
-
* 6. Top-level: `version` is always the literal `"
|
|
1812
|
-
* unrelated to the
|
|
1813
|
-
* `
|
|
2519
|
+
* 6. Top-level: `version` is always the literal `"4"` (IR's own version,
|
|
2520
|
+
* unrelated to the deck spec's own `version: "1"`) — the deck spec's own
|
|
2521
|
+
* `narrative` field (renamed this task from `scenario`, spec §8.1's
|
|
2522
|
+
* `DeckPlan`→`DeckSpec` rename, task 2) carries across into the v4 IR's
|
|
2523
|
+
* own `narrative` field, its value already in the new strategy/pacing
|
|
2524
|
+
* vocabulary (vocabulary-v4 rename, task 1 — `plan/index.ts`'s own
|
|
2525
|
+
* `resolveNarrative` call already validates it against that vocabulary
|
|
2526
|
+
* before this function ever runs).
|
|
2527
|
+
* `theme`/`filename`/`brand`/`meta`/`seed` (step 7) carry over from the spec when
|
|
1814
2528
|
* present. When absent, this function omits the field from the raw
|
|
1815
2529
|
* object it hands to {@link PptxIRSchema} rather than re-deriving IR's
|
|
1816
2530
|
* own default value a second time — one default source of truth, the
|
|
1817
2531
|
* schema itself (e.g. `theme` omitted here becomes `{ id: "consulting" }`,
|
|
1818
2532
|
* exactly like a bare hand-authored IR that never mentions theme at all —
|
|
1819
2533
|
* not a value this function needs to know).
|
|
1820
|
-
* 7. Seed: `
|
|
2534
|
+
* 7. Seed: `spec.seed` present → passed through, `generatedSeed` stays
|
|
1821
2535
|
* `undefined` on the result. Absent → {@link generateSeed} derives one
|
|
1822
|
-
* from `filename` + the
|
|
2536
|
+
* from `filename` + the spec's own ordered page-id list (never page
|
|
1823
2537
|
* *content* — see that function's own doc comment for why), written to
|
|
1824
2538
|
* `ir.seed` *and* returned as `generatedSeed` so a CLI shell can suggest
|
|
1825
|
-
* writing it back into the
|
|
2539
|
+
* writing it back into the spec file (this function never touches disk
|
|
1826
2540
|
* itself).
|
|
1827
2541
|
* 8. Idempotence: every step above is a pure function of its inputs (no
|
|
1828
2542
|
* randomness, no wall-clock, no reliance on unordered iteration) — two
|
|
1829
|
-
* calls with structurally-equal `
|
|
2543
|
+
* calls with structurally-equal `spec`/`pages` produce deep-equal
|
|
1830
2544
|
* results, `generatedSeed` included. Exercised directly by this module's
|
|
1831
2545
|
* test suite rather than asserted here.
|
|
1832
2546
|
*/
|
|
1833
|
-
declare function assembleDeck(
|
|
2547
|
+
declare function assembleDeck(spec: unknown, pages: Record<string, PageContent>): AssembleResult;
|
|
1834
2548
|
/**
|
|
1835
|
-
* Inverse of {@link assembleDeck}: reconstructs `{
|
|
2549
|
+
* Inverse of {@link assembleDeck}: reconstructs `{ spec, pages }` from an
|
|
1836
2550
|
* existing IR well enough that `assembleDeck(...disassembleDeck(ir))`
|
|
1837
2551
|
* reproduces every slide's content — but the map is not lossless in both
|
|
1838
2552
|
* directions, only in the direction the round trip actually exercises
|
|
1839
|
-
* (IR →
|
|
2553
|
+
* (IR → spec/pages → IR). Fields with no IR-side home never survive being
|
|
1840
2554
|
* written *to* the IR in the first place, so there is nothing here to read
|
|
1841
2555
|
* back:
|
|
1842
2556
|
*
|
|
1843
|
-
* - `
|
|
1844
|
-
* both are
|
|
2557
|
+
* - `beat` and `focus` never appear on any produced {@link PageSpec} —
|
|
2558
|
+
* both are spec-only authoring anchors with no corresponding `Slide`
|
|
1845
2559
|
* field at all (see {@link assembleDeck} step 5's doc comment). Nothing
|
|
1846
2560
|
* here could recover a value that was never written anywhere.
|
|
1847
2561
|
* - `summary` is recovered *only* for a placeholder slide, by reversing
|
|
1848
2562
|
* step 4's `summary` → `subheading` injection (`slide.subheading` back to
|
|
1849
|
-
* `
|
|
2563
|
+
* `pageSpec.summary`). A non-placeholder slide's own `subheading` — a
|
|
1850
2564
|
* legitimate, independent `Slide` field a hand-authored bare IR is free to
|
|
1851
2565
|
* set — has no {@link PageContent} field to land in (spec §7's pages/
|
|
1852
2566
|
* record is deliberately narrower than `Slide` itself, see that
|
|
@@ -1855,14 +2569,14 @@ declare function assembleDeck(plan: unknown, pages: Record<string, PageContent>)
|
|
|
1855
2569
|
* a filled page) — so it is dropped. Same for `decor`: a real `Slide`
|
|
1856
2570
|
* field, absent from `PageContent`'s shape entirely.
|
|
1857
2571
|
* - `theme.style` / `theme.brand` overrides collapse to a bare theme-id
|
|
1858
|
-
* string (`
|
|
2572
|
+
* string (`DeckSpecSchema.theme` has no shape for either) — only `theme.id`
|
|
1859
2573
|
* survives. That `theme.brand` is `ThemeSchema.brand` (`BrandConfigSchema`
|
|
1860
2574
|
* — `suppressFooterOnCardContent`/`suppressFooterRule`, footer-chrome
|
|
1861
2575
|
* flags owned by the *theme*) — not to be confused with the deck-level
|
|
1862
2576
|
* `brand` field below, a different, unrelated schema despite the shared
|
|
1863
2577
|
* name.
|
|
1864
2578
|
* - `ir.assets.images` is not part of this function's return value at all —
|
|
1865
|
-
* `{
|
|
2579
|
+
* `{ spec, pages }` has no `assets` field, and this module stays zero-fs
|
|
1866
2580
|
* by design (this file's own top comment), so it has no way to write an
|
|
1867
2581
|
* `assets/` directory itself. Any `asset_id` reference inside a copied
|
|
1868
2582
|
* `components`/`background` survives untouched ({@link extractPageContent}
|
|
@@ -1881,10 +2595,10 @@ declare function assembleDeck(plan: unknown, pages: Record<string, PageContent>)
|
|
|
1881
2595
|
* collision: the top-level `brand` field (`BrandSchema` — `logo_asset_id` /
|
|
1882
2596
|
* `position`, the deck logo/position `BrandChrome` reads,
|
|
1883
2597
|
* `src/svg/BrandChrome.tsx`) is a plain passthrough on both sides
|
|
1884
|
-
* ({@link assembleDeck} step 6 reads `
|
|
1885
|
-
* function reads `ir.brand` back into `
|
|
1886
|
-
* unmodified, same as `
|
|
1887
|
-
* dropped.
|
|
2598
|
+
* ({@link assembleDeck} step 6 reads `spec.brand` into `ir.brand` — this
|
|
2599
|
+
* function reads `ir.brand` back into `spec.brand` below) — carried through
|
|
2600
|
+
* unmodified, same as `narrative`/`filename`/`seed`, never
|
|
2601
|
+
* synthesized or dropped.
|
|
1888
2602
|
*
|
|
1889
2603
|
* `layout` deserves a different kind of callout: it round-trips as plain
|
|
1890
2604
|
* content like any other field ({@link extractPageContent} copies
|
|
@@ -1913,7 +2627,7 @@ declare function assembleDeck(plan: unknown, pages: Record<string, PageContent>)
|
|
|
1913
2627
|
* {@link UNTITLED_HEADING} for a missing/blank `heading`, and a positional
|
|
1914
2628
|
* `p-<1-based-ordinal>-<type>` scheme for a missing `id` (stable across
|
|
1915
2629
|
* repeated calls on the same IR — it is a pure function of slide position
|
|
1916
|
-
* and type — but, unlike a
|
|
2630
|
+
* and type — but, unlike a spec-assigned id, *not* stable across inserting
|
|
1917
2631
|
* or reordering slides — out of scope here, since a bare IR with no `id` at
|
|
1918
2632
|
* all has no stabler identity to fall back to in the first place).
|
|
1919
2633
|
*
|
|
@@ -1932,10 +2646,44 @@ declare function assembleDeck(plan: unknown, pages: Record<string, PageContent>)
|
|
|
1932
2646
|
* function) is what actually closes the vulnerability.
|
|
1933
2647
|
*/
|
|
1934
2648
|
declare function disassembleDeck(ir: PptxIR): {
|
|
1935
|
-
|
|
2649
|
+
spec: DeckSpec;
|
|
1936
2650
|
pages: Record<string, PageContent>;
|
|
1937
2651
|
};
|
|
1938
2652
|
|
|
2653
|
+
/**
|
|
2654
|
+
* Deterministic, pure `deck.plan.json` → `deck.spec.json` migration (spec
|
|
2655
|
+
* §9.2, vocabulary-v4 rename, task 2). Mirrors `../ir/migrate.ts`'s
|
|
2656
|
+
* `migrateIrV3ToV4` in spirit — mechanical field rename only, no schema
|
|
2657
|
+
* validation, no model call, no content rewrite (same posture spec §9.3
|
|
2658
|
+
* states for the IR v3→v4 primitive: "只做已声明的结构映射,不运行模型,
|
|
2659
|
+
* 不重写内容,不重新选择 layout" — this function is that same contract
|
|
2660
|
+
* applied to the other artifact this rename touches). A caller should run
|
|
2661
|
+
* `validateSpec` (`./index.ts`) against the result to confirm it lands as a
|
|
2662
|
+
* legal deck spec, exactly as it would for a `deck.spec.json` authored by
|
|
2663
|
+
* hand — this function itself never parses against `DeckSpecSchema`.
|
|
2664
|
+
*
|
|
2665
|
+
* Field-for-field, value-for-value per spec §9.2's table:
|
|
2666
|
+
*
|
|
2667
|
+
* ```text
|
|
2668
|
+
* deck.plan.json → deck.spec.json
|
|
2669
|
+
* scenario → narrative
|
|
2670
|
+
* pages[].rhythm → pages[].beat
|
|
2671
|
+
* 其余字段 → 原样保留
|
|
2672
|
+
* ```
|
|
2673
|
+
*
|
|
2674
|
+
* Takes the raw, `JSON.parse`d `deck.plan.json` contents (`unknown`) rather
|
|
2675
|
+
* than an already-validated shape — unlike `migrateIrV3ToV4` (which takes an
|
|
2676
|
+
* already-`PptxIRV3Schema.parse`d object), there is no schema left in this
|
|
2677
|
+
* codebase a pre-rename plan file could validate against: `DeckSpecSchema`
|
|
2678
|
+
* (`./index.ts`) already requires the *post*-rename field names (`narrative`,
|
|
2679
|
+
* `beat`), so parsing a plan-shaped document against it would just fail on
|
|
2680
|
+
* the very keys this function exists to rename. Non-object input, or a
|
|
2681
|
+
* `pages` value that isn't an array, passes through completely unchanged —
|
|
2682
|
+
* reporting a malformed source file is `runMigrate`'s job
|
|
2683
|
+
* (`../cli/commands.ts`), not this mechanical rename step's.
|
|
2684
|
+
*/
|
|
2685
|
+
declare function migrateDeckPlanToSpec(raw: unknown): unknown;
|
|
2686
|
+
|
|
1939
2687
|
/**
|
|
1940
2688
|
* `pptfast audit` finding shape (v0.3 W6, spec §7 workflow ④). `page` is
|
|
1941
2689
|
* 1-based (matches `ValidationIssue.page` in `api.ts`); `slideId` is set
|
|
@@ -1946,38 +2694,72 @@ declare function disassembleDeck(ir: PptxIR): {
|
|
|
1946
2694
|
interface AuditFinding {
|
|
1947
2695
|
page: number;
|
|
1948
2696
|
slideId?: string;
|
|
1949
|
-
code: "overflow" | "out-of-bounds" | "low-contrast" | "overlap";
|
|
2697
|
+
code: "overflow" | "out-of-bounds" | "low-contrast" | "overlap" | "content-truncated" | "content-dropped";
|
|
1950
2698
|
message: string;
|
|
1951
2699
|
detail?: Record<string, unknown>;
|
|
1952
2700
|
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Which check families actually ran (audit-v2 phase B, spec §4.2/§11.6) —
|
|
2703
|
+
* the one piece of the spec's original metrics design that survives into
|
|
2704
|
+
* this wave (§11.6: metrics itself deferred, "唯一现值是 checks 结构"). This
|
|
2705
|
+
* is the wave's own spirit made literal: a field can only ever be
|
|
2706
|
+
* `"completed"` once its check family genuinely ran over every audited
|
|
2707
|
+
* page, never a default that silently reads as "passed". `svg` has no
|
|
2708
|
+
* `"not-requested"` state — the deterministic SVG audit always runs, so it
|
|
2709
|
+
* is always `"completed"`. `pixels` starts `"not-requested"` and only ever
|
|
2710
|
+
* becomes `"completed"`; there is deliberately no `"failed"` value — a
|
|
2711
|
+
* failed pixel audit throws (spec §11.7's "契约层"), it never reports
|
|
2712
|
+
* itself as a completed check that happens to carry no findings.
|
|
2713
|
+
*/
|
|
2714
|
+
interface AuditChecks {
|
|
2715
|
+
svg: "completed";
|
|
2716
|
+
pixels: "not-requested" | "completed";
|
|
2717
|
+
}
|
|
1953
2718
|
interface AuditReport {
|
|
1954
2719
|
findings: AuditFinding[];
|
|
1955
2720
|
pagesAudited: number;
|
|
1956
2721
|
pagesSkipped: number;
|
|
2722
|
+
checks: AuditChecks;
|
|
2723
|
+
}
|
|
2724
|
+
/** `auditDeck`'s second parameter (audit-v2 phase B) — see that function's
|
|
2725
|
+
* own doc comment for the overload contract this shape backs. */
|
|
2726
|
+
interface AuditDeckOptions {
|
|
2727
|
+
pixels?: boolean;
|
|
1957
2728
|
}
|
|
1958
2729
|
/**
|
|
1959
|
-
*
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
1964
|
-
*
|
|
1965
|
-
*
|
|
1966
|
-
* `parseSvg`'s doc comment) — `auditDeck` itself never calls
|
|
1967
|
-
* `installNodePlatform()`; that's the caller's job (the CLI does it
|
|
1968
|
-
* automatically).
|
|
1969
|
-
*
|
|
1970
|
-
* Advisory, not a hard gate: `validateIr` already rejects structurally
|
|
1971
|
-
* invalid or over-dense decks before a caller ever gets this far; this
|
|
1972
|
-
* function looks for the visual problems that can still slip through a
|
|
1973
|
-
* valid deck at render time (an author-chosen near-background text color,
|
|
1974
|
-
* two components whose combined content happens to collide). A non-empty
|
|
1975
|
-
* `findings` array is a prompt for a human/agent to look, not a rejection.
|
|
2730
|
+
* The SDK entry point. Advisory, not a hard gate: `validateIr` already
|
|
2731
|
+
* rejects structurally invalid or over-dense decks before a caller ever
|
|
2732
|
+
* gets this far; this function looks for the visual problems that can still
|
|
2733
|
+
* slip through a valid deck at render time (an author-chosen near-background
|
|
2734
|
+
* text color, two components whose combined content happens to collide, a
|
|
2735
|
+
* card list that had to drop an item to fit). A non-empty `findings` array
|
|
2736
|
+
* is a prompt for a human/agent to look, not a rejection.
|
|
1976
2737
|
*
|
|
1977
2738
|
* Placeholder pages (`slide.placeholder === true`) are skipped — assemble's
|
|
1978
2739
|
* stand-in for content nobody has written yet has nothing to audit, same
|
|
1979
2740
|
* reasoning `checkIrQuality` already uses to skip them (`ir-quality.ts`).
|
|
2741
|
+
*
|
|
2742
|
+
* `auditDeck` itself never calls `installNodePlatform()`; that's the
|
|
2743
|
+
* caller's job (the CLI does it automatically).
|
|
2744
|
+
*
|
|
2745
|
+
* `opts.pixels` (audit-v2 phase B, spec §4.3/§11.7) opts into the optional
|
|
2746
|
+
* pixel-level contrast audit over image-backed text (`../svg/audit/pixel-audit.ts`)
|
|
2747
|
+
* — overloaded so the far more common omitted/`false` case keeps returning
|
|
2748
|
+
* a plain, synchronous `AuditReport` (spec §11.7's "语义层": the default
|
|
2749
|
+
* audit stays pure TS with zero added latency or Promise-wrapping), while
|
|
2750
|
+
* `pixels: true` returns a `Promise<AuditReport>` that resolves once the
|
|
2751
|
+
* rasterization pass completes. A caller holding a non-literal
|
|
2752
|
+
* `{ pixels: someBoolean }` won't match either overload directly (TypeScript
|
|
2753
|
+
* can't narrow it to one specific branch) — branch on the boolean first and
|
|
2754
|
+
* call this function with a literal in each arm, e.g. `opts.pixels ? await
|
|
2755
|
+
* auditDeck(ir, { pixels: true }) : auditDeck(ir)` (`cli/commands.ts`'s
|
|
2756
|
+
* `runAudit` does exactly this).
|
|
1980
2757
|
*/
|
|
1981
|
-
declare function auditDeck(ir: PptxIR
|
|
2758
|
+
declare function auditDeck(ir: PptxIR, opts?: {
|
|
2759
|
+
pixels?: false;
|
|
2760
|
+
}): AuditReport;
|
|
2761
|
+
declare function auditDeck(ir: PptxIR, opts: {
|
|
2762
|
+
pixels: true;
|
|
2763
|
+
}): Promise<AuditReport>;
|
|
1982
2764
|
|
|
1983
|
-
export { AUDIENCE_VALUES, type AssembleResult, type Assets, type Audience, type AuditFinding, type AuditReport, BUILTIN_THEME_IDS, type BackgroundSpec, type BrandConfig, BrandConfigSchema, COMPONENT_TYPES, type Component,
|
|
2765
|
+
export { AUDIENCE_VALUES, type AssembleResult, type Assets, type Audience, type AuditChecks, type AuditDeckOptions, type AuditFinding, type AuditReport, BUILTIN_THEME_IDS, type BackgroundSpec, type BrandConfig, BrandConfigSchema, COMPONENT_TYPES, type Component, DEFAULT_NARRATIVE, type DeckSpec, DeckSpecSchema, type Meta, NARRATIVE_PRESETS, type NarrativePreset, type NarrativeProfile, PACING_BUDGETS, PACING_VALUES, type Pacing, type PacingBudget, type PageBeat, type PageContent, type PageSpec, PageSpecSchema, type PageSpecType, PptfastError, type PptxIR, PptxIRSchema, type PptxIRV3, PptxIRV3Schema, SPEC_PAGE_COUNT_RANGE, STRATEGY_DEFINITIONS, STRATEGY_VALUES, type Slide, type SpecValidateResult, type SpecValidationIssue, type Strategy, type StrategyDefinition, type StyleOverride, StyleOverrideSchema, type ThemeDefinition, type ThemeInfo, type ThemeRegistration, ThemeSchema, VERSION, type ValidateResult, type ValidationIssue, assembleDeck, auditDeck, disassembleDeck, formatIssues, formatSpecIssues, generatePptx, getInstalledThemeIds, getThemeDefinition, irJsonSchema, listThemes, migrateDeckPlanToSpec, migrateIrV3ToV4, registerTheme, renderSlideSvg, resolveNarrative, resolveSpecThemeId, specJsonSchema, styleJsonSchema, validateIr, validateSpec };
|