@neatico/visual-core 0.14.2 → 0.14.26
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/dist/cjs/composition.js +62 -36
- package/dist/cjs/compositionScenes.js +91 -38
- package/dist/cjs/content.js +538 -120
- package/dist/cjs/designBlueprint.js +94 -19
- package/dist/cjs/designSystem.js +464 -77
- package/dist/cjs/editorial.js +76 -20
- package/dist/cjs/fontCatalog.js +75 -23
- package/dist/cjs/layoutContracts.js +57 -11
- package/dist/cjs/layouts.js +5 -92
- package/dist/cjs/pagination.js +16 -5
- package/dist/cjs/socialLayouts.js +127 -387
- package/dist/cjs/text.js +12 -2
- package/dist/composition.d.ts +4 -4
- package/dist/composition.js +62 -36
- package/dist/compositionScenes.d.ts +2 -2
- package/dist/compositionScenes.js +91 -38
- package/dist/content.d.ts +55 -20
- package/dist/content.js +539 -121
- package/dist/designBlueprint.d.ts +4 -4
- package/dist/designBlueprint.js +95 -20
- package/dist/designSystem.d.ts +11 -7
- package/dist/designSystem.js +465 -79
- package/dist/editorial.d.ts +1 -1
- package/dist/editorial.js +77 -21
- package/dist/fontCatalog.d.ts +1 -1
- package/dist/fontCatalog.js +75 -23
- package/dist/layoutContracts.d.ts +3 -3
- package/dist/layoutContracts.js +58 -12
- package/dist/layouts.d.ts +2 -2
- package/dist/layouts.js +5 -92
- package/dist/pagination.js +16 -5
- package/dist/socialLayouts.d.ts +40 -232
- package/dist/socialLayouts.js +127 -387
- package/dist/text.js +12 -2
- package/package.json +2 -2
package/dist/composition.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
export const COMPOSITION_PROMPT = `Also return composition:{version:1,root:node}. Design a hierarchy from the content, not a fixed template. A node is {type,weight?,tone?,role?,bind?,icon?,children?}. row and column contain 2-4 children; nest up to 5 levels, at most 40 nodes. Weight is relative space 0.5-4. Tone plain/card/contrast applies to groups. Text binds exactly one existing nonempty content field (title or headline, body, eyebrow, cta, value, attribution, items.0.title, items.0.description, etc); never bind notes. Include EVERY nonempty content field exactly once. Text role hero/heading/body/caption controls typography. Image is an optional original conceptual illustration, not invented evidence or a photo. Icon optionally uses idea/check/chart/people/shield/arrow. Divider separates related content. No coordinates, CSS, HTML, URLs or text in the tree. Use one strong hero, purposeful contrast, asymmetric columns, grouped evidence, and breathing room. Rows reflow for narrow formats. Different stories should have different trees, not just different colors.`;
|
|
2
2
|
export function compositionFields(value) {
|
|
3
3
|
const fields = {};
|
|
4
|
-
for (const key of [
|
|
5
|
-
|
|
4
|
+
for (const key of [
|
|
5
|
+
"title",
|
|
6
|
+
"headline",
|
|
7
|
+
"body",
|
|
8
|
+
"eyebrow",
|
|
9
|
+
"cta",
|
|
10
|
+
"value",
|
|
11
|
+
"attribution",
|
|
12
|
+
])
|
|
13
|
+
if (typeof value[key] === "string" && value[key].trim())
|
|
6
14
|
fields[key] = value[key];
|
|
7
15
|
if (Array.isArray(value.items))
|
|
8
|
-
value.items.forEach((item, i) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
value.items.forEach((item, i) => {
|
|
17
|
+
if (!item || typeof item !== "object")
|
|
18
|
+
return;
|
|
19
|
+
for (const key of ["title", "description"]) {
|
|
20
|
+
if (!Object.prototype.hasOwnProperty.call(item, key))
|
|
21
|
+
continue;
|
|
22
|
+
const v = item[key];
|
|
23
|
+
if (typeof v === "string" && v.trim())
|
|
24
|
+
fields[`items.${i}.${key}`] = v;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
16
27
|
return fields;
|
|
17
28
|
}
|
|
18
29
|
export function validateComposition(raw, fields) {
|
|
@@ -20,44 +31,59 @@ export function validateComposition(raw, fields) {
|
|
|
20
31
|
let count = 0, images = 0;
|
|
21
32
|
const seen = new Set();
|
|
22
33
|
if (!value || value.version !== 1)
|
|
23
|
-
throw new Error(
|
|
34
|
+
throw new Error("Unsupported composition");
|
|
24
35
|
function visit(n, depth) {
|
|
25
|
-
if (!n ||
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
throw new Error(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
if (!n ||
|
|
37
|
+
typeof n !== "object" ||
|
|
38
|
+
++count > 40 ||
|
|
39
|
+
depth > 5 ||
|
|
40
|
+
!["row", "column", "text", "image", "divider", "icon"].includes(n.type))
|
|
41
|
+
throw new Error("Invalid composition node");
|
|
42
|
+
if (n.weight !== undefined &&
|
|
43
|
+
(!Number.isFinite(n.weight) || n.weight < 0.5 || n.weight > 4))
|
|
44
|
+
throw new Error("Invalid composition weight");
|
|
45
|
+
if (n.tone !== undefined && !["plain", "card", "contrast"].includes(n.tone))
|
|
46
|
+
throw new Error("Invalid composition tone");
|
|
47
|
+
const out = {
|
|
48
|
+
type: n.type,
|
|
49
|
+
weight: n.weight,
|
|
50
|
+
tone: n.tone,
|
|
51
|
+
};
|
|
52
|
+
if (n.type === "row" || n.type === "column") {
|
|
53
|
+
if (!Array.isArray(n.children) ||
|
|
54
|
+
n.children.length < 2 ||
|
|
55
|
+
n.children.length > 4)
|
|
56
|
+
throw new Error("Invalid composition group");
|
|
57
|
+
out.children = n.children.map((c) => visit(c, depth + 1));
|
|
36
58
|
}
|
|
37
59
|
else {
|
|
38
60
|
if (n.children !== undefined)
|
|
39
|
-
throw new Error(
|
|
40
|
-
if (n.type ===
|
|
41
|
-
if (!n.bind ||
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
throw new Error(
|
|
61
|
+
throw new Error("Leaf cannot have children");
|
|
62
|
+
if (n.type === "text") {
|
|
63
|
+
if (!n.bind ||
|
|
64
|
+
!Object.prototype.hasOwnProperty.call(fields, n.bind) ||
|
|
65
|
+
seen.has(n.bind))
|
|
66
|
+
throw new Error("Invalid or duplicated binding");
|
|
67
|
+
if (n.role !== undefined &&
|
|
68
|
+
!["hero", "heading", "body", "caption"].includes(n.role))
|
|
69
|
+
throw new Error("Invalid text role");
|
|
45
70
|
seen.add(n.bind);
|
|
46
71
|
out.bind = n.bind;
|
|
47
|
-
out.role = n.role ||
|
|
72
|
+
out.role = n.role || "body";
|
|
48
73
|
}
|
|
49
|
-
if (n.type ===
|
|
50
|
-
throw new Error(
|
|
51
|
-
if (n.type ===
|
|
52
|
-
if (!n.icon ||
|
|
53
|
-
|
|
74
|
+
if (n.type === "image" && ++images > 1)
|
|
75
|
+
throw new Error("Only one illustration per composition");
|
|
76
|
+
if (n.type === "icon") {
|
|
77
|
+
if (!n.icon ||
|
|
78
|
+
!["idea", "check", "chart", "people", "shield", "arrow"].includes(n.icon))
|
|
79
|
+
throw new Error("Invalid icon");
|
|
54
80
|
out.icon = n.icon;
|
|
55
81
|
}
|
|
56
82
|
}
|
|
57
83
|
return out;
|
|
58
84
|
}
|
|
59
85
|
const root = visit(value.root, 0);
|
|
60
|
-
if (Object.keys(fields).some(key => !seen.has(key)))
|
|
61
|
-
throw new Error(
|
|
86
|
+
if (Object.keys(fields).some((key) => !seen.has(key)))
|
|
87
|
+
throw new Error("Composition omits content");
|
|
62
88
|
return { version: 1, root };
|
|
63
89
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { SemanticKind } from
|
|
1
|
+
import type { SemanticKind } from "./content.js";
|
|
2
2
|
/** Product scenes describe user intent; recipes describe an executable composition.
|
|
3
3
|
* Multiple scenes deliberately reuse a recipe. Scene count is not design count.
|
|
4
4
|
*/
|
|
5
5
|
export interface CompositionScene {
|
|
6
6
|
id: string;
|
|
7
7
|
kinds: readonly SemanticKind[];
|
|
8
|
-
outputs: readonly (
|
|
8
|
+
outputs: readonly ("presentation" | "social")[];
|
|
9
9
|
requiresAsset?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare const COMPOSITION_SCENES: readonly CompositionScene[];
|
|
@@ -1,47 +1,100 @@
|
|
|
1
|
-
const both = [
|
|
1
|
+
const both = ["presentation", "social"];
|
|
2
2
|
export const COMPOSITION_SCENES = [
|
|
3
|
-
{ id:
|
|
4
|
-
{ id:
|
|
5
|
-
{ id:
|
|
6
|
-
{ id:
|
|
7
|
-
{ id:
|
|
8
|
-
{ id:
|
|
9
|
-
{ id:
|
|
10
|
-
{ id:
|
|
11
|
-
{ id:
|
|
12
|
-
{ id:
|
|
13
|
-
{ id:
|
|
14
|
-
{ id:
|
|
15
|
-
{ id:
|
|
16
|
-
{ id:
|
|
17
|
-
{ id:
|
|
18
|
-
{ id:
|
|
19
|
-
|
|
3
|
+
{ id: "menu-details", kinds: ["parameters"], outputs: both },
|
|
4
|
+
{ id: "travel-details", kinds: ["parameters"], outputs: both },
|
|
5
|
+
{ id: "offer-details", kinds: ["parameters"], outputs: both },
|
|
6
|
+
{ id: "questions", kinds: ["features"], outputs: both },
|
|
7
|
+
{ id: "case-study", kinds: ["features"], outputs: both },
|
|
8
|
+
{ id: "brand-collection", kinds: ["features"], outputs: both },
|
|
9
|
+
{ id: "hero", kinds: ["paragraph"], outputs: both },
|
|
10
|
+
{ id: "chapter", kinds: ["paragraph"], outputs: ["presentation"] },
|
|
11
|
+
{ id: "statement", kinds: ["paragraph"], outputs: both },
|
|
12
|
+
{ id: "closing", kinds: ["paragraph"], outputs: both },
|
|
13
|
+
{ id: "product-launch", kinds: ["features"], outputs: both },
|
|
14
|
+
{ id: "feature-spotlight", kinds: ["features"], outputs: both },
|
|
15
|
+
{ id: "feature-overview", kinds: ["features"], outputs: both },
|
|
16
|
+
{ id: "benefits", kinds: ["features"], outputs: both },
|
|
17
|
+
{ id: "comparison", kinds: ["comparison"], outputs: both },
|
|
18
|
+
{ id: "before-after", kinds: ["comparison"], outputs: both },
|
|
19
|
+
{ id: "plan-options", kinds: ["comparison"], outputs: both },
|
|
20
|
+
{ id: "process", kinds: ["steps"], outputs: both },
|
|
21
|
+
{ id: "roadmap", kinds: ["steps"], outputs: both },
|
|
22
|
+
{ id: "tutorial", kinds: ["steps"], outputs: both },
|
|
23
|
+
{ id: "checklist", kinds: ["features"], outputs: both },
|
|
24
|
+
{ id: "metrics", kinds: ["chart"], outputs: both },
|
|
25
|
+
{ id: "trend", kinds: ["chart"], outputs: both },
|
|
26
|
+
{ id: "results", kinds: ["chart"], outputs: both },
|
|
27
|
+
{ id: "quotation", kinds: ["quote"], outputs: both },
|
|
28
|
+
{ id: "testimonial", kinds: ["quote"], outputs: both },
|
|
29
|
+
{ id: "code-example", kinds: ["code"], outputs: both },
|
|
30
|
+
{ id: "specifications", kinds: ["parameters"], outputs: both },
|
|
31
|
+
{ id: "pricing-details", kinds: ["parameters"], outputs: both },
|
|
32
|
+
{ id: "event-details", kinds: ["parameters"], outputs: both },
|
|
33
|
+
{ id: "recruitment-details", kinds: ["parameters"], outputs: both },
|
|
34
|
+
{
|
|
35
|
+
id: "product-demonstration",
|
|
36
|
+
kinds: ["image"],
|
|
37
|
+
outputs: both,
|
|
38
|
+
requiresAsset: true,
|
|
39
|
+
},
|
|
40
|
+
{ id: "photograph", kinds: ["image"], outputs: both, requiresAsset: true },
|
|
41
|
+
...[
|
|
42
|
+
"banner-hero",
|
|
43
|
+
"product-showcase",
|
|
44
|
+
"editorial-cover",
|
|
45
|
+
"image-campaign",
|
|
46
|
+
].map((id) => ({
|
|
47
|
+
id,
|
|
48
|
+
kinds: ["image"],
|
|
49
|
+
outputs: both,
|
|
50
|
+
requiresAsset: true,
|
|
51
|
+
})),
|
|
20
52
|
];
|
|
21
53
|
export const AUTHORED_COMPOSITIONS = [
|
|
22
|
-
{ id:
|
|
23
|
-
{ id:
|
|
24
|
-
{ id:
|
|
25
|
-
{ id:
|
|
26
|
-
{ id:
|
|
27
|
-
{ id:
|
|
28
|
-
{ id:
|
|
29
|
-
{ id:
|
|
30
|
-
{ id:
|
|
31
|
-
{ id:
|
|
32
|
-
{ id:
|
|
54
|
+
{ id: "feature-focus", kind: "features", min: 1, max: 1 },
|
|
55
|
+
{ id: "feature-bento", kind: "features", min: 2, max: 6 },
|
|
56
|
+
{ id: "feature-index", kind: "features", min: 2, max: 6 },
|
|
57
|
+
{ id: "steps-journey", kind: "steps", min: 2, max: 6 },
|
|
58
|
+
{ id: "steps-ribbon", kind: "steps", min: 2, max: 4 },
|
|
59
|
+
{ id: "comparison-panels", kind: "comparison", min: 2, max: 3 },
|
|
60
|
+
{ id: "quote-editorial", kind: "quote", min: 0, max: 0 },
|
|
61
|
+
{ id: "quote-poster", kind: "quote", min: 0, max: 0 },
|
|
62
|
+
{ id: "code-editorial", kind: "code", min: 0, max: 0 },
|
|
63
|
+
{ id: "parameter-ledger", kind: "parameters", min: 1, max: 8 },
|
|
64
|
+
{ id: "parameter-cards", kind: "parameters", min: 1, max: 4 },
|
|
33
65
|
];
|
|
34
66
|
/** Soft art-direction preferences; capacity and measured quality always remain mandatory. */
|
|
35
67
|
export function sceneRecipePreference(scene, recipeId) {
|
|
36
68
|
const preferred = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
"banner-hero": ["authored-image-banner"],
|
|
70
|
+
"product-showcase": ["authored-image-split"],
|
|
71
|
+
"editorial-cover": ["authored-image-editorial"],
|
|
72
|
+
"image-campaign": ["authored-image-campaign"],
|
|
73
|
+
hero: ["cover", "semantic-hero-split"],
|
|
74
|
+
chapter: ["section", "semantic-hero-editorial"],
|
|
75
|
+
statement: ["statement", "semantic-hero-stack"],
|
|
76
|
+
closing: ["closing"],
|
|
77
|
+
"product-launch": ["authored-feature-bento"],
|
|
78
|
+
"feature-spotlight": ["authored-feature-focus"],
|
|
79
|
+
"feature-overview": ["authored-feature-bento", "three-column"],
|
|
80
|
+
benefits: ["authored-feature-index"],
|
|
81
|
+
comparison: ["authored-comparison-panels"],
|
|
82
|
+
"before-after": ["before-after", "semantic-comparison-split"],
|
|
83
|
+
"plan-options": ["authored-comparison-panels"],
|
|
84
|
+
process: ["authored-steps-ribbon"],
|
|
85
|
+
roadmap: ["authored-steps-ribbon", "roadmap"],
|
|
86
|
+
tutorial: ["authored-steps-journey"],
|
|
87
|
+
checklist: ["authored-feature-index", "checklist"],
|
|
88
|
+
quotation: ["authored-quote-editorial"],
|
|
89
|
+
testimonial: ["authored-quote-poster"],
|
|
90
|
+
"code-example": ["authored-code-editorial"],
|
|
91
|
+
specifications: ["authored-parameter-ledger"],
|
|
92
|
+
"pricing-details": ["authored-parameter-cards"],
|
|
93
|
+
"event-details": ["authored-parameter-cards"],
|
|
94
|
+
"recruitment-details": ["authored-parameter-ledger"],
|
|
45
95
|
};
|
|
46
|
-
return scene &&
|
|
96
|
+
return scene &&
|
|
97
|
+
preferred[scene]?.some((id) => recipeId === id || recipeId.startsWith(`${id}--`))
|
|
98
|
+
? 6
|
|
99
|
+
: 0;
|
|
47
100
|
}
|
package/dist/content.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Source-backed content, independent of layout and editor primitives. */
|
|
2
|
-
export type SourceKind =
|
|
2
|
+
export type SourceKind = "text" | "code" | "image-reference";
|
|
3
3
|
export interface SourceSegment {
|
|
4
4
|
id: string;
|
|
5
5
|
kind: SourceKind;
|
|
@@ -17,73 +17,108 @@ export interface Evidence {
|
|
|
17
17
|
sourceId: string;
|
|
18
18
|
quote?: string;
|
|
19
19
|
}
|
|
20
|
-
export type SemanticKind =
|
|
20
|
+
export type SemanticKind = "paragraph" | "steps" | "features" | "comparison" | "code" | "parameters" | "quote" | "chart" | "image";
|
|
21
|
+
export interface ComparisonColumn {
|
|
22
|
+
title: string;
|
|
23
|
+
points: string[];
|
|
24
|
+
price?: string;
|
|
25
|
+
values?: string[];
|
|
26
|
+
statuses?: ("included" | "excluded" | "partial" | "unknown")[];
|
|
27
|
+
}
|
|
21
28
|
interface BlockBase {
|
|
22
29
|
id: string;
|
|
23
30
|
evidence: Evidence[];
|
|
24
31
|
}
|
|
25
32
|
export type ContentBlock = BlockBase & ({
|
|
26
|
-
kind:
|
|
33
|
+
kind: "paragraph";
|
|
27
34
|
text: string;
|
|
28
35
|
heading?: string;
|
|
29
36
|
level?: 2 | 3;
|
|
30
37
|
} | {
|
|
31
|
-
kind:
|
|
38
|
+
kind: "steps" | "features";
|
|
32
39
|
entries: {
|
|
33
40
|
title: string;
|
|
34
41
|
description: string;
|
|
42
|
+
icon?: string;
|
|
43
|
+
illustration?: string;
|
|
35
44
|
}[];
|
|
36
45
|
} | {
|
|
37
|
-
kind:
|
|
38
|
-
columns:
|
|
39
|
-
title: string;
|
|
40
|
-
points: string[];
|
|
41
|
-
}[];
|
|
46
|
+
kind: "comparison";
|
|
47
|
+
columns: ComparisonColumn[];
|
|
42
48
|
} | {
|
|
43
|
-
kind:
|
|
49
|
+
kind: "code";
|
|
44
50
|
sourceId: string;
|
|
45
51
|
language: string;
|
|
46
52
|
code: string;
|
|
47
53
|
} | {
|
|
48
|
-
kind:
|
|
54
|
+
kind: "parameters";
|
|
49
55
|
rows: {
|
|
50
56
|
name: string;
|
|
51
57
|
value: string;
|
|
52
58
|
description: string;
|
|
53
59
|
}[];
|
|
54
60
|
} | {
|
|
55
|
-
kind:
|
|
61
|
+
kind: "quote";
|
|
56
62
|
text: string;
|
|
57
63
|
attribution: string;
|
|
64
|
+
avatarAssetId?: string;
|
|
65
|
+
role?: string;
|
|
58
66
|
} | {
|
|
59
|
-
kind:
|
|
60
|
-
chart:
|
|
67
|
+
kind: "chart";
|
|
68
|
+
chart: "bar" | "line";
|
|
61
69
|
unit: string;
|
|
62
70
|
values: {
|
|
63
71
|
label: string;
|
|
64
72
|
value: number;
|
|
65
73
|
}[];
|
|
66
74
|
} | {
|
|
67
|
-
kind:
|
|
75
|
+
kind: "image";
|
|
68
76
|
sourceId: string;
|
|
69
77
|
alt: string;
|
|
70
|
-
treatment:
|
|
71
|
-
status:
|
|
78
|
+
treatment: "screenshot" | "photograph" | "diagram";
|
|
79
|
+
status: "missing" | "available";
|
|
72
80
|
assetId?: string;
|
|
73
81
|
});
|
|
82
|
+
export interface PublicationInfo {
|
|
83
|
+
tags?: string[];
|
|
84
|
+
author?: {
|
|
85
|
+
name: string;
|
|
86
|
+
role?: string;
|
|
87
|
+
avatarAssetId?: string;
|
|
88
|
+
};
|
|
89
|
+
metrics?: {
|
|
90
|
+
label: string;
|
|
91
|
+
value: string;
|
|
92
|
+
}[];
|
|
93
|
+
date?: string;
|
|
94
|
+
source?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface BookendInfo {
|
|
97
|
+
organization?: string;
|
|
98
|
+
presenter?: string;
|
|
99
|
+
role?: string;
|
|
100
|
+
date?: string;
|
|
101
|
+
contact?: string;
|
|
102
|
+
logoAssetId?: string;
|
|
103
|
+
}
|
|
74
104
|
export interface ContentPage {
|
|
105
|
+
/** Optional decorative Unicode emoji chosen explicitly by the author/model. */
|
|
106
|
+
emoji?: string;
|
|
75
107
|
id: string;
|
|
76
108
|
title: string;
|
|
77
|
-
|
|
109
|
+
titleEmphasis?: string[];
|
|
110
|
+
purpose: "cover" | "explain" | "instruct" | "compare" | "evidence" | "conclude";
|
|
78
111
|
evidence: Evidence[];
|
|
79
112
|
blocks: ContentBlock[];
|
|
113
|
+
publication?: PublicationInfo;
|
|
114
|
+
bookend?: BookendInfo;
|
|
80
115
|
caption?: string;
|
|
81
116
|
notes?: string;
|
|
82
117
|
scene?: string;
|
|
83
118
|
}
|
|
84
119
|
export interface ContentPlan {
|
|
85
120
|
version: 1;
|
|
86
|
-
kind:
|
|
121
|
+
kind: "presentation" | "social";
|
|
87
122
|
title: string;
|
|
88
123
|
pages: ContentPage[];
|
|
89
124
|
}
|
|
@@ -95,7 +130,7 @@ export declare class ContentContractError extends Error {
|
|
|
95
130
|
/** Lossless ranges. File-name placeholders are references, never fabricated assets. */
|
|
96
131
|
export declare function parseSource(input: string): SourceDocument;
|
|
97
132
|
/** Validate untrusted AI output; strip unknown properties and never trust generated code or asset URLs. */
|
|
98
|
-
export declare function validateContentPlan(raw: unknown, source: SourceDocument, expectedKind?: ContentPlan[
|
|
133
|
+
export declare function validateContentPlan(raw: unknown, source: SourceDocument, expectedKind?: ContentPlan["kind"]): ContentPlan;
|
|
99
134
|
export declare function contentBlockText(block: ContentBlock): string[];
|
|
100
135
|
/** Validate persisted/editor-authored content without reasserting that user edits came from AI evidence. */
|
|
101
136
|
export declare function validateEditableContentPage(raw: unknown, { allowIncomplete }?: {
|