@liustack/pptfast 0.1.0 → 0.4.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 +114 -11
- package/README.zh-CN.md +108 -11
- package/dist/chunk-4W2YZPJJ.js +564 -0
- package/dist/chunk-4W2YZPJJ.js.map +1 -0
- package/dist/chunk-7N4HGSMW.js +19013 -0
- package/dist/chunk-7N4HGSMW.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 +1094 -44
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1716 -62
- package/dist/index.js +66 -6
- package/dist/node.d.ts +3 -2
- package/dist/node.js +2 -2
- package/dist/pixel-audit-4DXKLFBV.js +176 -0
- package/dist/pixel-audit-4DXKLFBV.js.map +1 -0
- package/dist/registry-V079Jkry.d.ts +40 -0
- package/package.json +6 -4
- package/dist/chunk-4WISUDXE.js +0 -34
- package/dist/chunk-4WISUDXE.js.map +0 -1
- package/dist/chunk-DVMJWFDL.js +0 -14465
- package/dist/chunk-DVMJWFDL.js.map +0 -1
- package/dist/chunk-G76EVNVT.js +0 -14
- package/dist/chunk-G76EVNVT.js.map +0 -1
- package/dist/registry-CYKxZ0-U.d.ts +0 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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.4.0";
|
|
5
5
|
|
|
6
6
|
/** Error class thrown by pptfast's public API surface (validation, rendering, export). */
|
|
7
7
|
declare class PptfastError extends Error {
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
declare const
|
|
10
|
+
declare const BUILTIN_THEME_IDS: readonly ["consulting", "enterprise", "academic", "insight", "campaign", "bloom", "classroom", "ink", "tech", "runway", "journal", "luxe", "heritage"];
|
|
11
11
|
declare const BackgroundSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
12
12
|
kind: z.ZodLiteral<"color">;
|
|
13
13
|
value: z.ZodString;
|
|
@@ -32,6 +32,79 @@ declare const BackgroundSpecSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
32
32
|
contain: "contain";
|
|
33
33
|
}>>;
|
|
34
34
|
}, z.core.$strict>], "kind">;
|
|
35
|
+
/**
|
|
36
|
+
* Style-token override (theme.style): deep-partial palette/fonts/shape
|
|
37
|
+
* merged over the built-in theme (see themes/index.ts resolveStyle). Scope is
|
|
38
|
+
* deliberately palette-level (spec §11): no defaultBackgrounds or manifest
|
|
39
|
+
* overrides. gapScale range mirrors the documented sane range in
|
|
40
|
+
* themes/tokens.ts StyleShape.
|
|
41
|
+
*/
|
|
42
|
+
declare const StyleOverrideSchema: z.ZodObject<{
|
|
43
|
+
colors: z.ZodOptional<z.ZodObject<{
|
|
44
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
45
|
+
surface: z.ZodOptional<z.ZodString>;
|
|
46
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
47
|
+
primary: z.ZodOptional<z.ZodString>;
|
|
48
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
49
|
+
text: z.ZodOptional<z.ZodString>;
|
|
50
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
51
|
+
border: z.ZodOptional<z.ZodString>;
|
|
52
|
+
chartPalette: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
53
|
+
accentPool: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
54
|
+
cardStroke: z.ZodOptional<z.ZodString>;
|
|
55
|
+
}, z.core.$strict>>;
|
|
56
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
heading: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
|
+
body: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
+
mono: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
60
|
+
}, z.core.$strict>>;
|
|
61
|
+
shape: z.ZodOptional<z.ZodObject<{
|
|
62
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
63
|
+
gapScale: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.core.$strict>>;
|
|
65
|
+
}, z.core.$strict>;
|
|
66
|
+
type StyleOverride = z.infer<typeof StyleOverrideSchema>;
|
|
67
|
+
/**
|
|
68
|
+
* Brand (logical slide-master) config: brand-chrome behavior owned by a theme.
|
|
69
|
+
* W1 scope = exactly the two flags migrated from the old manifest.chrome.
|
|
70
|
+
* Single source of truth — the TS type is inferred, never hand-written.
|
|
71
|
+
*/
|
|
72
|
+
declare const BrandConfigSchema: z.ZodObject<{
|
|
73
|
+
suppressFooterOnCardContent: z.ZodOptional<z.ZodBoolean>;
|
|
74
|
+
suppressFooterRule: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
}, z.core.$strict>;
|
|
76
|
+
type BrandConfig = z.infer<typeof BrandConfigSchema>;
|
|
77
|
+
declare const ThemeSchema: z.ZodObject<{
|
|
78
|
+
id: z.ZodDefault<z.ZodString>;
|
|
79
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
80
|
+
colors: z.ZodOptional<z.ZodObject<{
|
|
81
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
82
|
+
surface: z.ZodOptional<z.ZodString>;
|
|
83
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
84
|
+
primary: z.ZodOptional<z.ZodString>;
|
|
85
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
86
|
+
text: z.ZodOptional<z.ZodString>;
|
|
87
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
88
|
+
border: z.ZodOptional<z.ZodString>;
|
|
89
|
+
chartPalette: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
90
|
+
accentPool: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
91
|
+
cardStroke: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strict>>;
|
|
93
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
94
|
+
heading: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
95
|
+
body: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
96
|
+
mono: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
97
|
+
}, z.core.$strict>>;
|
|
98
|
+
shape: z.ZodOptional<z.ZodObject<{
|
|
99
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
100
|
+
gapScale: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
}, z.core.$strict>>;
|
|
103
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
104
|
+
suppressFooterOnCardContent: z.ZodOptional<z.ZodBoolean>;
|
|
105
|
+
suppressFooterRule: z.ZodOptional<z.ZodBoolean>;
|
|
106
|
+
}, z.core.$strict>>;
|
|
107
|
+
}, z.core.$strict>;
|
|
35
108
|
declare const MetaSchema: z.ZodObject<{
|
|
36
109
|
organization: z.ZodOptional<z.ZodString>;
|
|
37
110
|
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -74,7 +147,7 @@ declare const AssetsSchema: z.ZodObject<{
|
|
|
74
147
|
error: z.ZodOptional<z.ZodString>;
|
|
75
148
|
}, z.core.$strict>>>;
|
|
76
149
|
}, z.core.$strict>;
|
|
77
|
-
declare const
|
|
150
|
+
declare const ComponentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
78
151
|
type: z.ZodLiteral<"bullets">;
|
|
79
152
|
items: z.ZodArray<z.ZodString>;
|
|
80
153
|
style: z.ZodOptional<z.ZodEnum<{
|
|
@@ -127,9 +200,9 @@ declare const BlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
127
200
|
chart_type: z.ZodEnum<{
|
|
128
201
|
line: "line";
|
|
129
202
|
dumbbell: "dumbbell";
|
|
203
|
+
funnel: "funnel";
|
|
130
204
|
bar: "bar";
|
|
131
205
|
pie: "pie";
|
|
132
|
-
funnel: "funnel";
|
|
133
206
|
}>;
|
|
134
207
|
direction: z.ZodOptional<z.ZodEnum<{
|
|
135
208
|
horizontal: "horizontal";
|
|
@@ -319,15 +392,70 @@ declare const BlockSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
319
392
|
vs: "vs";
|
|
320
393
|
before_after: "before_after";
|
|
321
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>>;
|
|
322
437
|
}, z.core.$strict>], "type">;
|
|
438
|
+
/**
|
|
439
|
+
* All 28 component `type` discriminant values, derived from `ComponentSchema`
|
|
440
|
+
* itself (never hand-copied) so this list can't drift from the union above.
|
|
441
|
+
* Typed as plain `readonly string[]` rather than `Component["type"][]` —
|
|
442
|
+
* every consumer of this list (W5's plan `focus` vocabulary gate,
|
|
443
|
+
* `src/plan/index.ts`) tests membership of an arbitrary author-supplied
|
|
444
|
+
* string, and TS's `Array.includes` is invariant in its element type, so a
|
|
445
|
+
* narrower literal-union type would reject that call at the caller.
|
|
446
|
+
*/
|
|
447
|
+
declare const COMPONENT_TYPES: readonly string[];
|
|
323
448
|
declare const SlideSchema: z.ZodObject<{
|
|
324
|
-
type: z.ZodEnum<{
|
|
449
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
325
450
|
cover: "cover";
|
|
326
451
|
chapter: "chapter";
|
|
327
452
|
content: "content";
|
|
328
453
|
ending: "ending";
|
|
329
|
-
}
|
|
330
|
-
|
|
454
|
+
}>>;
|
|
455
|
+
id: z.ZodOptional<z.ZodString>;
|
|
456
|
+
placeholder: z.ZodOptional<z.ZodLiteral<true>>;
|
|
457
|
+
layout: z.ZodOptional<z.ZodString>;
|
|
458
|
+
arrangement: z.ZodOptional<z.ZodEnum<{
|
|
331
459
|
code: "code";
|
|
332
460
|
quote: "quote";
|
|
333
461
|
single: "single";
|
|
@@ -336,15 +464,11 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
336
464
|
image_focus: "image_focus";
|
|
337
465
|
big_number: "big_number";
|
|
338
466
|
assertion_evidence: "assertion_evidence";
|
|
339
|
-
image_split: "image_split";
|
|
340
|
-
image_top: "image_top";
|
|
341
|
-
image_bottom: "image_bottom";
|
|
342
|
-
image_annotate: "image_annotate";
|
|
343
467
|
aside: "aside";
|
|
344
468
|
}>>;
|
|
345
469
|
heading: z.ZodOptional<z.ZodString>;
|
|
346
470
|
subheading: z.ZodOptional<z.ZodString>;
|
|
347
|
-
|
|
471
|
+
components: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
348
472
|
type: z.ZodLiteral<"bullets">;
|
|
349
473
|
items: z.ZodArray<z.ZodString>;
|
|
350
474
|
style: z.ZodOptional<z.ZodEnum<{
|
|
@@ -397,9 +521,9 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
397
521
|
chart_type: z.ZodEnum<{
|
|
398
522
|
line: "line";
|
|
399
523
|
dumbbell: "dumbbell";
|
|
524
|
+
funnel: "funnel";
|
|
400
525
|
bar: "bar";
|
|
401
526
|
pie: "pie";
|
|
402
|
-
funnel: "funnel";
|
|
403
527
|
}>;
|
|
404
528
|
direction: z.ZodOptional<z.ZodEnum<{
|
|
405
529
|
horizontal: "horizontal";
|
|
@@ -589,6 +713,48 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
589
713
|
vs: "vs";
|
|
590
714
|
before_after: "before_after";
|
|
591
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>>;
|
|
592
758
|
}, z.core.$strict>], "type">>>;
|
|
593
759
|
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
594
760
|
kind: z.ZodLiteral<"color">;
|
|
@@ -633,33 +799,43 @@ declare const SlideSchema: z.ZodObject<{
|
|
|
633
799
|
right: "right";
|
|
634
800
|
}>>;
|
|
635
801
|
footnote: z.ZodOptional<z.ZodString>;
|
|
802
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
636
803
|
}, z.core.$strict>;
|
|
637
804
|
declare const PptxIRSchema: z.ZodObject<{
|
|
638
|
-
version: z.ZodLiteral<"
|
|
639
|
-
filename: z.ZodString
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
805
|
+
version: z.ZodDefault<z.ZodLiteral<"4">>;
|
|
806
|
+
filename: z.ZodDefault<z.ZodString>;
|
|
807
|
+
narrative: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
808
|
+
theme: z.ZodDefault<z.ZodObject<{
|
|
809
|
+
id: z.ZodDefault<z.ZodString>;
|
|
810
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
811
|
+
colors: z.ZodOptional<z.ZodObject<{
|
|
812
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
813
|
+
surface: z.ZodOptional<z.ZodString>;
|
|
814
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
815
|
+
primary: z.ZodOptional<z.ZodString>;
|
|
816
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
817
|
+
text: z.ZodOptional<z.ZodString>;
|
|
818
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
819
|
+
border: z.ZodOptional<z.ZodString>;
|
|
820
|
+
chartPalette: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
821
|
+
accentPool: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
822
|
+
cardStroke: z.ZodOptional<z.ZodString>;
|
|
823
|
+
}, z.core.$strict>>;
|
|
824
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
825
|
+
heading: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
826
|
+
body: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
827
|
+
mono: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
828
|
+
}, z.core.$strict>>;
|
|
829
|
+
shape: z.ZodOptional<z.ZodObject<{
|
|
830
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
831
|
+
gapScale: z.ZodOptional<z.ZodNumber>;
|
|
832
|
+
}, z.core.$strict>>;
|
|
661
833
|
}, z.core.$strict>>;
|
|
662
|
-
|
|
834
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
835
|
+
suppressFooterOnCardContent: z.ZodOptional<z.ZodBoolean>;
|
|
836
|
+
suppressFooterRule: z.ZodOptional<z.ZodBoolean>;
|
|
837
|
+
}, z.core.$strict>>;
|
|
838
|
+
}, z.core.$strict>>;
|
|
663
839
|
meta: z.ZodDefault<z.ZodObject<{
|
|
664
840
|
organization: z.ZodOptional<z.ZodString>;
|
|
665
841
|
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -711,14 +887,18 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
711
887
|
br: "br";
|
|
712
888
|
}>>;
|
|
713
889
|
}, z.core.$strict>>;
|
|
890
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
714
891
|
slides: z.ZodArray<z.ZodObject<{
|
|
715
|
-
type: z.ZodEnum<{
|
|
892
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
716
893
|
cover: "cover";
|
|
717
894
|
chapter: "chapter";
|
|
718
895
|
content: "content";
|
|
719
896
|
ending: "ending";
|
|
720
|
-
}
|
|
721
|
-
|
|
897
|
+
}>>;
|
|
898
|
+
id: z.ZodOptional<z.ZodString>;
|
|
899
|
+
placeholder: z.ZodOptional<z.ZodLiteral<true>>;
|
|
900
|
+
layout: z.ZodOptional<z.ZodString>;
|
|
901
|
+
arrangement: z.ZodOptional<z.ZodEnum<{
|
|
722
902
|
code: "code";
|
|
723
903
|
quote: "quote";
|
|
724
904
|
single: "single";
|
|
@@ -727,15 +907,11 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
727
907
|
image_focus: "image_focus";
|
|
728
908
|
big_number: "big_number";
|
|
729
909
|
assertion_evidence: "assertion_evidence";
|
|
730
|
-
image_split: "image_split";
|
|
731
|
-
image_top: "image_top";
|
|
732
|
-
image_bottom: "image_bottom";
|
|
733
|
-
image_annotate: "image_annotate";
|
|
734
910
|
aside: "aside";
|
|
735
911
|
}>>;
|
|
736
912
|
heading: z.ZodOptional<z.ZodString>;
|
|
737
913
|
subheading: z.ZodOptional<z.ZodString>;
|
|
738
|
-
|
|
914
|
+
components: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
739
915
|
type: z.ZodLiteral<"bullets">;
|
|
740
916
|
items: z.ZodArray<z.ZodString>;
|
|
741
917
|
style: z.ZodOptional<z.ZodEnum<{
|
|
@@ -788,9 +964,9 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
788
964
|
chart_type: z.ZodEnum<{
|
|
789
965
|
line: "line";
|
|
790
966
|
dumbbell: "dumbbell";
|
|
967
|
+
funnel: "funnel";
|
|
791
968
|
bar: "bar";
|
|
792
969
|
pie: "pie";
|
|
793
|
-
funnel: "funnel";
|
|
794
970
|
}>;
|
|
795
971
|
direction: z.ZodOptional<z.ZodEnum<{
|
|
796
972
|
horizontal: "horizontal";
|
|
@@ -980,6 +1156,48 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
980
1156
|
vs: "vs";
|
|
981
1157
|
before_after: "before_after";
|
|
982
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>>;
|
|
983
1201
|
}, z.core.$strict>], "type">>>;
|
|
984
1202
|
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
985
1203
|
kind: z.ZodLiteral<"color">;
|
|
@@ -1024,10 +1242,11 @@ declare const PptxIRSchema: z.ZodObject<{
|
|
|
1024
1242
|
right: "right";
|
|
1025
1243
|
}>>;
|
|
1026
1244
|
footnote: z.ZodOptional<z.ZodString>;
|
|
1245
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
1027
1246
|
}, z.core.$strict>>;
|
|
1028
1247
|
}, z.core.$strict>;
|
|
1029
1248
|
type PptxIR = z.infer<typeof PptxIRSchema>;
|
|
1030
|
-
type
|
|
1249
|
+
type Component = z.infer<typeof ComponentSchema>;
|
|
1031
1250
|
type BackgroundSpec = z.infer<typeof BackgroundSpecSchema>;
|
|
1032
1251
|
type Slide = z.infer<typeof SlideSchema>;
|
|
1033
1252
|
type Assets = z.infer<typeof AssetsSchema>;
|
|
@@ -1038,39 +1257,1474 @@ interface ValidationIssue {
|
|
|
1038
1257
|
message: string;
|
|
1039
1258
|
/** 1-based slide number when the issue is scoped to a slide. */
|
|
1040
1259
|
page?: number;
|
|
1260
|
+
/**
|
|
1261
|
+
* The offending slide's own `id` (`Slide.id`, `ir/index.ts`) — W5
|
|
1262
|
+
* whole-branch review finding 2: the README already claimed "validation
|
|
1263
|
+
* error messages reference [a slide] by [its] id"; this is what makes
|
|
1264
|
+
* that true. Set by every page-scoped issue producer
|
|
1265
|
+
* ({@link checkLayoutApplicability}, {@link checkBoundaryPageContent},
|
|
1266
|
+
* the content-quality-gate translation in {@link validateIr},
|
|
1267
|
+
* {@link checkDuplicateSlideIds})
|
|
1268
|
+
* when the slide in question has an `id` — absent when the slide has
|
|
1269
|
+
* none (bare, pre-W5 IR) or the issue is deck-level, not scoped to any
|
|
1270
|
+
* single slide. {@link formatIssues} appends it in parens after the page
|
|
1271
|
+
* number.
|
|
1272
|
+
*/
|
|
1273
|
+
slideId?: string;
|
|
1041
1274
|
}
|
|
1042
1275
|
interface ValidateResult {
|
|
1043
1276
|
ok: boolean;
|
|
1044
1277
|
ir?: PptxIR;
|
|
1045
1278
|
errors: ValidationIssue[];
|
|
1279
|
+
/**
|
|
1280
|
+
* Human-readable "`path`: `alias` → `canonical`" entries for every
|
|
1281
|
+
* deterministic field-alias rewrite `validateIr` applied before parsing
|
|
1282
|
+
* (W5 task 4, `ir/field-aliases.ts`'s `normalizeComponentAliases`) — e.g. a
|
|
1283
|
+
* kpi item's `title` silently adopted as `label`. Present only when at
|
|
1284
|
+
* least one rewrite happened; informational, never gates `ok` on its own.
|
|
1285
|
+
*/
|
|
1286
|
+
normalized?: string[];
|
|
1046
1287
|
}
|
|
1047
1288
|
/**
|
|
1048
|
-
* Validate raw JSON against the IR schema, then — once it parses —
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1053
|
-
*
|
|
1054
|
-
*
|
|
1055
|
-
* (
|
|
1056
|
-
*
|
|
1057
|
-
*
|
|
1058
|
-
*
|
|
1289
|
+
* Validate raw JSON against the IR schema, then — once it parses — resolve
|
|
1290
|
+
* `narrative` (`resolveNarrative`, spec §5: an unrecognized preset name is a
|
|
1291
|
+
* `narrative`-path error, page-less) and run the content-quality gate
|
|
1292
|
+
* (`checkIrQuality`, passed the resolved axes) against the parsed IR. All
|
|
1293
|
+
* stages must pass for `ok: true`. Quality findings are reported the same
|
|
1294
|
+
* way as schema errors (page-scoped, 1-based). `checkIrQuality` itself tags
|
|
1295
|
+
* findings "warn" vs "error", but that split was designed for its original
|
|
1296
|
+
* consumer (surfacing informational warnings in a UI after generation, per
|
|
1297
|
+
* its own docstring) — here it is the pre-generation hard gate, so any
|
|
1298
|
+
* finding blocks (`ok: false`), not only "error"-severity ones. Treating
|
|
1299
|
+
* "warn" findings (e.g. a cover with no heading) as advisory-only would
|
|
1300
|
+
* defeat the point of wiring this in: the spec's core principle is a hard
|
|
1301
|
+
* protocol gate, not hoped-for prompt compliance.
|
|
1302
|
+
*
|
|
1303
|
+
* Before any of that, one deterministic alias pass runs
|
|
1304
|
+
* ({@link normalizeComponentAliases}, W5 task 4) for the component
|
|
1305
|
+
* field-name synonym rescue only (kpi `title`→`label`, quote
|
|
1306
|
+
* `content`→`text`, …) — a weak-model rescue for schema-internal synonym
|
|
1307
|
+
* drift, scoped to `slides[]`. It only rewrites where the canonical key is
|
|
1308
|
+
* absent, so the schema parse below never sees an alias as an "unrecognized
|
|
1309
|
+
* key" in the first place. Purely informational: every rewrite is recorded
|
|
1310
|
+
* as a human-readable `path: alias → canonical` string and threaded onto
|
|
1311
|
+
* `ValidateResult.normalized` on *every* return path below via
|
|
1312
|
+
* `withNormalized`, success or failure alike — it never itself gates `ok`.
|
|
1313
|
+
*
|
|
1314
|
+
* There is deliberately no root/narrative-level alias pass (spec §16,
|
|
1315
|
+
* reversing the now-superseded §15.4): a v4 document that still spells its
|
|
1316
|
+
* pre-rename vocabulary — `scenario` instead of `narrative`, `mode`/
|
|
1317
|
+
* `delivery` instead of `strategy`/`pacing`, or the old enum values
|
|
1318
|
+
* `"text"`/`"presentation"`/`"narrative"` — is not old-vocabulary
|
|
1319
|
+
* *compatibility*, it is exactly the vocabulary this rename retired, so it
|
|
1320
|
+
* hard-errors like any other unknown key or value: `scenario` fails the
|
|
1321
|
+
* schema's `.strict()` parse below as an unrecognized key, and an old enum
|
|
1322
|
+
* value (or the axis-key names `mode`/`delivery` inside `narrative`, which
|
|
1323
|
+
* the schema itself leaves open) fails `resolveNarrative`'s own runtime
|
|
1324
|
+
* check, listing the current values. `pptfast migrate` (`ir/migrate.ts`)
|
|
1325
|
+
* remains the sanctioned bridge for a genuine v3 document — see the v3 hard
|
|
1326
|
+
* reject below, which points there.
|
|
1327
|
+
*
|
|
1328
|
+
* The component-alias pass only ever runs for a document already headed for
|
|
1329
|
+
* the v4 schema — an explicit `version: "2"` or `version: "3"` is
|
|
1330
|
+
* hard-rejected first, below, before the alias pass or any schema parse
|
|
1331
|
+
* (spec §9.3: a v2/v3 document is never silently reinterpreted as v4).
|
|
1059
1332
|
*/
|
|
1060
1333
|
declare function validateIr(input: unknown): ValidateResult;
|
|
1334
|
+
/**
|
|
1335
|
+
* `"page 2 (p-kpi) — path: message"` when the issue carries both a `page`
|
|
1336
|
+
* and a `slideId` (W5 whole-branch review finding 2 — the README's own
|
|
1337
|
+
* claim that a validation error "references [a slide] by [its] id", made
|
|
1338
|
+
* true) — the parenthesized id is appended only alongside a `page` number,
|
|
1339
|
+
* never on its own: a deck-level issue that happens to set a representative
|
|
1340
|
+
* `slideId` with no `page` ({@link checkDuplicateSlideIds} above) keeps its
|
|
1341
|
+
* pre-existing, unchanged format. Every other combination — `page` with no
|
|
1342
|
+
* `slideId` (an id-less slide), or neither — is byte-identical to before
|
|
1343
|
+
* this task.
|
|
1344
|
+
*/
|
|
1061
1345
|
declare function formatIssues(errors: ValidationIssue[]): string;
|
|
1062
1346
|
/** Render a single slide to standalone SVG markup (preview / self-check). */
|
|
1063
1347
|
declare function renderSlideSvg(ir: PptxIR, slideIndex: number): string;
|
|
1064
1348
|
/** Full pipeline: validate → SVG → DrawingML → animation patches → pptx bytes. */
|
|
1065
|
-
declare function generatePptx(input: unknown
|
|
1349
|
+
declare function generatePptx(input: unknown, opts?: {
|
|
1350
|
+
draft?: boolean;
|
|
1351
|
+
}): Promise<Uint8Array>;
|
|
1066
1352
|
interface ThemeInfo {
|
|
1067
1353
|
id: string;
|
|
1068
1354
|
label: string;
|
|
1069
1355
|
colors: Record<string, unknown>;
|
|
1070
1356
|
}
|
|
1071
|
-
/** Built-in theme catalog with labels and color tokens. */
|
|
1357
|
+
/** Built-in theme catalog with labels and style color tokens. */
|
|
1072
1358
|
declare function listThemes(): ThemeInfo[];
|
|
1073
1359
|
/** JSON Schema for the IR — feed this to a model before it writes IR. */
|
|
1074
1360
|
declare function irJsonSchema(): Record<string, unknown>;
|
|
1361
|
+
/** JSON Schema for style-token overrides (IR theme.style, --style files, config "style"). */
|
|
1362
|
+
declare function styleJsonSchema(): Record<string, unknown>;
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* The frozen IR v3 top-level shape (vocabulary-v4 rename, task 1 — spec
|
|
1366
|
+
* §9.3: "v3 已冻结... 顶层字段和枚举改名必须进入新的 IR 版本,不能在 v3
|
|
1367
|
+
* 内静默改变含义"). `./index.ts`'s `PptxIRSchema` is v4 now — this module
|
|
1368
|
+
* exists only so a genuinely v3-shaped document still has somewhere to parse
|
|
1369
|
+
* against: `migrateIrV3ToV4`'s input type (`./migrate.ts`), and the
|
|
1370
|
+
* v3-hard-reject path's own tests (constructing a *valid* v3 IR to prove the
|
|
1371
|
+
* reject fires on version alone, not on some other schema defect).
|
|
1372
|
+
*
|
|
1373
|
+
* `validateIr` (`src/api.ts`) never calls this schema itself — an incoming
|
|
1374
|
+
* v3 document (`version === "3"`) is hard-rejected before any schema parse
|
|
1375
|
+
* runs at all (spec §9.3), full stop. This schema is a migration-tooling and
|
|
1376
|
+
* test fixture, not a second accepted input shape.
|
|
1377
|
+
*
|
|
1378
|
+
* Every field but `version` and `scenario` is byte-identical to `./index.ts`'s
|
|
1379
|
+
* v4 `PptxIRSchema` (spec §9.1: "其余 IR 字段保持不变") — reuses the exact
|
|
1380
|
+
* same `ThemeSchema`/`MetaSchema`/`AssetsSchema`/`BrandSchema`/`SlideSchema`
|
|
1381
|
+
* instances rather than redefining them, so there is no way for this frozen
|
|
1382
|
+
* shape to silently drift from the fields it shares with v4.
|
|
1383
|
+
*/
|
|
1384
|
+
declare const PptxIRV3Schema: z.ZodObject<{
|
|
1385
|
+
version: z.ZodDefault<z.ZodLiteral<"3">>;
|
|
1386
|
+
filename: z.ZodDefault<z.ZodString>;
|
|
1387
|
+
scenario: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
1388
|
+
theme: z.ZodDefault<z.ZodObject<{
|
|
1389
|
+
id: z.ZodDefault<z.ZodString>;
|
|
1390
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
1391
|
+
colors: z.ZodOptional<z.ZodObject<{
|
|
1392
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
1393
|
+
surface: z.ZodOptional<z.ZodString>;
|
|
1394
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
1395
|
+
primary: z.ZodOptional<z.ZodString>;
|
|
1396
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
1397
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1398
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
1399
|
+
border: z.ZodOptional<z.ZodString>;
|
|
1400
|
+
chartPalette: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1401
|
+
accentPool: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1402
|
+
cardStroke: z.ZodOptional<z.ZodString>;
|
|
1403
|
+
}, z.core.$strict>>;
|
|
1404
|
+
fonts: z.ZodOptional<z.ZodObject<{
|
|
1405
|
+
heading: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1406
|
+
body: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1407
|
+
mono: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1408
|
+
}, z.core.$strict>>;
|
|
1409
|
+
shape: z.ZodOptional<z.ZodObject<{
|
|
1410
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
1411
|
+
gapScale: z.ZodOptional<z.ZodNumber>;
|
|
1412
|
+
}, z.core.$strict>>;
|
|
1413
|
+
}, z.core.$strict>>;
|
|
1414
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
1415
|
+
suppressFooterOnCardContent: z.ZodOptional<z.ZodBoolean>;
|
|
1416
|
+
suppressFooterRule: z.ZodOptional<z.ZodBoolean>;
|
|
1417
|
+
}, z.core.$strict>>;
|
|
1418
|
+
}, z.core.$strict>>;
|
|
1419
|
+
meta: z.ZodDefault<z.ZodObject<{
|
|
1420
|
+
organization: z.ZodOptional<z.ZodString>;
|
|
1421
|
+
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1422
|
+
name: z.ZodString;
|
|
1423
|
+
role: z.ZodOptional<z.ZodString>;
|
|
1424
|
+
org: z.ZodOptional<z.ZodString>;
|
|
1425
|
+
}, z.core.$strict>>>;
|
|
1426
|
+
date: z.ZodOptional<z.ZodString>;
|
|
1427
|
+
version: z.ZodOptional<z.ZodString>;
|
|
1428
|
+
confidentiality: z.ZodOptional<z.ZodEnum<{
|
|
1429
|
+
public: "public";
|
|
1430
|
+
internal: "internal";
|
|
1431
|
+
confidential: "confidential";
|
|
1432
|
+
restricted: "restricted";
|
|
1433
|
+
}>>;
|
|
1434
|
+
contact: z.ZodOptional<z.ZodObject<{
|
|
1435
|
+
name: z.ZodOptional<z.ZodString>;
|
|
1436
|
+
email: z.ZodOptional<z.ZodString>;
|
|
1437
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
1438
|
+
website: z.ZodOptional<z.ZodString>;
|
|
1439
|
+
}, z.core.$strict>>;
|
|
1440
|
+
copyright: z.ZodOptional<z.ZodString>;
|
|
1441
|
+
animation: z.ZodOptional<z.ZodObject<{
|
|
1442
|
+
transition: z.ZodOptional<z.ZodEnum<{
|
|
1443
|
+
fade: "fade";
|
|
1444
|
+
push: "push";
|
|
1445
|
+
wipe: "wipe";
|
|
1446
|
+
none: "none";
|
|
1447
|
+
}>>;
|
|
1448
|
+
elements: z.ZodOptional<z.ZodEnum<{
|
|
1449
|
+
none: "none";
|
|
1450
|
+
auto: "auto";
|
|
1451
|
+
}>>;
|
|
1452
|
+
}, z.core.$strict>>;
|
|
1453
|
+
}, z.core.$strict>>;
|
|
1454
|
+
assets: z.ZodDefault<z.ZodObject<{
|
|
1455
|
+
images: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1456
|
+
src: z.ZodString;
|
|
1457
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
1458
|
+
error: z.ZodOptional<z.ZodString>;
|
|
1459
|
+
}, z.core.$strict>>>;
|
|
1460
|
+
}, z.core.$strict>>;
|
|
1461
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
1462
|
+
logo_asset_id: z.ZodOptional<z.ZodString>;
|
|
1463
|
+
position: z.ZodOptional<z.ZodEnum<{
|
|
1464
|
+
tl: "tl";
|
|
1465
|
+
tr: "tr";
|
|
1466
|
+
bl: "bl";
|
|
1467
|
+
br: "br";
|
|
1468
|
+
}>>;
|
|
1469
|
+
}, z.core.$strict>>;
|
|
1470
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
1471
|
+
slides: z.ZodArray<z.ZodObject<{
|
|
1472
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
1473
|
+
cover: "cover";
|
|
1474
|
+
chapter: "chapter";
|
|
1475
|
+
content: "content";
|
|
1476
|
+
ending: "ending";
|
|
1477
|
+
}>>;
|
|
1478
|
+
id: z.ZodOptional<z.ZodString>;
|
|
1479
|
+
placeholder: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1480
|
+
layout: z.ZodOptional<z.ZodString>;
|
|
1481
|
+
arrangement: z.ZodOptional<z.ZodEnum<{
|
|
1482
|
+
code: "code";
|
|
1483
|
+
quote: "quote";
|
|
1484
|
+
single: "single";
|
|
1485
|
+
two_column: "two_column";
|
|
1486
|
+
kpi_focus: "kpi_focus";
|
|
1487
|
+
image_focus: "image_focus";
|
|
1488
|
+
big_number: "big_number";
|
|
1489
|
+
assertion_evidence: "assertion_evidence";
|
|
1490
|
+
aside: "aside";
|
|
1491
|
+
}>>;
|
|
1492
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
1493
|
+
subheading: z.ZodOptional<z.ZodString>;
|
|
1494
|
+
components: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1495
|
+
type: z.ZodLiteral<"bullets">;
|
|
1496
|
+
items: z.ZodArray<z.ZodString>;
|
|
1497
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1498
|
+
default: "default";
|
|
1499
|
+
checklist: "checklist";
|
|
1500
|
+
numbered: "numbered";
|
|
1501
|
+
plain: "plain";
|
|
1502
|
+
divided: "divided";
|
|
1503
|
+
}>>;
|
|
1504
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1505
|
+
type: z.ZodLiteral<"paragraph">;
|
|
1506
|
+
text: z.ZodString;
|
|
1507
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1508
|
+
type: z.ZodLiteral<"quote">;
|
|
1509
|
+
text: z.ZodString;
|
|
1510
|
+
attribution: z.ZodOptional<z.ZodString>;
|
|
1511
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1512
|
+
type: z.ZodLiteral<"callout">;
|
|
1513
|
+
variant: z.ZodEnum<{
|
|
1514
|
+
info: "info";
|
|
1515
|
+
warn: "warn";
|
|
1516
|
+
tip: "tip";
|
|
1517
|
+
}>;
|
|
1518
|
+
text: z.ZodString;
|
|
1519
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1520
|
+
[x: string]: string;
|
|
1521
|
+
}>>;
|
|
1522
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1523
|
+
type: z.ZodLiteral<"code">;
|
|
1524
|
+
language: z.ZodString;
|
|
1525
|
+
code: z.ZodString;
|
|
1526
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1527
|
+
type: z.ZodLiteral<"kpi_cards">;
|
|
1528
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1529
|
+
value: z.ZodString;
|
|
1530
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
1531
|
+
label: z.ZodString;
|
|
1532
|
+
delta: z.ZodOptional<z.ZodEnum<{
|
|
1533
|
+
up: "up";
|
|
1534
|
+
down: "down";
|
|
1535
|
+
flat: "flat";
|
|
1536
|
+
}>>;
|
|
1537
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1538
|
+
[x: string]: string;
|
|
1539
|
+
}>>;
|
|
1540
|
+
source: z.ZodOptional<z.ZodString>;
|
|
1541
|
+
}, z.core.$strict>>;
|
|
1542
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1543
|
+
type: z.ZodLiteral<"chart">;
|
|
1544
|
+
chart_type: z.ZodEnum<{
|
|
1545
|
+
line: "line";
|
|
1546
|
+
dumbbell: "dumbbell";
|
|
1547
|
+
funnel: "funnel";
|
|
1548
|
+
bar: "bar";
|
|
1549
|
+
pie: "pie";
|
|
1550
|
+
}>;
|
|
1551
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1552
|
+
horizontal: "horizontal";
|
|
1553
|
+
vertical: "vertical";
|
|
1554
|
+
}>>;
|
|
1555
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1556
|
+
donut: "donut";
|
|
1557
|
+
}>>;
|
|
1558
|
+
axes: z.ZodOptional<z.ZodObject<{
|
|
1559
|
+
x_title: z.ZodOptional<z.ZodString>;
|
|
1560
|
+
y_title: z.ZodOptional<z.ZodString>;
|
|
1561
|
+
show_grid: z.ZodOptional<z.ZodBoolean>;
|
|
1562
|
+
}, z.core.$strict>>;
|
|
1563
|
+
series: z.ZodArray<z.ZodObject<{
|
|
1564
|
+
name: z.ZodString;
|
|
1565
|
+
data: z.ZodArray<z.ZodObject<{
|
|
1566
|
+
x: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
1567
|
+
y: z.ZodNumber;
|
|
1568
|
+
}, z.core.$strict>>;
|
|
1569
|
+
}, z.core.$strict>>;
|
|
1570
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1571
|
+
type: z.ZodLiteral<"flowchart">;
|
|
1572
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
1573
|
+
id: z.ZodString;
|
|
1574
|
+
label: z.ZodString;
|
|
1575
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1576
|
+
rect: "rect";
|
|
1577
|
+
diamond: "diamond";
|
|
1578
|
+
round: "round";
|
|
1579
|
+
}>>;
|
|
1580
|
+
}, z.core.$strict>>;
|
|
1581
|
+
edges: z.ZodArray<z.ZodObject<{
|
|
1582
|
+
from: z.ZodString;
|
|
1583
|
+
to: z.ZodString;
|
|
1584
|
+
label: z.ZodOptional<z.ZodString>;
|
|
1585
|
+
}, z.core.$strict>>;
|
|
1586
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1587
|
+
TB: "TB";
|
|
1588
|
+
TD: "TD";
|
|
1589
|
+
BT: "BT";
|
|
1590
|
+
LR: "LR";
|
|
1591
|
+
RL: "RL";
|
|
1592
|
+
}>>;
|
|
1593
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1594
|
+
type: z.ZodLiteral<"architecture">;
|
|
1595
|
+
layers: z.ZodArray<z.ZodObject<{
|
|
1596
|
+
title: z.ZodString;
|
|
1597
|
+
items: z.ZodArray<z.ZodString>;
|
|
1598
|
+
}, z.core.$strict>>;
|
|
1599
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1600
|
+
type: z.ZodLiteral<"timeline">;
|
|
1601
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
1602
|
+
horizontal: "horizontal";
|
|
1603
|
+
vertical: "vertical";
|
|
1604
|
+
}>>;
|
|
1605
|
+
milestones: z.ZodArray<z.ZodObject<{
|
|
1606
|
+
date: z.ZodString;
|
|
1607
|
+
title: z.ZodString;
|
|
1608
|
+
desc: z.ZodOptional<z.ZodString>;
|
|
1609
|
+
highlight: z.ZodOptional<z.ZodBoolean>;
|
|
1610
|
+
}, z.core.$strict>>;
|
|
1611
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1612
|
+
type: z.ZodLiteral<"comparison">;
|
|
1613
|
+
columns: z.ZodArray<z.ZodString>;
|
|
1614
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
1615
|
+
label: z.ZodString;
|
|
1616
|
+
cells: z.ZodArray<z.ZodString>;
|
|
1617
|
+
}, z.core.$strict>>;
|
|
1618
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1619
|
+
type: z.ZodLiteral<"icon_cards">;
|
|
1620
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1621
|
+
icon: z.ZodEnum<{
|
|
1622
|
+
[x: string]: string;
|
|
1623
|
+
}>;
|
|
1624
|
+
title: z.ZodString;
|
|
1625
|
+
text: z.ZodString;
|
|
1626
|
+
}, z.core.$strict>>;
|
|
1627
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1628
|
+
type: z.ZodLiteral<"row_cards">;
|
|
1629
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1630
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1631
|
+
[x: string]: string;
|
|
1632
|
+
}>>;
|
|
1633
|
+
title: z.ZodString;
|
|
1634
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1635
|
+
sub: z.ZodOptional<z.ZodString>;
|
|
1636
|
+
highlight: z.ZodOptional<z.ZodBoolean>;
|
|
1637
|
+
}, z.core.$strict>>;
|
|
1638
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1639
|
+
type: z.ZodLiteral<"steps">;
|
|
1640
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1641
|
+
title: z.ZodString;
|
|
1642
|
+
text: z.ZodString;
|
|
1643
|
+
}, z.core.$strict>>;
|
|
1644
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1645
|
+
type: z.ZodLiteral<"rings">;
|
|
1646
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1647
|
+
label: z.ZodString;
|
|
1648
|
+
desc: z.ZodOptional<z.ZodString>;
|
|
1649
|
+
}, z.core.$strict>>;
|
|
1650
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1651
|
+
type: z.ZodLiteral<"numbered_cards">;
|
|
1652
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1653
|
+
title: z.ZodString;
|
|
1654
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1655
|
+
sub: z.ZodOptional<z.ZodString>;
|
|
1656
|
+
}, z.core.$strict>>;
|
|
1657
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1658
|
+
type: z.ZodLiteral<"roadmap">;
|
|
1659
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1660
|
+
title: z.ZodString;
|
|
1661
|
+
period: z.ZodOptional<z.ZodString>;
|
|
1662
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1663
|
+
label: z.ZodString;
|
|
1664
|
+
value: z.ZodString;
|
|
1665
|
+
}, z.core.$strict>>>;
|
|
1666
|
+
}, z.core.$strict>>;
|
|
1667
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1668
|
+
type: z.ZodLiteral<"matrix">;
|
|
1669
|
+
x_title: z.ZodOptional<z.ZodString>;
|
|
1670
|
+
y_title: z.ZodOptional<z.ZodString>;
|
|
1671
|
+
cols: z.ZodNumber;
|
|
1672
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1673
|
+
title: z.ZodString;
|
|
1674
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
1675
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
1676
|
+
info: "info";
|
|
1677
|
+
accent: "accent";
|
|
1678
|
+
neutral: "neutral";
|
|
1679
|
+
}>>;
|
|
1680
|
+
}, z.core.$strict>>;
|
|
1681
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1682
|
+
type: z.ZodLiteral<"insight_panel">;
|
|
1683
|
+
title: z.ZodString;
|
|
1684
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
1685
|
+
label: z.ZodString;
|
|
1686
|
+
text: z.ZodString;
|
|
1687
|
+
}, z.core.$strict>>;
|
|
1688
|
+
footnote: z.ZodOptional<z.ZodString>;
|
|
1689
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1690
|
+
type: z.ZodLiteral<"verdict_banner">;
|
|
1691
|
+
text: z.ZodString;
|
|
1692
|
+
tone: z.ZodEnum<{
|
|
1693
|
+
neutral: "neutral";
|
|
1694
|
+
positive: "positive";
|
|
1695
|
+
warning: "warning";
|
|
1696
|
+
}>;
|
|
1697
|
+
icon: z.ZodOptional<z.ZodEnum<{
|
|
1698
|
+
[x: string]: string;
|
|
1699
|
+
}>>;
|
|
1700
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1701
|
+
type: z.ZodLiteral<"citation">;
|
|
1702
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
1703
|
+
label: z.ZodString;
|
|
1704
|
+
url: z.ZodOptional<z.ZodString>;
|
|
1705
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
1706
|
+
}, z.core.$strict>>;
|
|
1707
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1708
|
+
type: z.ZodLiteral<"image">;
|
|
1709
|
+
asset_id: z.ZodString;
|
|
1710
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
1711
|
+
fit: z.ZodDefault<z.ZodEnum<{
|
|
1712
|
+
cover: "cover";
|
|
1713
|
+
contain: "contain";
|
|
1714
|
+
}>>;
|
|
1715
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1716
|
+
type: z.ZodLiteral<"image_grid">;
|
|
1717
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1718
|
+
asset_id: z.ZodString;
|
|
1719
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
1720
|
+
}, z.core.$strict>>;
|
|
1721
|
+
emphasis: z.ZodOptional<z.ZodEnum<{
|
|
1722
|
+
none: "none";
|
|
1723
|
+
first: "first";
|
|
1724
|
+
}>>;
|
|
1725
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1726
|
+
type: z.ZodLiteral<"image_compare">;
|
|
1727
|
+
left: z.ZodObject<{
|
|
1728
|
+
asset_id: z.ZodString;
|
|
1729
|
+
label: z.ZodString;
|
|
1730
|
+
}, z.core.$strict>;
|
|
1731
|
+
right: z.ZodObject<{
|
|
1732
|
+
asset_id: z.ZodString;
|
|
1733
|
+
label: z.ZodString;
|
|
1734
|
+
}, z.core.$strict>;
|
|
1735
|
+
style: z.ZodOptional<z.ZodEnum<{
|
|
1736
|
+
vs: "vs";
|
|
1737
|
+
before_after: "before_after";
|
|
1738
|
+
}>>;
|
|
1739
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1740
|
+
type: z.ZodLiteral<"swot">;
|
|
1741
|
+
strengths: z.ZodArray<z.ZodString>;
|
|
1742
|
+
weaknesses: z.ZodArray<z.ZodString>;
|
|
1743
|
+
opportunities: z.ZodArray<z.ZodString>;
|
|
1744
|
+
threats: z.ZodArray<z.ZodString>;
|
|
1745
|
+
labels: z.ZodOptional<z.ZodObject<{
|
|
1746
|
+
strengths: z.ZodOptional<z.ZodString>;
|
|
1747
|
+
weaknesses: z.ZodOptional<z.ZodString>;
|
|
1748
|
+
opportunities: z.ZodOptional<z.ZodString>;
|
|
1749
|
+
threats: z.ZodOptional<z.ZodString>;
|
|
1750
|
+
}, z.core.$strict>>;
|
|
1751
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1752
|
+
type: z.ZodLiteral<"bmc">;
|
|
1753
|
+
key_partners: z.ZodArray<z.ZodString>;
|
|
1754
|
+
key_activities: z.ZodArray<z.ZodString>;
|
|
1755
|
+
key_resources: z.ZodArray<z.ZodString>;
|
|
1756
|
+
value_propositions: z.ZodArray<z.ZodString>;
|
|
1757
|
+
customer_relationships: z.ZodArray<z.ZodString>;
|
|
1758
|
+
channels: z.ZodArray<z.ZodString>;
|
|
1759
|
+
customer_segments: z.ZodArray<z.ZodString>;
|
|
1760
|
+
cost_structure: z.ZodArray<z.ZodString>;
|
|
1761
|
+
revenue_streams: z.ZodArray<z.ZodString>;
|
|
1762
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1763
|
+
type: z.ZodLiteral<"waterfall">;
|
|
1764
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1765
|
+
label: z.ZodString;
|
|
1766
|
+
value: z.ZodNumber;
|
|
1767
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
1768
|
+
delta: "delta";
|
|
1769
|
+
total: "total";
|
|
1770
|
+
}>>;
|
|
1771
|
+
}, z.core.$strict>>;
|
|
1772
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
1773
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1774
|
+
type: z.ZodLiteral<"gantt">;
|
|
1775
|
+
items: z.ZodArray<z.ZodObject<{
|
|
1776
|
+
label: z.ZodString;
|
|
1777
|
+
start: z.ZodNumber;
|
|
1778
|
+
end: z.ZodNumber;
|
|
1779
|
+
}, z.core.$strict>>;
|
|
1780
|
+
axis_labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1781
|
+
}, z.core.$strict>], "type">>>;
|
|
1782
|
+
background: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1783
|
+
kind: z.ZodLiteral<"color">;
|
|
1784
|
+
value: z.ZodString;
|
|
1785
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1786
|
+
kind: z.ZodLiteral<"gradient">;
|
|
1787
|
+
from: z.ZodString;
|
|
1788
|
+
to: z.ZodString;
|
|
1789
|
+
direction: z.ZodOptional<z.ZodEnum<{
|
|
1790
|
+
tb: "tb";
|
|
1791
|
+
lr: "lr";
|
|
1792
|
+
diagonal: "diagonal";
|
|
1793
|
+
}>>;
|
|
1794
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1795
|
+
kind: z.ZodLiteral<"asset">;
|
|
1796
|
+
asset_id: z.ZodString;
|
|
1797
|
+
overlay: z.ZodOptional<z.ZodObject<{
|
|
1798
|
+
color: z.ZodString;
|
|
1799
|
+
opacity: z.ZodNumber;
|
|
1800
|
+
}, z.core.$strict>>;
|
|
1801
|
+
fit: z.ZodOptional<z.ZodEnum<{
|
|
1802
|
+
cover: "cover";
|
|
1803
|
+
contain: "contain";
|
|
1804
|
+
}>>;
|
|
1805
|
+
}, z.core.$strict>], "kind">>;
|
|
1806
|
+
decor: z.ZodOptional<z.ZodObject<{
|
|
1807
|
+
kind: z.ZodEnum<{
|
|
1808
|
+
big_number: "big_number";
|
|
1809
|
+
corner_tag: "corner_tag";
|
|
1810
|
+
rule_line: "rule_line";
|
|
1811
|
+
quote_marks: "quote_marks";
|
|
1812
|
+
geo_dots: "geo_dots";
|
|
1813
|
+
}>;
|
|
1814
|
+
intensity: z.ZodOptional<z.ZodEnum<{
|
|
1815
|
+
subtle: "subtle";
|
|
1816
|
+
normal: "normal";
|
|
1817
|
+
}>>;
|
|
1818
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1819
|
+
}, z.core.$strict>>;
|
|
1820
|
+
image_side: z.ZodOptional<z.ZodEnum<{
|
|
1821
|
+
left: "left";
|
|
1822
|
+
right: "right";
|
|
1823
|
+
}>>;
|
|
1824
|
+
footnote: z.ZodOptional<z.ZodString>;
|
|
1825
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
1826
|
+
}, z.core.$strict>>;
|
|
1827
|
+
}, z.core.$strict>;
|
|
1828
|
+
type PptxIRV3 = z.infer<typeof PptxIRV3Schema>;
|
|
1829
|
+
|
|
1830
|
+
/**
|
|
1831
|
+
* Deterministic, pure IR v3 → v4 migration (spec §9.1). Field-for-field,
|
|
1832
|
+
* value-for-value per the mapping in {@link migrateNarrativeInput}'s
|
|
1833
|
+
* docstring — every field this function doesn't touch (`filename`, `theme`,
|
|
1834
|
+
* `meta`, `assets`, `brand`, `seed`, `slides`) carries across by the exact
|
|
1835
|
+
* same reference it came in with, unchanged (spec §9.1: "其余 IR 字段保持不
|
|
1836
|
+
* 变"; spec §10: no weight/budget/selection/render change is in scope for
|
|
1837
|
+
* this migration, ever).
|
|
1838
|
+
*
|
|
1839
|
+
* Exported from the SDK surface (`src/index.ts`) as the deterministic
|
|
1840
|
+
* migration primitive the `pptfast migrate` CLI command (task 2) wraps —
|
|
1841
|
+
* this function itself does no I/O and never runs a model, per spec §9.3's
|
|
1842
|
+
* "只做已声明的结构映射,不运行模型,不重写内容,不重新选择 layout".
|
|
1843
|
+
*
|
|
1844
|
+
* Takes an already-parsed `PptxIRV3` (i.e. `PptxIRV3Schema.parse(...)`'s
|
|
1845
|
+
* output, defaults already applied) rather than raw `unknown` JSON — schema
|
|
1846
|
+
* validation of the v3 input is the caller's job (the CLI parses-then-
|
|
1847
|
+
* migrates; `validateIr`'s own v3 path hard-rejects before ever reaching
|
|
1848
|
+
* this function, spec §9.3, so `validateIr` itself never calls this).
|
|
1849
|
+
*/
|
|
1850
|
+
declare function migrateIrV3ToV4(v3: PptxIRV3): PptxIR;
|
|
1851
|
+
|
|
1852
|
+
type LayoutType = "cover" | "chapter" | "content" | "ending";
|
|
1853
|
+
interface StyleColors {
|
|
1854
|
+
bg: string;
|
|
1855
|
+
surface: string;
|
|
1856
|
+
panel?: string;
|
|
1857
|
+
primary: string;
|
|
1858
|
+
accent: string;
|
|
1859
|
+
text: string;
|
|
1860
|
+
muted: string;
|
|
1861
|
+
border?: string;
|
|
1862
|
+
chartPalette: string[];
|
|
1863
|
+
/**
|
|
1864
|
+
* Optional accent color pool for themes whose layout grammar cycles through
|
|
1865
|
+
* multiple accent colors (e.g. tech card corner stripes). Themes that
|
|
1866
|
+
* only use a single accent color can omit this.
|
|
1867
|
+
*/
|
|
1868
|
+
accentPool?: string[];
|
|
1869
|
+
/**
|
|
1870
|
+
* Optional hairline stroke color for the shared "surface card" shells
|
|
1871
|
+
* (kpi_cards / icon_cards / callout — see components/kpi.tsx,
|
|
1872
|
+
* components/icon-cards.tsx, components/callout.tsx). Those component renderers are
|
|
1873
|
+
* shared across every theme and must stay theme-agnostic (no
|
|
1874
|
+
* `if (themeId === ...)` branching), so a theme that wants its cards
|
|
1875
|
+
* outlined sets this token instead — the renderers just consult it and add
|
|
1876
|
+
* a 1px stroke when it's defined. Omitted (`undefined`, the default) draws
|
|
1877
|
+
* no stroke, so every theme that doesn't set it keeps its current
|
|
1878
|
+
* fill-only card unchanged.
|
|
1879
|
+
*/
|
|
1880
|
+
cardStroke?: string;
|
|
1881
|
+
}
|
|
1882
|
+
interface StyleFonts {
|
|
1883
|
+
heading: string[];
|
|
1884
|
+
body: string[];
|
|
1885
|
+
mono?: string[];
|
|
1886
|
+
}
|
|
1887
|
+
interface StyleTokens {
|
|
1888
|
+
id: string;
|
|
1889
|
+
allowCustomBackground?: boolean;
|
|
1890
|
+
colors: StyleColors;
|
|
1891
|
+
fonts: StyleFonts;
|
|
1892
|
+
shape?: StyleShape;
|
|
1893
|
+
defaultBackgrounds: Record<LayoutType, BackgroundSpec>;
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* 主题细节 shape token(2026-07-10 用户立项,spec:
|
|
1897
|
+
* .issues/specs/2026-07-10-pptx-theme-detail-tokens-design.md)。
|
|
1898
|
+
* 全部可选——缺省时各消费点沿用自己的 baked 值(零观感)。
|
|
1899
|
+
*/
|
|
1900
|
+
interface StyleShape {
|
|
1901
|
+
/** 卡片/横幅圆角 px(统一值)。缺省=各消费点原 baked 值(kpi 8/architecture 6/白卡 14 等)。 */
|
|
1902
|
+
radius?: number;
|
|
1903
|
+
/** 块间距缩放(1=现 BLOCK_GAP)。建议范围 [0.8, 1.3]。 */
|
|
1904
|
+
gapScale?: number;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
type MotifArchetypeId = "banner-motif" | "rail-motif" | "poster-motif" | "constellation-motif" | "corner-ornament-motif" | "tone-adaptive-motif" | "campaign-motif" | "bloom-motif" | "classroom-motif" | "ink-motif" | "luxe-motif" | "enterprise-motif" | "heritage-motif";
|
|
1908
|
+
|
|
1909
|
+
/**
|
|
1910
|
+
* A theme = distributable bundle: `style` (style tokens) + `brand` (brand
|
|
1911
|
+
* chrome) + affinity tags (filled in W4).
|
|
1912
|
+
*
|
|
1913
|
+
* `id` is a plain `string`, not `CanonicalThemeId` — the 13 builtins satisfy
|
|
1914
|
+
* this (`CanonicalThemeId` is a subtype of `string`), but `registerTheme`
|
|
1915
|
+
* below (W3 task 4's SDK registration seam) must also accept ids outside that
|
|
1916
|
+
* closed union.
|
|
1917
|
+
*/
|
|
1918
|
+
interface ThemeDefinition {
|
|
1919
|
+
id: string;
|
|
1920
|
+
style: StyleTokens;
|
|
1921
|
+
brand: BrandConfig;
|
|
1922
|
+
tags: readonly string[];
|
|
1923
|
+
/**
|
|
1924
|
+
* 主题的「选择权」配置(spec §3 theme.layouts 命名裁决;W2 任务 2 由
|
|
1925
|
+
* src/themes/manifest.ts〔已删除〕的旧选择权类型原地迁居于此)——四页型
|
|
1926
|
+
* 各自允许哪些 archetype 参与自动选型。排印/色彩在 style,这里只放集合。
|
|
1927
|
+
* **W4 全集放开**(spec §3「缺省 = 全集,策展收窄塑造个性」,design
|
|
1928
|
+
* decision 7):十三内置主题四页型默认均为 {@link fullArchetypeSet} 的
|
|
1929
|
+
* 全集。design decision 7/8 曾经的六处对比度策展排除(luxe/campaign/
|
|
1930
|
+
* classroom 的 content 排除 banner-heading、tech 的 cover/content、
|
|
1931
|
+
* consulting 的 chapter)已在 W4 fix round 随对比度自适应 ink helper
|
|
1932
|
+
* (`src/svg/ink.ts`)的根因修复全部撤销。fix round 自身新发现的三处
|
|
1933
|
+
* (bloom/classroom/heritage 的 chapter 排除 fashion-chapter)也已在
|
|
1934
|
+
* post-v0.3 W8 fix round 随 `readableOn` 两墨实测对比度取优的根因修复一并
|
|
1935
|
+
* 撤销(backlog item 2)——十三主题四页型现在均为不折不扣的全集,无任何
|
|
1936
|
+
* 排除残留。页型空集 = 该页型回落调用侧兜底(十三主题四页型均非空,
|
|
1937
|
+
* `definitions.test.ts` 锁死)。id 是通用 string(不再按页型区分
|
|
1938
|
+
* archetype id 联合类型)。
|
|
1939
|
+
*/
|
|
1940
|
+
layouts: Record<Slide["type"], readonly string[]>;
|
|
1941
|
+
/** Motif:单值,非 allowed-set(spec §3 示意)。undefined = 该主题无 motif 装饰(十三主题中 runway 留空,其余均已设)。 */
|
|
1942
|
+
motif?: MotifArchetypeId;
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* `registerTheme`'s input shape (W4, spec §3 "缺省 = 全集"): identical to
|
|
1946
|
+
* {@link ThemeDefinition} except `layouts` is optional, and — when present —
|
|
1947
|
+
* each of its four slide-type entries is independently optional too. A
|
|
1948
|
+
* slide type this theme doesn't narrow (its own key omitted, or the whole
|
|
1949
|
+
* `layouts` object omitted) defaults to that type's full registered-
|
|
1950
|
+
* archetype set ({@link FULL_LAYOUTS}) — the exact same default every
|
|
1951
|
+
* builtin theme in `LAYOUTS` above resolves to for a slide type it doesn't
|
|
1952
|
+
* curate away from. `getThemeDefinition`/`REGISTERED_THEMES` still only ever
|
|
1953
|
+
* hold the fully-resolved `ThemeDefinition` shape (`layouts` total over all
|
|
1954
|
+
* four types) — `registerTheme` performs the defaulting once, here, so
|
|
1955
|
+
* every downstream reader (`resolveArchetypeId` foremost) can keep assuming
|
|
1956
|
+
* a total record and never re-derive "was this slide type curated or
|
|
1957
|
+
* defaulted".
|
|
1958
|
+
*/
|
|
1959
|
+
type ThemeRegistration = Omit<ThemeDefinition, "layouts"> & {
|
|
1960
|
+
layouts?: Partial<Record<Slide["type"], readonly string[]>>;
|
|
1961
|
+
};
|
|
1962
|
+
/**
|
|
1963
|
+
* Register a theme at runtime (SDK seam, not the v0.4 distribution
|
|
1964
|
+
* protocol). Validates just enough to keep the render chain from silently
|
|
1965
|
+
* breaking on a malformed registration — not a full schema:
|
|
1966
|
+
*
|
|
1967
|
+
* - `id` must not collide with a builtin or an already-registered theme.
|
|
1968
|
+
* - each of the four slide types, once defaulted ({@link ThemeRegistration}),
|
|
1969
|
+
* must have at least one layout id that is both registered in
|
|
1970
|
+
* `LAYOUT_REGISTRY` and valid for that slide type (the same registry
|
|
1971
|
+
* `resolveArchetypeId`/`FullSlideSvg` select from — a theme never ships
|
|
1972
|
+
* new render code, only a curated subset of the existing 30 archetypes +
|
|
1973
|
+
* 4 takeovers, per `docs/architecture.md`'s "Adding a theme" section). An
|
|
1974
|
+
* *explicit* empty array for a slide type still fails this check (the
|
|
1975
|
+
* default only kicks in when the key — or `layouts` itself — is omitted
|
|
1976
|
+
* entirely, `undefined`, never for a caller-supplied `[]`).
|
|
1977
|
+
* - `style` must be present (a JS caller can bypass the TS type).
|
|
1978
|
+
*
|
|
1979
|
+
* Once registered, the theme participates in `getInstalledThemeIds`,
|
|
1980
|
+
* `getThemeDefinition` (hence `effective-layout.ts`/`FullSlideSvg`'s
|
|
1981
|
+
* selection and `resolveBrand`), and `themes/index.ts`'s `resolveStyle` —
|
|
1982
|
+
* every internal theme lookup, with no separate "registered theme" branch
|
|
1983
|
+
* for callers to remember.
|
|
1984
|
+
*/
|
|
1985
|
+
declare function registerTheme(def: ThemeRegistration): void;
|
|
1986
|
+
/** Every installed theme id: the 13 builtins, then registered themes in registration order. */
|
|
1987
|
+
declare function getInstalledThemeIds(): readonly string[];
|
|
1988
|
+
/**
|
|
1989
|
+
* Resolve a theme id to its full definition — a registered theme first, then
|
|
1990
|
+
* the builtin fallback (`THEME_DEFINITIONS[resolveThemeId(id)]`, which itself
|
|
1991
|
+
* folds an unrecognized id to consulting). The one lookup every internal
|
|
1992
|
+
* consumer that used to read `THEME_DEFINITIONS[resolveThemeId(id)]`
|
|
1993
|
+
* directly (`effective-layout.ts`, `FullSlideSvg.tsx`) now calls instead, so
|
|
1994
|
+
* a registered theme's curated layouts actually drive selection end-to-end.
|
|
1995
|
+
*/
|
|
1996
|
+
declare function getThemeDefinition(id: string): ThemeDefinition;
|
|
1997
|
+
|
|
1998
|
+
declare const STRATEGY_VALUES: readonly ["pyramid", "storytelling", "instructional", "showcase", "briefing"];
|
|
1999
|
+
declare const PACING_VALUES: readonly ["dense", "balanced", "spacious"];
|
|
2000
|
+
declare const AUDIENCE_VALUES: readonly ["executive", "technical", "customer", "public"];
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* Narrative argument style (spec §5's five-way strategy classification,
|
|
2004
|
+
* renamed from "mode" — spec §8.1). Each strategy carries a
|
|
2005
|
+
* component/layout tendency set (soft-weight material, consumed by W4's
|
|
2006
|
+
* weighted selection — nothing consumes it this wave) and a beat policy
|
|
2007
|
+
* (renamed from "rhythm policy" — spec §2.3's deck/page beat split; consumed
|
|
2008
|
+
* by W5's spec-validate rotation gate).
|
|
2009
|
+
*/
|
|
2010
|
+
type Strategy = (typeof STRATEGY_VALUES)[number];
|
|
2011
|
+
/**
|
|
2012
|
+
* Content density budget + typographic baseline (spec §5 pacing table,
|
|
2013
|
+
* renamed from "delivery" — spec §8.1). See {@link PacingBudget} for the
|
|
2014
|
+
* dual-attribute capacity split this axis is one half of.
|
|
2015
|
+
*/
|
|
2016
|
+
type Pacing = (typeof PACING_VALUES)[number];
|
|
2017
|
+
/**
|
|
2018
|
+
* Tone anchor only (spec §5: audience is tone-anchoring only, no rendering
|
|
2019
|
+
* effect on the IR yet). Reserved for a future lint pass (e.g. executive ×
|
|
2020
|
+
* long paragraphs → suggest kpi_cards/verdict_banner instead). The rule set
|
|
2021
|
+
* itself is explicitly out of scope this wave (spec §10 open questions).
|
|
2022
|
+
* Name unchanged by the vocabulary-v4 rename (spec §4.3: "audience 保持不变").
|
|
2023
|
+
*/
|
|
2024
|
+
type Audience = (typeof AUDIENCE_VALUES)[number];
|
|
2025
|
+
interface NarrativeProfile {
|
|
2026
|
+
readonly strategy: Strategy;
|
|
2027
|
+
readonly pacing: Pacing;
|
|
2028
|
+
readonly audience: Audience;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
interface StrategyDefinition {
|
|
2032
|
+
id: Strategy;
|
|
2033
|
+
/**
|
|
2034
|
+
* Layout/component tendency set (soft-weight material). Filled row-for-row
|
|
2035
|
+
* from spec §5's strategy table for W4's weighted selection step (spec §6
|
|
2036
|
+
* step 4: in-set candidates get ×3 weight, out-of-set ×1 floor — not
|
|
2037
|
+
* implemented yet, this module only stores the data).
|
|
2038
|
+
*
|
|
2039
|
+
* The set deliberately mixes two different vocabularies: component `type`
|
|
2040
|
+
* names (e.g. "kpi_cards", "chart" — see the `Component` discriminated
|
|
2041
|
+
* union in `ir/index.ts`) and layout `id`s (e.g. "image-split" — see
|
|
2042
|
+
* `LAYOUT_REGISTRY` in `svg/layouts/registry.ts`). This is intentional,
|
|
2043
|
+
* not an oversight: W4's weighting step resolves each entry against
|
|
2044
|
+
* whichever vocabulary it belongs to (component types when scoring a
|
|
2045
|
+
* candidate's components, layout ids when scoring the candidate layout
|
|
2046
|
+
* itself). Nothing in this wave (W3) reads this field.
|
|
2047
|
+
*/
|
|
2048
|
+
tendencies: readonly string[];
|
|
2049
|
+
/**
|
|
2050
|
+
* Content-archetype soft-weight set for W4's weighted layout selection
|
|
2051
|
+
* (spec §6 step 4: in-set candidates get `TENDENCY_WEIGHT` (×3), out-of-set
|
|
2052
|
+
* get the `BASE_WEIGHT` floor (×1) — both named constants live in
|
|
2053
|
+
* `svg/effective-layout.ts`, next to `resolveArchetypeId`, the sole
|
|
2054
|
+
* consumer). Deliberately a separate field from {@link tendencies} above,
|
|
2055
|
+
* not a reinterpretation of it: `tendencies` mixes component-type names and
|
|
2056
|
+
* layout ids drawn from spec §5's strategy table verbatim and also feeds W5's
|
|
2057
|
+
* spec `focus` vocabulary gate, so narrowing its meaning here would be a
|
|
2058
|
+
* breaking change to an existing consumer. This field holds only
|
|
2059
|
+
* `LAYOUT_REGISTRY` content-archetype ids (`svg/layouts/registry.ts`'s
|
|
2060
|
+
* `CONTENT_LAYOUTS` keys) — cover/chapter/ending ids never appear in any
|
|
2061
|
+
* strategy's list here, which is exactly why `resolveArchetypeId`'s weighting
|
|
2062
|
+
* is a no-op for those three slide types (spec: "身份页个性来自 theme 不来自
|
|
2063
|
+
* strategy,均匀取样") without needing a slide-type special case — a weight
|
|
2064
|
+
* lookup against an id that can never match falls through to the ×1 floor
|
|
2065
|
+
* for every candidate, uniform by construction. `tone-adaptive-content`
|
|
2066
|
+
* appears in no strategy's list either (spec's "万金油" call-out: it is the
|
|
2067
|
+
* one content archetype meant to read as strategy-neutral, so it always
|
|
2068
|
+
* gets the ×1 floor too).
|
|
2069
|
+
*/
|
|
2070
|
+
layoutTendencies: readonly string[];
|
|
2071
|
+
/**
|
|
2072
|
+
* Beat template descriptor (spec §5's per-strategy beat-default column,
|
|
2073
|
+
* renamed from "rhythm" — spec §2.3), parameterized by strategy for W5's
|
|
2074
|
+
* spec-validate rotation gate — e.g. briefing is exempt from a generic
|
|
2075
|
+
* "three same-beat pages in a row is an error" rule because uniform-dense
|
|
2076
|
+
* *is* briefing's correct default, not a violation of it (spec §5's
|
|
2077
|
+
* spec-gate section calls out that a generic same-beat-streak rule would
|
|
2078
|
+
* reject briefing's own default). Not consumed this wave.
|
|
2079
|
+
*/
|
|
2080
|
+
beatPolicy: "anchor-open" | "alternate" | "repetition-ok" | "anchor-sparse" | "uniform-dense";
|
|
2081
|
+
}
|
|
2082
|
+
declare const STRATEGY_DEFINITIONS: Record<Strategy, StrategyDefinition>;
|
|
2083
|
+
|
|
2084
|
+
interface PacingBudget {
|
|
2085
|
+
/**
|
|
2086
|
+
* Body-text baseline, in px, at 1280×720 slide geometry (spec §5 pacing
|
|
2087
|
+
* table's body-baseline column). Wired into rendering as of W4 task 3
|
|
2088
|
+
* (design decision 9): `src/svg/FullSlideSvg.tsx` resolves
|
|
2089
|
+
* `PACING_BUDGETS[resolveNarrative(ir.narrative).pacing].bodyBaselinePx`
|
|
2090
|
+
* once and passes it into `buildCtx`, which stores it as
|
|
2091
|
+
* `ComponentCtx.bodyFontPx` — the sole font-size input for the
|
|
2092
|
+
* paragraph/bullets/callout trio ("正文" = continuous running text).
|
|
2093
|
+
* Every other component's own bespoke type scale, the heading system, and
|
|
2094
|
+
* quote's fixed 26px attribution line don't read this field. Since
|
|
2095
|
+
* `balanced` (24px) is the narrative default, an omitted-narrative deck
|
|
2096
|
+
* now renders body text at 24px, not the previous fixed 20px — the
|
|
2097
|
+
* one-time, spec-sanctioned snapshot re-pin that landed alongside this
|
|
2098
|
+
* wiring (W4 task 3, unchanged since — vocabulary-v4 only renames this
|
|
2099
|
+
* table, it doesn't touch any of its numbers, spec §10).
|
|
2100
|
+
*/
|
|
2101
|
+
bodyBaselinePx: number;
|
|
2102
|
+
/**
|
|
2103
|
+
* Per-slide editorial budget (component count) — content discipline
|
|
2104
|
+
* ("how many things belong on this slide"), not geometry. Spec §5's
|
|
2105
|
+
* dual-attribute capacity split keeps *physical* capacity ("how many
|
|
2106
|
+
* things fit in this layout's slots") on the layout registry's body-slot
|
|
2107
|
+
* `capacity` metadata (`svg/layouts/registry.ts`). The W3 quality gate
|
|
2108
|
+
* takes `min(this budget, the resolved layout's body capacity)`.
|
|
2109
|
+
*/
|
|
2110
|
+
maxComponentsPerSlide: number;
|
|
2111
|
+
bullets: {
|
|
2112
|
+
maxItems: number;
|
|
2113
|
+
/**
|
|
2114
|
+
* Same "unit" concept the deleted `CAPACITY.bullets` used
|
|
2115
|
+
* (`measureTextUnits`, CJK weight = 1.0) — a visual-width-weighted
|
|
2116
|
+
* character count, not a raw `.length`. The old physical ceiling (53,
|
|
2117
|
+
* derived from render geometry) was deleted in W3 without a replacement
|
|
2118
|
+
* check because every pacing's editorial budget here (30/40/48) is
|
|
2119
|
+
* already strictly tighter than it — the quality gate now applies
|
|
2120
|
+
* exactly this one number per pacing.
|
|
2121
|
+
*/
|
|
2122
|
+
maxUnitsPerItem: number;
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Pinned to spec §5's pacing table (`bodyBaselinePx` / editorial budget /
|
|
2127
|
+
* bullets budget columns): dense 20/5/6×48, balanced 24/4/5×40, spacious
|
|
2128
|
+
* 32/3/4×30 — same three rows as the pre-rename `text`/`balanced`/
|
|
2129
|
+
* `presentation` delivery table, values byte-for-byte unchanged (spec §10).
|
|
2130
|
+
*/
|
|
2131
|
+
declare const PACING_BUDGETS: Record<Pacing, PacingBudget>;
|
|
2132
|
+
|
|
2133
|
+
interface NarrativePreset {
|
|
2134
|
+
id: string;
|
|
2135
|
+
axes: NarrativeProfile;
|
|
2136
|
+
/**
|
|
2137
|
+
* Soft theme recommendations — a suggestion, never a hard constraint
|
|
2138
|
+
* (spec §5). Surfaced in workflow step ① so an agent can open with a
|
|
2139
|
+
* themed proposal — the user may still pick any theme. Every entry here
|
|
2140
|
+
* must be a real `BUILTIN_THEME_IDS` member (`ir/index.ts`) — enforced by
|
|
2141
|
+
* this module's test suite, which imports and tests against it.
|
|
2142
|
+
*/
|
|
2143
|
+
themeRecommendations: readonly string[];
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Pinned to spec §5's 7 named presets and their theme recommendation table.
|
|
2147
|
+
* Preset ids are unchanged by the vocabulary-v4 rename (spec §5: "预设 ID
|
|
2148
|
+
* 保持不变") — only each preset's internal axes keys/values follow the
|
|
2149
|
+
* strategy/pacing rename (e.g. `annual-review`'s `mode: "narrative"` is now
|
|
2150
|
+
* `strategy: "storytelling"`, spec §5's own worked example).
|
|
2151
|
+
*/
|
|
2152
|
+
declare const NARRATIVE_PRESETS: Record<string, NarrativePreset>;
|
|
2153
|
+
/**
|
|
2154
|
+
* = `NARRATIVE_PRESETS.general.axes` (briefing × balanced × public) — the
|
|
2155
|
+
* global default when narrative is omitted entirely (spec §5's defaults
|
|
2156
|
+
* chain).
|
|
2157
|
+
*/
|
|
2158
|
+
declare const DEFAULT_NARRATIVE: NarrativeProfile;
|
|
2159
|
+
/**
|
|
2160
|
+
* Resolve a narrative input down to concrete axes, per spec §5's design
|
|
2161
|
+
* principle "omission gets the default, a typo is a hard error" (weak-model
|
|
2162
|
+
* friendly: a model that leaves a field out gets a sane deck, a model that
|
|
2163
|
+
* misspells a value gets a loud, actionable error instead of a silently
|
|
2164
|
+
* wrong deck):
|
|
2165
|
+
*
|
|
2166
|
+
* - `undefined` → {@link DEFAULT_NARRATIVE} (the `general` preset's axes)
|
|
2167
|
+
* - a preset id string → that preset's axes (unknown id throws
|
|
2168
|
+
* {@link PptfastError}, listing the available preset ids)
|
|
2169
|
+
* - a partial axes object → each axis defaults independently
|
|
2170
|
+
* (strategy → "briefing", pacing → "balanced", audience → "public" — these
|
|
2171
|
+
* happen to equal `DEFAULT_NARRATIVE`'s values because `general` *is* that
|
|
2172
|
+
* exact combination, but the fallback here is per-axis, not "any omitted
|
|
2173
|
+
* axis falls back to the whole default object")
|
|
2174
|
+
*
|
|
2175
|
+
* An unknown axis value, or an unknown key on the partial axes object,
|
|
2176
|
+
* always throws {@link PptfastError} (never silently ignored or dropped) —
|
|
2177
|
+
* omission and a typo are different intents, and only the former has a
|
|
2178
|
+
* reasonable default.
|
|
2179
|
+
*
|
|
2180
|
+
* Renamed from `resolveScenario` (spec §8.1). Callers that still hold a
|
|
2181
|
+
* pre-rename `mode`/`delivery` shaped input (e.g. a v3 IR's `scenario`
|
|
2182
|
+
* field) must migrate it first — see `migrateIrV3ToV4`
|
|
2183
|
+
* (`src/ir/migrate.ts`) for the deterministic field/value mapping. A
|
|
2184
|
+
* v4-track document that still writes the old field/value spelling gets no
|
|
2185
|
+
* such rescue (spec §16, reversing the now-superseded §15.4): this function
|
|
2186
|
+
* hard-errors on it, same as any other unknown axis key or value.
|
|
2187
|
+
*/
|
|
2188
|
+
declare function resolveNarrative(input: string | Partial<NarrativeProfile> | undefined): NarrativeProfile;
|
|
2189
|
+
|
|
2190
|
+
/**
|
|
2191
|
+
* Deck spec schema + validation (spec §5 "plan artifact and hard gates", W5
|
|
2192
|
+
* task 2 — renamed to "Deck Spec" per the vocabulary-v4 rename, spec §6/§8.1;
|
|
2193
|
+
* "spec §N" citations throughout this file that predate this rename still
|
|
2194
|
+
* point at that original W5 design doc, not this rename's own spec — left
|
|
2195
|
+
* as historical citations, not renumbered).
|
|
2196
|
+
*
|
|
2197
|
+
* A deck spec is a workflow artifact, not a render prerequisite (spec §5's
|
|
2198
|
+
* "escape hatch": a bare IR v3 renders directly, `spec validate` is a
|
|
2199
|
+
* separate, optional gate for the spec-authoring stage of the six-phase
|
|
2200
|
+
* workflow). This module stays pure and Node-free — no `fs`, no CLI concerns
|
|
2201
|
+
* — so it can sit in `src/index.ts`'s dependency closure exactly like
|
|
2202
|
+
* `src/ir` and `src/narrative` already do (`AGENTS.md`'s layout rule).
|
|
2203
|
+
* `src/cli/commands.ts` owns the one Node-touching wrapper
|
|
2204
|
+
* (`runSpecValidate`: read file, call {@link validateSpec}, format the
|
|
2205
|
+
* result).
|
|
2206
|
+
*
|
|
2207
|
+
* Design mirrors `api.ts`'s `validateIr`/`ValidateResult` throughout
|
|
2208
|
+
* (structural zod pass first, then a sequential chain of hard-gate
|
|
2209
|
+
* categories, each short-circuiting the chain on its own failure — see
|
|
2210
|
+
* {@link validateSpec}'s own comment for why): same overall shape, adapted
|
|
2211
|
+
* for spec pages being keyed by an author-assigned `id` instead of IR's
|
|
2212
|
+
* positional slide index.
|
|
2213
|
+
*/
|
|
2214
|
+
|
|
2215
|
+
/**
|
|
2216
|
+
* Mirrors `SlideSchema.type` / `SlideType` (`ir/index.ts`,
|
|
2217
|
+
* `svg/layouts/registry.ts`) exactly. Kept as an independent literal tuple
|
|
2218
|
+
* here (not imported from either) — a page spec's `type` is a 4-value enum
|
|
2219
|
+
* on its own schema, not a re-export of IR's — but the `satisfies` clause
|
|
2220
|
+
* makes any future drift between the two a compile error instead of a
|
|
2221
|
+
* silent mismatch.
|
|
2222
|
+
*/
|
|
2223
|
+
declare const PAGE_TYPES: readonly ["cover", "chapter", "content", "ending"];
|
|
2224
|
+
declare const BEAT_VALUES: readonly ["anchor", "dense", "breathing"];
|
|
2225
|
+
type PageSpecType = (typeof PAGE_TYPES)[number];
|
|
2226
|
+
type PageBeat = (typeof BEAT_VALUES)[number];
|
|
2227
|
+
/**
|
|
2228
|
+
* A single page spec (spec §5, §6). `type`/`heading` are required — unlike
|
|
2229
|
+
* IR's own `SlideSchema` (where both default/omit for weak-model
|
|
2230
|
+
* friendliness), a deck spec is the authoring artifact those fields get
|
|
2231
|
+
* *locked* from at assemble time (W5 task 3), so leaving either implicit
|
|
2232
|
+
* here would defeat the point. `beat`/`focus`/`summary` stay optional per
|
|
2233
|
+
* spec §5's defaults chain ("beat omitted → auto-rotates by page position —
|
|
2234
|
+
* focus/summary/layout/slot can all be omitted").
|
|
2235
|
+
*/
|
|
2236
|
+
declare const PageSpecSchema: z.ZodObject<{
|
|
2237
|
+
id: z.ZodString;
|
|
2238
|
+
type: z.ZodEnum<{
|
|
2239
|
+
cover: "cover";
|
|
2240
|
+
chapter: "chapter";
|
|
2241
|
+
content: "content";
|
|
2242
|
+
ending: "ending";
|
|
2243
|
+
}>;
|
|
2244
|
+
heading: z.ZodString;
|
|
2245
|
+
beat: z.ZodOptional<z.ZodEnum<{
|
|
2246
|
+
anchor: "anchor";
|
|
2247
|
+
dense: "dense";
|
|
2248
|
+
breathing: "breathing";
|
|
2249
|
+
}>>;
|
|
2250
|
+
focus: z.ZodOptional<z.ZodString>;
|
|
2251
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
2252
|
+
}, z.core.$strict>;
|
|
2253
|
+
type PageSpec = z.infer<typeof PageSpecSchema>;
|
|
2254
|
+
/**
|
|
2255
|
+
* Top-level deck spec shape (spec §5, §6). `narrative`/`theme` deliberately
|
|
2256
|
+
* have no schema-level `.default(...)` — same reasoning as `PptxIRSchema`'s
|
|
2257
|
+
* own `narrative` field (`ir/index.ts`): the resolved value is never baked
|
|
2258
|
+
* back into the parsed shape, {@link validateSpec} (here) and, later,
|
|
2259
|
+
* assemble (W5 task 3) each resolve it themselves. `seed` is accepted but
|
|
2260
|
+
* entirely unexamined by this module — "not validateSpec's concern" (spec's
|
|
2261
|
+
* own wording): assemble generates and suggests writing one back on first
|
|
2262
|
+
* materialization.
|
|
2263
|
+
*
|
|
2264
|
+
* `version` stays the literal `"1"` (unchanged value) but now carries an
|
|
2265
|
+
* independent Deck Spec versioning scheme (spec §6: "`deck.spec.json` 使用
|
|
2266
|
+
* 独立的 spec 版本 1。它是新工件,不继承 `deck.plan.json` 的版本号语义") —
|
|
2267
|
+
* this "1" is the Deck Spec artifact's own first version, not a continuation
|
|
2268
|
+
* of the old deck-plan artifact's version counter, even though the digit is
|
|
2269
|
+
* the same.
|
|
2270
|
+
*/
|
|
2271
|
+
declare const DeckSpecSchema: z.ZodObject<{
|
|
2272
|
+
version: z.ZodDefault<z.ZodLiteral<"1">>;
|
|
2273
|
+
narrative: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
|
|
2274
|
+
theme: z.ZodOptional<z.ZodString>;
|
|
2275
|
+
filename: z.ZodOptional<z.ZodString>;
|
|
2276
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
2277
|
+
meta: z.ZodDefault<z.ZodObject<{
|
|
2278
|
+
organization: z.ZodOptional<z.ZodString>;
|
|
2279
|
+
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2280
|
+
name: z.ZodString;
|
|
2281
|
+
role: z.ZodOptional<z.ZodString>;
|
|
2282
|
+
org: z.ZodOptional<z.ZodString>;
|
|
2283
|
+
}, z.core.$strict>>>;
|
|
2284
|
+
date: z.ZodOptional<z.ZodString>;
|
|
2285
|
+
version: z.ZodOptional<z.ZodString>;
|
|
2286
|
+
confidentiality: z.ZodOptional<z.ZodEnum<{
|
|
2287
|
+
public: "public";
|
|
2288
|
+
internal: "internal";
|
|
2289
|
+
confidential: "confidential";
|
|
2290
|
+
restricted: "restricted";
|
|
2291
|
+
}>>;
|
|
2292
|
+
contact: z.ZodOptional<z.ZodObject<{
|
|
2293
|
+
name: z.ZodOptional<z.ZodString>;
|
|
2294
|
+
email: z.ZodOptional<z.ZodString>;
|
|
2295
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
2296
|
+
website: z.ZodOptional<z.ZodString>;
|
|
2297
|
+
}, z.core.$strict>>;
|
|
2298
|
+
copyright: z.ZodOptional<z.ZodString>;
|
|
2299
|
+
animation: z.ZodOptional<z.ZodObject<{
|
|
2300
|
+
transition: z.ZodOptional<z.ZodEnum<{
|
|
2301
|
+
fade: "fade";
|
|
2302
|
+
push: "push";
|
|
2303
|
+
wipe: "wipe";
|
|
2304
|
+
none: "none";
|
|
2305
|
+
}>>;
|
|
2306
|
+
elements: z.ZodOptional<z.ZodEnum<{
|
|
2307
|
+
none: "none";
|
|
2308
|
+
auto: "auto";
|
|
2309
|
+
}>>;
|
|
2310
|
+
}, z.core.$strict>>;
|
|
2311
|
+
}, z.core.$strict>>;
|
|
2312
|
+
brand: z.ZodOptional<z.ZodObject<{
|
|
2313
|
+
logo_asset_id: z.ZodOptional<z.ZodString>;
|
|
2314
|
+
position: z.ZodOptional<z.ZodEnum<{
|
|
2315
|
+
tl: "tl";
|
|
2316
|
+
tr: "tr";
|
|
2317
|
+
bl: "bl";
|
|
2318
|
+
br: "br";
|
|
2319
|
+
}>>;
|
|
2320
|
+
}, z.core.$strict>>;
|
|
2321
|
+
pages: z.ZodArray<z.ZodObject<{
|
|
2322
|
+
id: z.ZodString;
|
|
2323
|
+
type: z.ZodEnum<{
|
|
2324
|
+
cover: "cover";
|
|
2325
|
+
chapter: "chapter";
|
|
2326
|
+
content: "content";
|
|
2327
|
+
ending: "ending";
|
|
2328
|
+
}>;
|
|
2329
|
+
heading: z.ZodString;
|
|
2330
|
+
beat: z.ZodOptional<z.ZodEnum<{
|
|
2331
|
+
anchor: "anchor";
|
|
2332
|
+
dense: "dense";
|
|
2333
|
+
breathing: "breathing";
|
|
2334
|
+
}>>;
|
|
2335
|
+
focus: z.ZodOptional<z.ZodString>;
|
|
2336
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
2337
|
+
}, z.core.$strict>>;
|
|
2338
|
+
}, z.core.$strict>;
|
|
2339
|
+
type DeckSpec = z.infer<typeof DeckSpecSchema>;
|
|
2340
|
+
/** JSON Schema for the deck spec — feed this to a model before it writes one (see `pptfast schema --spec`). */
|
|
2341
|
+
declare function specJsonSchema(): Record<string, unknown>;
|
|
2342
|
+
interface SpecValidationIssue {
|
|
2343
|
+
path: string;
|
|
2344
|
+
message: string;
|
|
2345
|
+
/** The offending page's `id`, when the issue is scoped to one specific
|
|
2346
|
+
* page and that page's `id` could be determined — absent for deck-level
|
|
2347
|
+
* issues (e.g. "pages non-empty") and for structural issues on a page
|
|
2348
|
+
* whose own `id` itself failed to parse. */
|
|
2349
|
+
pageId?: string;
|
|
2350
|
+
}
|
|
2351
|
+
interface SpecValidateResult {
|
|
2352
|
+
ok: boolean;
|
|
2353
|
+
spec?: DeckSpec;
|
|
2354
|
+
errors: SpecValidationIssue[];
|
|
2355
|
+
}
|
|
2356
|
+
declare function formatSpecIssues(errors: SpecValidationIssue[]): string;
|
|
2357
|
+
/**
|
|
2358
|
+
* Deck-spec-level theme default (spec §5's defaults chain: "theme omitted →
|
|
2359
|
+
* consulting") — the same default IR's own `theme.id` field carries
|
|
2360
|
+
* (`ThemeSchema` in `ir/index.ts`). Exported so a caller already holding a
|
|
2361
|
+
* validated {@link DeckSpec} (the CLI's OK-summary line) doesn't re-derive
|
|
2362
|
+
* the fallback itself.
|
|
2363
|
+
*/
|
|
2364
|
+
declare function resolveSpecThemeId(spec: DeckSpec): string;
|
|
2365
|
+
/**
|
|
2366
|
+
* Deck-level page-count range per pacing (spec §5's pacing table,
|
|
2367
|
+
* initial values — "dense 8-30 / balanced 6-24 / spacious 4-16", not yet
|
|
2368
|
+
* tuned against real usage). Independent of `PACING_BUDGETS`
|
|
2369
|
+
* (`narrative/index.ts`, per-slide component-count/bullets editorial
|
|
2370
|
+
* budget) — this is a separate, deck-wide page-count concern the spec calls
|
|
2371
|
+
* out as its own hard gate ("page count vs. pacing recommended range").
|
|
2372
|
+
* Message wording renamed from "delivery" to "pacing" (vocabulary-v4
|
|
2373
|
+
* residual, routed from the task 1 review) — the axis itself was already
|
|
2374
|
+
* `Pacing` at the type level, this closes the last stale word in the
|
|
2375
|
+
* error text.
|
|
2376
|
+
*/
|
|
2377
|
+
declare const SPEC_PAGE_COUNT_RANGE: Record<Pacing, {
|
|
2378
|
+
min: number;
|
|
2379
|
+
max: number;
|
|
2380
|
+
}>;
|
|
2381
|
+
/**
|
|
2382
|
+
* Validate raw JSON against the spec schema, then — once it parses — run the
|
|
2383
|
+
* spec §5 hard-gate chain. Mirrors `validateIr`'s (`api.ts`) overall shape: a
|
|
2384
|
+
* structural zod pass first, then a sequence of isolated hard-gate
|
|
2385
|
+
* categories, each short-circuiting the whole chain on its own failure
|
|
2386
|
+
* (rather than accumulating errors across categories) so a later category
|
|
2387
|
+
* never has to guess at what an earlier, already-broken one would have
|
|
2388
|
+
* meant — e.g. beat-rotation and page-count both need a resolved
|
|
2389
|
+
* narrative, so nothing past the narrative/theme stage runs until that
|
|
2390
|
+
* resolves cleanly. Every spec-gate philosophy here is "hard block, no soft
|
|
2391
|
+
* warning" (spec §5's "escape hatch" section — a spec that doesn't fit this shape
|
|
2392
|
+
* should be authored as bare IR instead, not warned-and-shipped).
|
|
2393
|
+
*/
|
|
2394
|
+
declare function validateSpec(input: unknown): SpecValidateResult;
|
|
2395
|
+
|
|
2396
|
+
/**
|
|
2397
|
+
* One page's fillable content — everything a page spec's `id` does *not*
|
|
2398
|
+
* already lock in. Deliberately excludes `type`/`heading` (spec-owned, see
|
|
2399
|
+
* {@link assembleDeck}'s locked-field gate) and `subheading`/`decor`
|
|
2400
|
+
* (legitimate `Slide` fields, but outside this record's shape by spec §7's
|
|
2401
|
+
* own layout — "pages/<id>.json contains only components" — a spec/pages deck can't
|
|
2402
|
+
* author either one — a hand-authored bare IR still can). Every field here is
|
|
2403
|
+
* a same-name, same-shape subset of `Slide`'s own optional fields
|
|
2404
|
+
* (`../ir`'s `SlideSchema`) — reused, not redeclared, so the two can't drift.
|
|
2405
|
+
*/
|
|
2406
|
+
interface PageContent {
|
|
2407
|
+
components?: Component[];
|
|
2408
|
+
layout?: string;
|
|
2409
|
+
arrangement?: NonNullable<Slide["arrangement"]>;
|
|
2410
|
+
background?: BackgroundSpec;
|
|
2411
|
+
image_side?: "left" | "right";
|
|
2412
|
+
footnote?: string;
|
|
2413
|
+
notes?: string;
|
|
2414
|
+
}
|
|
2415
|
+
interface AssembleResult {
|
|
2416
|
+
ir: PptxIR;
|
|
2417
|
+
/**
|
|
2418
|
+
* Set only when `spec.seed` was absent and {@link assembleDeck} generated
|
|
2419
|
+
* one deterministically (see the seed section of this function's doc
|
|
2420
|
+
* comment) — `undefined` when `spec.seed` was already present and simply
|
|
2421
|
+
* passed through. The CLI shell (W5 task 5) is the one that acts on this:
|
|
2422
|
+
* suggest writing the value back into the spec file, never rewrite it
|
|
2423
|
+
* itself (assemble stays a pure function, no fs side effects here).
|
|
2424
|
+
*/
|
|
2425
|
+
generatedSeed?: number;
|
|
2426
|
+
/**
|
|
2427
|
+
* Count of pages whose `layout` field this call filled in via
|
|
2428
|
+
* {@link materializeEffectiveLayouts} (W4 design decision 10) —
|
|
2429
|
+
* `undefined` when zero, same "absent means nothing to report" posture as
|
|
2430
|
+
* {@link generatedSeed} just above (a page with an explicit pin already in
|
|
2431
|
+
* its page file, or every omitted page landing on the image-cover
|
|
2432
|
+
* takeover's `null` bypass, both leave this unset). Purely informational:
|
|
2433
|
+
* the CLI shell (`runAssemble`, `../cli/commands.ts`) surfaces it as a
|
|
2434
|
+
* one-line note, nothing here or downstream acts on the number itself.
|
|
2435
|
+
*/
|
|
2436
|
+
materializedLayoutCount?: number;
|
|
2437
|
+
}
|
|
2438
|
+
/**
|
|
2439
|
+
* Assemble a validated deck spec plus a per-page content record into a
|
|
2440
|
+
* renderable IR. See this module's own top comment for the overall shape.
|
|
2441
|
+
* Step numbers below match the W5 task-3 brief's own numbered "inject
|
|
2442
|
+
* semantics" list verbatim — kept in that exact order because two of them
|
|
2443
|
+
* (locked-field vs. orphan) both throw and their relative order is
|
|
2444
|
+
* otherwise unobservable from either doc comment alone.
|
|
2445
|
+
*
|
|
2446
|
+
* 1. `spec` is `unknown` (same boundary `validateSpec` itself has — a spec
|
|
2447
|
+
* is almost always freshly `JSON.parse`d off disk by the caller) —
|
|
2448
|
+
* invalid shape or a failed hard gate throws {@link PptfastError} with
|
|
2449
|
+
* `validateSpec`'s own formatted issue list, not a re-derived message.
|
|
2450
|
+
* 2. Shape guard + locked-field protection: a `pages[id]` entry must first be
|
|
2451
|
+
* a plain object — not `null`, an array, or a primitive — else throws.
|
|
2452
|
+
* `pages` is `unknown`-shaped off disk same as `spec` itself (step 1), so
|
|
2453
|
+
* a JSON `null`/string/array content value is a real possibility, not
|
|
2454
|
+
* just a type-system hole, and `Object.hasOwn` throws its own
|
|
2455
|
+
* uninformative native `TypeError` on `null` (and silently no-ops on a
|
|
2456
|
+
* string/array/number) rather than this gate's own readable message.
|
|
2457
|
+
* Once that holds, a `pages[id]` entry that carries a `type` or `heading`
|
|
2458
|
+
* *key* — even set to `undefined` — throws. `Object.hasOwn`, not a
|
|
2459
|
+
* `!== undefined` read, is what makes that "even `undefined`" case
|
|
2460
|
+
* catchable: `PageContent` itself never declares either field, but a
|
|
2461
|
+
* page file freshly parsed off disk is `unknown` before it reaches this
|
|
2462
|
+
* function's declared `PageContent` parameter type, so a stray
|
|
2463
|
+
* `"heading": null`-turned-`undefined` or a copy-pasted empty key is
|
|
2464
|
+
* exactly the drift this gate exists to catch instead of silently
|
|
2465
|
+
* ignoring.
|
|
2466
|
+
* 3. Orphan keys: a `pages` entry whose id isn't any spec page's id. Listed
|
|
2467
|
+
* together with a fix suggestion, checked only after every present page
|
|
2468
|
+
* has cleared the locked-field gate (step 2) — an orphan file that
|
|
2469
|
+
* *also* happens to redeclare `heading` reports as locked-field first.
|
|
2470
|
+
* 4. Missing pages (a spec id with no `pages` entry) become a placeholder
|
|
2471
|
+
* slide — never an error. Spec §7's own words: "assemble's precise
|
|
2472
|
+
* semantics — a missing page always succeeds (placeholder), a structural
|
|
2473
|
+
* contradiction (orphan file / bad spec / id conflict) errors". A
|
|
2474
|
+
* declared `summary` becomes the placeholder's `subheading` (the one spot
|
|
2475
|
+
* `summary` — otherwise a spec-only anchor, see step 5 — does reach the
|
|
2476
|
+
* IR) so a `--draft` preview of an unfilled deck still reads as more than
|
|
2477
|
+
* a bare "Untitled".
|
|
2478
|
+
* 5. Present pages become a full slide: `id`/`type`/`heading` from the page
|
|
2479
|
+
* spec (never the content record — see step 2), plus whichever of
|
|
2480
|
+
* {@link PageContent}'s seven fields the content record actually set.
|
|
2481
|
+
* `beat`/`focus`/`summary` never reach the IR for a present page —
|
|
2482
|
+
* they are spec-only authoring anchors (beat/focus steer a future
|
|
2483
|
+
* fill/select step, summary is "for the fill step's own reading only"), not slide content.
|
|
2484
|
+
* 6. Top-level: `version` is always the literal `"4"` (IR's own version,
|
|
2485
|
+
* unrelated to the deck spec's own `version: "1"`) — the deck spec's own
|
|
2486
|
+
* `narrative` field (renamed this task from `scenario`, spec §8.1's
|
|
2487
|
+
* `DeckPlan`→`DeckSpec` rename, task 2) carries across into the v4 IR's
|
|
2488
|
+
* own `narrative` field, its value already in the new strategy/pacing
|
|
2489
|
+
* vocabulary (vocabulary-v4 rename, task 1 — `plan/index.ts`'s own
|
|
2490
|
+
* `resolveNarrative` call already validates it against that vocabulary
|
|
2491
|
+
* before this function ever runs).
|
|
2492
|
+
* `theme`/`filename`/`brand`/`meta`/`seed` (step 7) carry over from the spec when
|
|
2493
|
+
* present. When absent, this function omits the field from the raw
|
|
2494
|
+
* object it hands to {@link PptxIRSchema} rather than re-deriving IR's
|
|
2495
|
+
* own default value a second time — one default source of truth, the
|
|
2496
|
+
* schema itself (e.g. `theme` omitted here becomes `{ id: "consulting" }`,
|
|
2497
|
+
* exactly like a bare hand-authored IR that never mentions theme at all —
|
|
2498
|
+
* not a value this function needs to know).
|
|
2499
|
+
* 7. Seed: `spec.seed` present → passed through, `generatedSeed` stays
|
|
2500
|
+
* `undefined` on the result. Absent → {@link generateSeed} derives one
|
|
2501
|
+
* from `filename` + the spec's own ordered page-id list (never page
|
|
2502
|
+
* *content* — see that function's own doc comment for why), written to
|
|
2503
|
+
* `ir.seed` *and* returned as `generatedSeed` so a CLI shell can suggest
|
|
2504
|
+
* writing it back into the spec file (this function never touches disk
|
|
2505
|
+
* itself).
|
|
2506
|
+
* 8. Idempotence: every step above is a pure function of its inputs (no
|
|
2507
|
+
* randomness, no wall-clock, no reliance on unordered iteration) — two
|
|
2508
|
+
* calls with structurally-equal `spec`/`pages` produce deep-equal
|
|
2509
|
+
* results, `generatedSeed` included. Exercised directly by this module's
|
|
2510
|
+
* test suite rather than asserted here.
|
|
2511
|
+
*/
|
|
2512
|
+
declare function assembleDeck(spec: unknown, pages: Record<string, PageContent>): AssembleResult;
|
|
2513
|
+
/**
|
|
2514
|
+
* Inverse of {@link assembleDeck}: reconstructs `{ spec, pages }` from an
|
|
2515
|
+
* existing IR well enough that `assembleDeck(...disassembleDeck(ir))`
|
|
2516
|
+
* reproduces every slide's content — but the map is not lossless in both
|
|
2517
|
+
* directions, only in the direction the round trip actually exercises
|
|
2518
|
+
* (IR → spec/pages → IR). Fields with no IR-side home never survive being
|
|
2519
|
+
* written *to* the IR in the first place, so there is nothing here to read
|
|
2520
|
+
* back:
|
|
2521
|
+
*
|
|
2522
|
+
* - `beat` and `focus` never appear on any produced {@link PageSpec} —
|
|
2523
|
+
* both are spec-only authoring anchors with no corresponding `Slide`
|
|
2524
|
+
* field at all (see {@link assembleDeck} step 5's doc comment). Nothing
|
|
2525
|
+
* here could recover a value that was never written anywhere.
|
|
2526
|
+
* - `summary` is recovered *only* for a placeholder slide, by reversing
|
|
2527
|
+
* step 4's `summary` → `subheading` injection (`slide.subheading` back to
|
|
2528
|
+
* `pageSpec.summary`). A non-placeholder slide's own `subheading` — a
|
|
2529
|
+
* legitimate, independent `Slide` field a hand-authored bare IR is free to
|
|
2530
|
+
* set — has no {@link PageContent} field to land in (spec §7's pages/
|
|
2531
|
+
* record is deliberately narrower than `Slide` itself, see that
|
|
2532
|
+
* interface's own doc comment) and no `summary` semantics either (`summary`
|
|
2533
|
+
* only ever flows to a *placeholder*'s `subheading`, never the reverse for
|
|
2534
|
+
* a filled page) — so it is dropped. Same for `decor`: a real `Slide`
|
|
2535
|
+
* field, absent from `PageContent`'s shape entirely.
|
|
2536
|
+
* - `theme.style` / `theme.brand` overrides collapse to a bare theme-id
|
|
2537
|
+
* string (`DeckSpecSchema.theme` has no shape for either) — only `theme.id`
|
|
2538
|
+
* survives. That `theme.brand` is `ThemeSchema.brand` (`BrandConfigSchema`
|
|
2539
|
+
* — `suppressFooterOnCardContent`/`suppressFooterRule`, footer-chrome
|
|
2540
|
+
* flags owned by the *theme*) — not to be confused with the deck-level
|
|
2541
|
+
* `brand` field below, a different, unrelated schema despite the shared
|
|
2542
|
+
* name.
|
|
2543
|
+
* - `ir.assets.images` is not part of this function's return value at all —
|
|
2544
|
+
* `{ spec, pages }` has no `assets` field, and this module stays zero-fs
|
|
2545
|
+
* by design (this file's own top comment), so it has no way to write an
|
|
2546
|
+
* `assets/` directory itself. Any `asset_id` reference inside a copied
|
|
2547
|
+
* `components`/`background` survives untouched ({@link extractPageContent}
|
|
2548
|
+
* copies both as-is), but the underlying image bytes are deliberately left
|
|
2549
|
+
* for the caller: the CLI shell closes that gap. `runDisassemble`
|
|
2550
|
+
* (`../cli/commands.ts`) walks the *input* IR's `ir.assets.images` itself
|
|
2551
|
+
* and materializes every entry into `<outDir>/assets/<id>.<ext>`
|
|
2552
|
+
* (`writeDeckAssets`, `../cli/deck-dir.ts`) so a later `readDeckDir`'s
|
|
2553
|
+
* `scanAssets` re-registers them exactly like any other deck-directory
|
|
2554
|
+
* asset. Skipping that shell-side step is not a doc-comment nuance — it
|
|
2555
|
+
* reproduces as a real bug: an image deck disassembles with every
|
|
2556
|
+
* `asset_id` left dangling, then re-assembles and renders with the image
|
|
2557
|
+
* silently missing.
|
|
2558
|
+
*
|
|
2559
|
+
* Round-trip-safe despite the above, worth calling out because of that name
|
|
2560
|
+
* collision: the top-level `brand` field (`BrandSchema` — `logo_asset_id` /
|
|
2561
|
+
* `position`, the deck logo/position `BrandChrome` reads,
|
|
2562
|
+
* `src/svg/BrandChrome.tsx`) is a plain passthrough on both sides
|
|
2563
|
+
* ({@link assembleDeck} step 6 reads `spec.brand` into `ir.brand` — this
|
|
2564
|
+
* function reads `ir.brand` back into `spec.brand` below) — carried through
|
|
2565
|
+
* unmodified, same as `narrative`/`filename`/`seed`, never
|
|
2566
|
+
* synthesized or dropped.
|
|
2567
|
+
*
|
|
2568
|
+
* `layout` deserves a different kind of callout: it round-trips as plain
|
|
2569
|
+
* content like any other field ({@link extractPageContent} copies
|
|
2570
|
+
* `slide.layout` into `PageContent.layout` whenever the slide has one, no
|
|
2571
|
+
* special case) — but a `deck.json` produced by {@link assembleDeck} now
|
|
2572
|
+
* carries a `layout` on nearly every slide (W4 design decision 10's
|
|
2573
|
+
* materialization, see that function's own doc comment), even on pages whose
|
|
2574
|
+
* *original page file* never set one. Nothing on `Slide` marks which is
|
|
2575
|
+
* which — `layout` is just a string either way — so disassembling an
|
|
2576
|
+
* already-assembled `deck.json` writes every materialized pick into the
|
|
2577
|
+
* regenerated page file as if it had been an explicit pin all along, and a
|
|
2578
|
+
* later re-`assembleDeck` call skips materialization for that page from then
|
|
2579
|
+
* on, same as any hand-authored pin. Accepted, not a bug: `disassembleDeck`
|
|
2580
|
+
* is a one-time bare-IR importer into an editable project directory, not a
|
|
2581
|
+
* round-trip channel for `deck.json` itself. A deck project's own
|
|
2582
|
+
* `pages/<id>.json` files are the durable, edit-in-place artifacts —
|
|
2583
|
+
* `deck.json` is downstream output, and feeding it back through
|
|
2584
|
+
* `disassembleDeck` (instead of editing the project directory that produced
|
|
2585
|
+
* it) is what actually costs those pages their revision-stability
|
|
2586
|
+
* re-selection eligibility going forward. A real, user-visible narrowing,
|
|
2587
|
+
* worth knowing about here, not worth adding code to guard against for a
|
|
2588
|
+
* usage pattern nothing in this codebase actually exercises.
|
|
2589
|
+
*
|
|
2590
|
+
* Two structural fields are synthesized rather than copied when the source
|
|
2591
|
+
* slide omits them, each documented where it is generated:
|
|
2592
|
+
* {@link UNTITLED_HEADING} for a missing/blank `heading`, and a positional
|
|
2593
|
+
* `p-<1-based-ordinal>-<type>` scheme for a missing `id` (stable across
|
|
2594
|
+
* repeated calls on the same IR — it is a pure function of slide position
|
|
2595
|
+
* and type — but, unlike a spec-assigned id, *not* stable across inserting
|
|
2596
|
+
* or reordering slides — out of scope here, since a bare IR with no `id` at
|
|
2597
|
+
* all has no stabler identity to fall back to in the first place).
|
|
2598
|
+
*
|
|
2599
|
+
* That generated `p-<ordinal>-<type>` id is safe by construction as a
|
|
2600
|
+
* page/asset file-name segment (W5 whole-branch review finding 1, verified
|
|
2601
|
+
* — not just asserted — rather than also routing it through
|
|
2602
|
+
* `assertSafeFileSegment`, `../cli/deck-dir.ts`): `<ordinal>` is
|
|
2603
|
+
* `index + 1`, always a plain non-negative integer, and `<type>` is a
|
|
2604
|
+
* `Slide["type"]`, a closed schema enum (`"cover" | "chapter" | "content" |
|
|
2605
|
+
* "ending"`, `SlideSchema.type` in `../ir/index.ts`) — neither half can ever
|
|
2606
|
+
* contain a `/`, a `\`, or resolve to `".."`, so the joined id can't either.
|
|
2607
|
+
* A carried-over `slide.id` (the `??` branch's other side) has no such
|
|
2608
|
+
* guarantee — that is the one this function passes straight through
|
|
2609
|
+
* unchecked, same as every other field {@link extractPageContent} copies —
|
|
2610
|
+
* which is exactly why the write-time gate in `../cli/deck-dir.ts` (not this
|
|
2611
|
+
* function) is what actually closes the vulnerability.
|
|
2612
|
+
*/
|
|
2613
|
+
declare function disassembleDeck(ir: PptxIR): {
|
|
2614
|
+
spec: DeckSpec;
|
|
2615
|
+
pages: Record<string, PageContent>;
|
|
2616
|
+
};
|
|
2617
|
+
|
|
2618
|
+
/**
|
|
2619
|
+
* Deterministic, pure `deck.plan.json` → `deck.spec.json` migration (spec
|
|
2620
|
+
* §9.2, vocabulary-v4 rename, task 2). Mirrors `../ir/migrate.ts`'s
|
|
2621
|
+
* `migrateIrV3ToV4` in spirit — mechanical field rename only, no schema
|
|
2622
|
+
* validation, no model call, no content rewrite (same posture spec §9.3
|
|
2623
|
+
* states for the IR v3→v4 primitive: "只做已声明的结构映射,不运行模型,
|
|
2624
|
+
* 不重写内容,不重新选择 layout" — this function is that same contract
|
|
2625
|
+
* applied to the other artifact this rename touches). A caller should run
|
|
2626
|
+
* `validateSpec` (`./index.ts`) against the result to confirm it lands as a
|
|
2627
|
+
* legal deck spec, exactly as it would for a `deck.spec.json` authored by
|
|
2628
|
+
* hand — this function itself never parses against `DeckSpecSchema`.
|
|
2629
|
+
*
|
|
2630
|
+
* Field-for-field, value-for-value per spec §9.2's table:
|
|
2631
|
+
*
|
|
2632
|
+
* ```text
|
|
2633
|
+
* deck.plan.json → deck.spec.json
|
|
2634
|
+
* scenario → narrative
|
|
2635
|
+
* pages[].rhythm → pages[].beat
|
|
2636
|
+
* 其余字段 → 原样保留
|
|
2637
|
+
* ```
|
|
2638
|
+
*
|
|
2639
|
+
* Takes the raw, `JSON.parse`d `deck.plan.json` contents (`unknown`) rather
|
|
2640
|
+
* than an already-validated shape — unlike `migrateIrV3ToV4` (which takes an
|
|
2641
|
+
* already-`PptxIRV3Schema.parse`d object), there is no schema left in this
|
|
2642
|
+
* codebase a pre-rename plan file could validate against: `DeckSpecSchema`
|
|
2643
|
+
* (`./index.ts`) already requires the *post*-rename field names (`narrative`,
|
|
2644
|
+
* `beat`), so parsing a plan-shaped document against it would just fail on
|
|
2645
|
+
* the very keys this function exists to rename. Non-object input, or a
|
|
2646
|
+
* `pages` value that isn't an array, passes through completely unchanged —
|
|
2647
|
+
* reporting a malformed source file is `runMigrate`'s job
|
|
2648
|
+
* (`../cli/commands.ts`), not this mechanical rename step's.
|
|
2649
|
+
*/
|
|
2650
|
+
declare function migrateDeckPlanToSpec(raw: unknown): unknown;
|
|
2651
|
+
|
|
2652
|
+
/**
|
|
2653
|
+
* `pptfast audit` finding shape (v0.3 W6, spec §7 workflow ④). `page` is
|
|
2654
|
+
* 1-based (matches `ValidationIssue.page` in `api.ts`); `slideId` is set
|
|
2655
|
+
* whenever the offending slide has a stable `id` (assemble-stamped or
|
|
2656
|
+
* author-supplied), same "page + optional id" convention `formatIssues`
|
|
2657
|
+
* already uses for validate errors.
|
|
2658
|
+
*/
|
|
2659
|
+
interface AuditFinding {
|
|
2660
|
+
page: number;
|
|
2661
|
+
slideId?: string;
|
|
2662
|
+
code: "overflow" | "out-of-bounds" | "low-contrast" | "overlap" | "content-truncated" | "content-dropped";
|
|
2663
|
+
message: string;
|
|
2664
|
+
detail?: Record<string, unknown>;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Which check families actually ran (audit-v2 phase B, spec §4.2/§11.6) —
|
|
2668
|
+
* the one piece of the spec's original metrics design that survives into
|
|
2669
|
+
* this wave (§11.6: metrics itself deferred, "唯一现值是 checks 结构"). This
|
|
2670
|
+
* is the wave's own spirit made literal: a field can only ever be
|
|
2671
|
+
* `"completed"` once its check family genuinely ran over every audited
|
|
2672
|
+
* page, never a default that silently reads as "passed". `svg` has no
|
|
2673
|
+
* `"not-requested"` state — the deterministic SVG audit always runs, so it
|
|
2674
|
+
* is always `"completed"`. `pixels` starts `"not-requested"` and only ever
|
|
2675
|
+
* becomes `"completed"`; there is deliberately no `"failed"` value — a
|
|
2676
|
+
* failed pixel audit throws (spec §11.7's "契约层"), it never reports
|
|
2677
|
+
* itself as a completed check that happens to carry no findings.
|
|
2678
|
+
*/
|
|
2679
|
+
interface AuditChecks {
|
|
2680
|
+
svg: "completed";
|
|
2681
|
+
pixels: "not-requested" | "completed";
|
|
2682
|
+
}
|
|
2683
|
+
interface AuditReport {
|
|
2684
|
+
findings: AuditFinding[];
|
|
2685
|
+
pagesAudited: number;
|
|
2686
|
+
pagesSkipped: number;
|
|
2687
|
+
checks: AuditChecks;
|
|
2688
|
+
}
|
|
2689
|
+
/** `auditDeck`'s second parameter (audit-v2 phase B) — see that function's
|
|
2690
|
+
* own doc comment for the overload contract this shape backs. */
|
|
2691
|
+
interface AuditDeckOptions {
|
|
2692
|
+
pixels?: boolean;
|
|
2693
|
+
}
|
|
2694
|
+
/**
|
|
2695
|
+
* The SDK entry point. Advisory, not a hard gate: `validateIr` already
|
|
2696
|
+
* rejects structurally invalid or over-dense decks before a caller ever
|
|
2697
|
+
* gets this far; this function looks for the visual problems that can still
|
|
2698
|
+
* slip through a valid deck at render time (an author-chosen near-background
|
|
2699
|
+
* text color, two components whose combined content happens to collide, a
|
|
2700
|
+
* card list that had to drop an item to fit). A non-empty `findings` array
|
|
2701
|
+
* is a prompt for a human/agent to look, not a rejection.
|
|
2702
|
+
*
|
|
2703
|
+
* Placeholder pages (`slide.placeholder === true`) are skipped — assemble's
|
|
2704
|
+
* stand-in for content nobody has written yet has nothing to audit, same
|
|
2705
|
+
* reasoning `checkIrQuality` already uses to skip them (`ir-quality.ts`).
|
|
2706
|
+
*
|
|
2707
|
+
* `auditDeck` itself never calls `installNodePlatform()`; that's the
|
|
2708
|
+
* caller's job (the CLI does it automatically).
|
|
2709
|
+
*
|
|
2710
|
+
* `opts.pixels` (audit-v2 phase B, spec §4.3/§11.7) opts into the optional
|
|
2711
|
+
* pixel-level contrast audit over image-backed text (`../svg/audit/pixel-audit.ts`)
|
|
2712
|
+
* — overloaded so the far more common omitted/`false` case keeps returning
|
|
2713
|
+
* a plain, synchronous `AuditReport` (spec §11.7's "语义层": the default
|
|
2714
|
+
* audit stays pure TS with zero added latency or Promise-wrapping), while
|
|
2715
|
+
* `pixels: true` returns a `Promise<AuditReport>` that resolves once the
|
|
2716
|
+
* rasterization pass completes. A caller holding a non-literal
|
|
2717
|
+
* `{ pixels: someBoolean }` won't match either overload directly (TypeScript
|
|
2718
|
+
* can't narrow it to one specific branch) — branch on the boolean first and
|
|
2719
|
+
* call this function with a literal in each arm, e.g. `opts.pixels ? await
|
|
2720
|
+
* auditDeck(ir, { pixels: true }) : auditDeck(ir)` (`cli/commands.ts`'s
|
|
2721
|
+
* `runAudit` does exactly this).
|
|
2722
|
+
*/
|
|
2723
|
+
declare function auditDeck(ir: PptxIR, opts?: {
|
|
2724
|
+
pixels?: false;
|
|
2725
|
+
}): AuditReport;
|
|
2726
|
+
declare function auditDeck(ir: PptxIR, opts: {
|
|
2727
|
+
pixels: true;
|
|
2728
|
+
}): Promise<AuditReport>;
|
|
1075
2729
|
|
|
1076
|
-
export { type Assets, type BackgroundSpec, type
|
|
2730
|
+
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 };
|