@neatico/visual-core 0.14.26 → 0.14.64

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.
Files changed (43) hide show
  1. package/dist/candidateRanking.d.ts +31 -0
  2. package/dist/candidateRanking.js +55 -0
  3. package/dist/cjs/candidateRanking.js +61 -0
  4. package/dist/cjs/compositionNumbers.js +111 -0
  5. package/dist/cjs/compositionUseCases.js +145 -0
  6. package/dist/cjs/content.js +46 -10
  7. package/dist/cjs/contentBrief.js +82 -0
  8. package/dist/cjs/designSystem.js +80 -8
  9. package/dist/cjs/fontCatalog.js +7 -1
  10. package/dist/cjs/formalCompositions.js +120 -0
  11. package/dist/cjs/index.js +13 -0
  12. package/dist/cjs/nativeSocialParameters.js +145 -0
  13. package/dist/cjs/pagination.js +14 -5
  14. package/dist/cjs/parameterDefaults.js +20 -0
  15. package/dist/cjs/recipeParameters.js +304 -0
  16. package/dist/cjs/styleManifest.js +107 -0
  17. package/dist/cjs/textPresentation.js +68 -0
  18. package/dist/compositionNumbers.d.ts +4 -0
  19. package/dist/compositionNumbers.js +108 -0
  20. package/dist/compositionUseCases.d.ts +1 -0
  21. package/dist/compositionUseCases.js +142 -0
  22. package/dist/content.d.ts +10 -0
  23. package/dist/content.js +46 -10
  24. package/dist/contentBrief.d.ts +16 -0
  25. package/dist/contentBrief.js +78 -0
  26. package/dist/designSystem.js +80 -8
  27. package/dist/fontCatalog.js +7 -1
  28. package/dist/formalCompositions.d.ts +26 -0
  29. package/dist/formalCompositions.js +116 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +10 -0
  32. package/dist/nativeSocialParameters.d.ts +38 -0
  33. package/dist/nativeSocialParameters.js +139 -0
  34. package/dist/pagination.js +14 -5
  35. package/dist/parameterDefaults.d.ts +2 -0
  36. package/dist/parameterDefaults.js +17 -0
  37. package/dist/recipeParameters.d.ts +34 -0
  38. package/dist/recipeParameters.js +300 -0
  39. package/dist/styleManifest.d.ts +20 -0
  40. package/dist/styleManifest.js +103 -0
  41. package/dist/textPresentation.d.ts +29 -0
  42. package/dist/textPresentation.js +64 -0
  43. package/package.json +1 -1
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECIPE_PARAMETER_CONTRACTS = void 0;
4
+ exports.recipeParameterContract = recipeParameterContract;
5
+ const nativeSocialParameters_js_1 = require("./nativeSocialParameters.js");
6
+ const textPresentation_js_1 = require("./textPresentation.js");
7
+ const designSystem_js_1 = require("./designSystem.js");
8
+ const text = (description, maxLength = 600) => ({
9
+ type: "string",
10
+ description,
11
+ maxLength,
12
+ });
13
+ const object = (properties, required) => ({
14
+ type: "object",
15
+ additionalProperties: false,
16
+ properties,
17
+ required,
18
+ });
19
+ const array = (items, minItems, maxItems) => ({ type: "array", items, minItems, maxItems });
20
+ const evidence = array(object({
21
+ sourceId: text("Existing source segment ID.", 160),
22
+ quote: text("Exact source quotation.", 2000),
23
+ }, ["sourceId"]), 1, 32);
24
+ const base = (kind, properties, required) => object({
25
+ id: text("Stable block ID, unique within this page.", 80),
26
+ kind: { const: kind },
27
+ evidence,
28
+ ...properties,
29
+ }, ["id", "kind", "evidence", ...required]);
30
+ const descriptions = {
31
+ paragraph: "Explain a narrative idea with readable prose and optional section headings.",
32
+ features: "Present parallel capabilities or a list of distinct ideas without implying a sequence.",
33
+ steps: "Explain ordered actions or a chronological sequence supported by the source.",
34
+ comparison: "Compare supplied alternatives with aligned criteria; never invent a competing alternative.",
35
+ parameters: "Present exact specifications, logistics, identifiers, dates, prices, or named facts.",
36
+ quote: "Emphasize a verbatim quotation with its supplied attribution.",
37
+ chart: "Explain a comparable numeric series with source-backed values and a shared unit.",
38
+ code: "Display exact source code without rewriting identifiers, literals, or line breaks.",
39
+ image: "Arrange a real, previously supplied image asset with its source reference and focal subject.",
40
+ };
41
+ function blockSchema(kind, maximum) {
42
+ const count = Math.max(1, Math.min(12, maximum || 12));
43
+ switch (kind) {
44
+ case "paragraph":
45
+ return base(kind, {
46
+ text: text("Source-faithful prose.", 2000),
47
+ heading: text("Optional source-supported heading.", 120),
48
+ level: { enum: [2, 3] },
49
+ }, ["text"]);
50
+ case "features":
51
+ case "steps":
52
+ return base(kind, {
53
+ entries: array(object({
54
+ title: text("Concise item label without an ordinal prefix.", 120),
55
+ description: text("Source-faithful explanation; omit filler.", 600),
56
+ }, ["title", "description"]), 1, count),
57
+ }, ["entries"]);
58
+ case "comparison":
59
+ return base(kind, {
60
+ columns: array(object({
61
+ title: text("A supplied alternative.", 120),
62
+ points: array(text("Source-backed comparison criterion.", 600), 1, 8),
63
+ price: text("Exact source price, only when supplied.", 120),
64
+ values: array(text("Exact value aligned with points.", 160), 1, 8),
65
+ statuses: array({ enum: ["included", "excluded", "partial", "unknown"] }, 1, 8),
66
+ }, ["title", "points"]), 2, Math.max(2, Math.min(4, count))),
67
+ }, ["columns"]);
68
+ case "parameters":
69
+ return base(kind, {
70
+ rows: array(object({
71
+ name: text("Fact name.", 120),
72
+ value: text("Exact source value.", 300),
73
+ description: text("Source-supported qualification, otherwise an empty string.", 600),
74
+ }, ["name", "value", "description"]), 1, count),
75
+ }, ["rows"]);
76
+ case "quote":
77
+ return base(kind, {
78
+ text: text("Verbatim source quotation.", 1200),
79
+ attribution: text("Exact source attribution.", 160),
80
+ role: text("Role only when explicitly supplied.", 160),
81
+ avatarAssetId: text("Existing supplied portrait asset ID; never invent a person or image.", 160),
82
+ }, ["text", "attribution"]);
83
+ case "chart":
84
+ return base(kind, {
85
+ chart: { enum: ["bar", "line"] },
86
+ unit: text("Exact source unit.", 80),
87
+ values: array(object({
88
+ label: text("Exact source label.", 120),
89
+ value: { type: "number" },
90
+ }, ["label", "value"]), 2, 12),
91
+ }, ["chart", "unit", "values"]);
92
+ case "code":
93
+ return base(kind, {
94
+ sourceId: text("Existing code source ID; code remains unchanged.", 160),
95
+ language: text("Source code language.", 80),
96
+ code: text("Exact source code.", 20000),
97
+ }, ["sourceId", "language", "code"]);
98
+ case "image":
99
+ return base(kind, {
100
+ sourceId: text("Existing image source ID.", 160),
101
+ assetId: text("Existing asset ID; no generated URLs.", 160),
102
+ alt: text("Subject and purpose of the supplied image.", 600),
103
+ treatment: { enum: ["screenshot", "photograph", "diagram"] },
104
+ status: { enum: ["available", "missing"] },
105
+ }, ["sourceId", "alt", "treatment", "status"]);
106
+ }
107
+ }
108
+ /** Each authored renderer receives a fixed block sequence and bounded copy. */
109
+ function fixedBlocks(recipe) {
110
+ const id = recipe.id;
111
+ let kinds;
112
+ if (id.endsWith("ledger-chart"))
113
+ kinds = ["parameters", "chart"];
114
+ else if (id.endsWith("detail-sidebar"))
115
+ kinds = ["paragraph", "parameters"];
116
+ else if (id.endsWith("technical-log"))
117
+ kinds = ["paragraph", "code"];
118
+ else if (id.endsWith("architecture-stack"))
119
+ kinds = ["steps", "paragraph"];
120
+ else if (["authored-mechanism-flow", "authored-reference-capability-map"].includes(id))
121
+ kinds = ["features", "paragraph"];
122
+ else if (id === "authored-founder-timeline")
123
+ kinds = ["steps", "paragraph"];
124
+ else if (id === "authored-founder-comparison")
125
+ kinds = ["comparison", "paragraph"];
126
+ else if (id === "semantic-hero-editorial")
127
+ kinds = ["paragraph", "paragraph", "paragraph", "image"];
128
+ else if (id.endsWith("speaker-lineup"))
129
+ kinds = ["parameters", "quote", "quote", "quote"];
130
+ else if (id.endsWith("speaker-spotlight"))
131
+ kinds = ["parameters", "quote"];
132
+ else if (id === "authored-reference-story")
133
+ kinds = ["image", "paragraph", "features"];
134
+ else if (id === "authored-reference-photo")
135
+ kinds = ["paragraph", "image"];
136
+ else if (id === "authored-image-triptych")
137
+ kinds = ["paragraph", "features", "paragraph", "image"];
138
+ else if (recipe.requiresImage && recipe.accepts.includes("image"))
139
+ kinds = ["image"];
140
+ else if (recipe.family === "mixed-stack")
141
+ kinds = ["features"];
142
+ else
143
+ kinds = [
144
+ recipe.accepts.find((kind) => kind !== "paragraph" && kind !== "image") ||
145
+ "paragraph",
146
+ ];
147
+ if (id.startsWith("bookend-") || recipe.family.startsWith("prose-"))
148
+ kinds = ["paragraph"];
149
+ const items = kinds.map((kind) => {
150
+ const schema = JSON.parse(JSON.stringify(blockSchema(kind, recipe.maxEntries)));
151
+ const fields = schema.properties;
152
+ const count = Math.max(recipe.minEntries || 1, Math.min(3, recipe.maxEntries || 3));
153
+ if (kind === "paragraph")
154
+ fields.text.maxLength =
155
+ ["authored-reference-detail-sidebar", "authored-reference-diagonal", "authored-prose-inset"].includes(id) ? 160 : 120;
156
+ if (kind === "features" || kind === "steps") {
157
+ const entries = fields.entries;
158
+ entries.minItems = id === "authored-reference-story" ? 2 : count;
159
+ entries.maxItems = count;
160
+ entries.items.properties.illustration = {
161
+ type: "string",
162
+ enum: [...nativeSocialParameters_js_1.COMPOSITION_ILLUSTRATIONS, "symbol"],
163
+ default: "structure",
164
+ description: "Editable native illustration for the item. Choose the visual concept that explains its meaning. This field controls a graphic, not prose.",
165
+ };
166
+ entries.items.required.push("illustration");
167
+ entries.items.properties.icon = { type: "string", enum: ["BsJournalText", "BsPersonCheck", "BsShare", "BsPeople", "BsSearch", "BsShieldCheck", "BsBarChart", "BsCodeSlash", "BsSignpostSplit", "BsCheck2Circle", "BsFolder2Open", "BsGrid1X2"], description: "Optional authored icon variant; illustration remains the primary visual concept." };
168
+ entries.items.properties.title.maxLength = 28;
169
+ entries.items.properties.description.maxLength =
170
+ id === "authored-feature-focus" ? 160 : 48;
171
+ if (/(?:reference-faq|editorial-faq)$/.test(id) || id === "faq")
172
+ entries.items.properties.title.pattern = "^.+[??]$";
173
+ }
174
+ if (kind === "parameters") {
175
+ const rows = id.endsWith("ledger-chart") ||
176
+ id.endsWith("detail-sidebar") ||
177
+ id.includes("speaker-")
178
+ ? 2
179
+ : count;
180
+ fields.rows.minItems = id.endsWith("speaker-lineup") ? 1 : id === "pricing" ? 2 : rows;
181
+ fields.rows.maxItems = rows;
182
+ fields.rows.items.properties.name.maxLength = 20;
183
+ fields.rows.items.properties.value.maxLength = 24;
184
+ fields.rows.items.properties.description.maxLength = 64;
185
+ if (id.includes("speaker-"))
186
+ fields.rows.items.properties.description = { const: "" };
187
+ }
188
+ if (kind === "comparison") {
189
+ fields.columns.minItems = 2;
190
+ fields.columns.maxItems = 2;
191
+ const column = fields.columns.items.properties;
192
+ column.title.maxLength = 20;
193
+ column.points.minItems = id === "authored-founder-comparison" ? 1 : 2;
194
+ column.points.maxItems = 3;
195
+ column.points.items.maxLength = id === "authored-founder-comparison" ? 64 : 40;
196
+ column.values.maxItems = 3;
197
+ column.values.items.maxLength = 16;
198
+ column.values.minItems = 0;
199
+ column.statuses.minItems = 0;
200
+ column.statuses.maxItems = 3;
201
+ column.price.maxLength = 24;
202
+ }
203
+ if (kind === "quote") {
204
+ fields.text.maxLength = id.includes("speaker-") ? 48 : 160;
205
+ fields.attribution.maxLength = 32;
206
+ if (id.includes("speaker-")) {
207
+ fields.avatarAssetId.description =
208
+ "An existing portrait asset ID, or neatico-editorial-placeholder for an original source-attributed illustration.";
209
+ schema.required.push("avatarAssetId");
210
+ }
211
+ }
212
+ if (kind === "code") {
213
+ fields.code.maxLength = 400;
214
+ fields.code.description =
215
+ "A concise exact source excerpt, or a clearly labeled illustrative example when no source code exists. At most eight lines and 56 columns per line.";
216
+ fields.code.pattern = String.raw `^(?:[^\n]{0,56}\n){0,7}[^\n]{0,56}$`;
217
+ }
218
+ if (kind === "image")
219
+ fields.alt.maxLength = 80;
220
+ if (kind === "chart") {
221
+ const count = id.endsWith("ledger-chart") ? 3 : 4;
222
+ fields.values.minItems = Math.min(3, count);
223
+ fields.values.maxItems = count;
224
+ fields.values.items.properties.label.maxLength = 20;
225
+ fields.unit.maxLength = 24;
226
+ if (/trend/.test(id))
227
+ fields.chart = { const: "line" };
228
+ if (/bars|ledger-chart/.test(id))
229
+ fields.chart = { const: "bar" };
230
+ if (/donut|reference-share|reference-metrics/.test(id)) {
231
+ fields.chart = { const: "bar" };
232
+ fields.unit = { type: "string", maxLength: 24, pattern: "%", description: "Percentage unit, optionally qualified as illustrative or simulated." };
233
+ fields.values.description =
234
+ "Nonnegative shares totaling exactly 100. Use labeled illustrative shares when the input has no comparable numbers.";
235
+ fields.values.items.properties.value.minimum = 0;
236
+ fields.values.items.properties.value.maximum = 100;
237
+ }
238
+ }
239
+ return schema;
240
+ });
241
+ return {
242
+ type: "array",
243
+ items,
244
+ minItems: items.length,
245
+ maxItems: items.length,
246
+ additionalItems: false,
247
+ description: "Fixed ordered parameters for this renderer. Return every block exactly once in this order; do not add explanatory blocks.",
248
+ };
249
+ }
250
+ /** Fixed renderer parameters, not executable layout trees. The complete schema
251
+ * travels with each formal generation batch; summaries are scheduling aids only.
252
+ */
253
+ function recipeParameterContract(recipe, supportedKinds = [recipe.kind]) {
254
+ return {
255
+ id: recipe.id,
256
+ schemaVersion: 1,
257
+ supportedKinds,
258
+ semanticDescription: recipe.accepts
259
+ .map((kind) => descriptions[kind])
260
+ .join(" "),
261
+ scenes: recipe.scenes || [],
262
+ rendererKey: `${recipe.renderer}:${recipe.id}`,
263
+ capacity: {
264
+ minEntries: recipe.minEntries,
265
+ maxEntries: recipe.maxEntries,
266
+ requiresImage: recipe.requiresImage,
267
+ },
268
+ parameterSchema: object({
269
+ title: {
270
+ ...text("Audience-facing headline preserving the original topic.", 64),
271
+ minLength: 1,
272
+ },
273
+ blocks: fixedBlocks(recipe),
274
+ textPresentation: (0, textPresentation_js_1.textPresentationSchema)(),
275
+ }, ["title", "blocks"]),
276
+ importance: { title: 3, blocks: 7 },
277
+ generationPolicy: {
278
+ title: ["user-fact", "derived", "creative"],
279
+ blocks: ["user-fact", "derived", "creative", "simulated"],
280
+ assets: ["existing-reference-only"],
281
+ },
282
+ fallbackPolicy: {
283
+ missingContent: "derive-or-labeled-example",
284
+ missingAsset: "search-or-native-placeholder",
285
+ omitCandidate: false,
286
+ },
287
+ themeBindings: [
288
+ "palette.background",
289
+ "palette.surface",
290
+ "palette.ink",
291
+ "palette.muted",
292
+ "palette.accent",
293
+ "palette.line",
294
+ "typography.heading",
295
+ "typography.body",
296
+ "typography.code",
297
+ "geometry",
298
+ ],
299
+ };
300
+ }
301
+ /** Shared recipes are one part of the formal catalog; host-specific renderer
302
+ * definitions register through the same contract constructor during migration.
303
+ */
304
+ exports.RECIPE_PARAMETER_CONTRACTS = designSystem_js_1.DESIGN_RECIPES.filter((recipe, index, recipes) => recipes.findIndex((other) => other.id === recipe.id) === index).map((recipe) => recipeParameterContract(recipe, designSystem_js_1.DESIGN_RECIPES.filter((other) => other.id === recipe.id).map((other) => other.kind)));
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStyleManifest = createStyleManifest;
4
+ exports.validateStyleManifest = validateStyleManifest;
5
+ const designSystem_js_1 = require("./designSystem.js");
6
+ const fontCatalog_js_1 = require("./fontCatalog.js");
7
+ function createStyleManifest(themeId, copy = "", imagery) {
8
+ const theme = (0, designSystem_js_1.designTheme)(themeId);
9
+ if (!designSystem_js_1.DESIGN_THEMES.some((candidate) => candidate.id === themeId) &&
10
+ !["paper", "midnight", "ocean", "plum"].includes(themeId))
11
+ throw new Error("STYLE_UNKNOWN_THEME");
12
+ const manifest = {
13
+ version: 1,
14
+ themeId: theme.id,
15
+ themeVersion: 1,
16
+ theme: {
17
+ ...theme,
18
+ palette: { ...theme.palette },
19
+ typography: { ...theme.typography },
20
+ geometry: { ...theme.geometry },
21
+ },
22
+ fonts: {
23
+ heading: (0, fontCatalog_js_1.languageFont)(copy, theme.typography.heading),
24
+ body: (0, fontCatalog_js_1.languageFont)(copy, theme.typography.body),
25
+ code: theme.typography.code === "monospace"
26
+ ? "JetBrains Mono"
27
+ : theme.typography.code,
28
+ },
29
+ imagery: {
30
+ medium: "native",
31
+ referenceAssetIds: [],
32
+ direction: "",
33
+ ...imagery,
34
+ },
35
+ };
36
+ return validateStyleManifest(manifest);
37
+ }
38
+ function validateStyleManifest(value) {
39
+ const manifest = value;
40
+ const fail = () => {
41
+ throw new Error("STYLE_INVALID_MANIFEST");
42
+ };
43
+ if (!manifest ||
44
+ manifest.version !== 1 ||
45
+ manifest.themeVersion !== 1 ||
46
+ typeof manifest.themeId !== "string" ||
47
+ !manifest.theme ||
48
+ manifest.theme.id !== manifest.themeId ||
49
+ !designSystem_js_1.DESIGN_THEMES.some((theme) => theme.id === manifest.themeId))
50
+ return fail();
51
+ const { palette, typography, geometry } = manifest.theme;
52
+ if (!palette ||
53
+ ["background", "surface", "ink", "muted", "accent", "line"].some((key) => !/^#[\da-f]{6}$/i.test(palette[key] || "")))
54
+ return fail();
55
+ if (!typography ||
56
+ ["heading", "body", "code"].some((key) => typeof typography[key] !==
57
+ "string") ||
58
+ !Number.isInteger(typography.headingWeight) ||
59
+ typography.headingWeight < 100 ||
60
+ typography.headingWeight > 900)
61
+ return fail();
62
+ if (!geometry ||
63
+ !Number.isFinite(geometry.radius) ||
64
+ geometry.radius < 0 ||
65
+ geometry.radius > 100 ||
66
+ !Number.isFinite(geometry.spacing) ||
67
+ geometry.spacing <= 0 ||
68
+ geometry.spacing > 4 ||
69
+ !["none", "hairline", "accent"].includes(geometry.rule))
70
+ return fail();
71
+ if (!manifest.fonts ||
72
+ Object.keys(manifest.fonts).length !== 3 ||
73
+ ["heading", "body", "code"].some((key) => {
74
+ const font = manifest.fonts[key];
75
+ return typeof font !== "string" || !font.trim() || font.length > 100;
76
+ }))
77
+ return fail();
78
+ if (!manifest.imagery ||
79
+ !["native", "photography", "illustration", "3d"].includes(manifest.imagery.medium) ||
80
+ typeof manifest.imagery.direction !== "string" ||
81
+ manifest.imagery.direction.length > 2000 ||
82
+ !Array.isArray(manifest.imagery.referenceAssetIds) ||
83
+ manifest.imagery.referenceAssetIds.length > 16 ||
84
+ manifest.imagery.referenceAssetIds.some((id) => typeof id !== "string" || !id || id.length > 160))
85
+ return fail();
86
+ return {
87
+ version: 1,
88
+ themeId: manifest.themeId,
89
+ themeVersion: 1,
90
+ theme: {
91
+ id: manifest.themeId,
92
+ palette: { ...palette },
93
+ typography: { ...typography },
94
+ geometry: { ...geometry },
95
+ },
96
+ fonts: {
97
+ heading: manifest.fonts.heading,
98
+ body: manifest.fonts.body,
99
+ code: manifest.fonts.code,
100
+ },
101
+ imagery: {
102
+ medium: manifest.imagery.medium,
103
+ direction: manifest.imagery.direction,
104
+ referenceAssetIds: [...manifest.imagery.referenceAssetIds],
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.textPresentationSchema = textPresentationSchema;
4
+ exports.validateTextPresentations = validateTextPresentations;
5
+ const object = (properties) => ({
6
+ type: 'object', additionalProperties: false, properties, required: Object.keys(properties),
7
+ });
8
+ function textPresentationSchema() {
9
+ return {
10
+ type: 'array', minItems: 0, maxItems: 8, default: [],
11
+ description: 'Optional typography for visible text fields, including title, body, item descriptions and quotations. Use a few deliberate contrasts within the shared suite. Every line must preserve the original field text and word boundaries. Empty uses the authored typography. Never style code, evidence, IDs or asset metadata.',
12
+ items: object({
13
+ path: { type: 'string', minLength: 1, maxLength: 120, description: 'Exact dot path of the visible text field, such as body, items.0.description or blocks.0.text.' },
14
+ fit: { type: 'string', enum: ['natural', 'equal-width'], description: 'Equal-width adjusts each line independently within readable size limits; it never stretches glyphs.' },
15
+ lines: { type: 'array', minItems: 1, maxItems: 8, items: object({
16
+ text: { type: 'string', minLength: 1, maxLength: 220, description: 'One complete phrase copied exactly from the target field. All lines together must preserve that entire field.' },
17
+ font: { type: 'string', enum: ['display', 'body', 'mono'] },
18
+ scale: { type: 'number', minimum: 0.8, maximum: 1.35 },
19
+ tone: { type: 'string', enum: ['ink', 'accent', 'secondary'] },
20
+ decoration: { type: 'string', enum: ['none', 'marker', 'underline'] },
21
+ italic: { type: 'boolean' },
22
+ }) },
23
+ }),
24
+ };
25
+ }
26
+ const normalize = (text) => text.normalize('NFC').replace(/\s+/gu, ' ').trim();
27
+ const word = (character) => /[\p{Script=Latin}\p{Script=Cyrillic}\p{Script=Greek}\p{Number}]/u.test(character);
28
+ /** Validate exact source reconstruction; script-aware boundaries permit CJK breaks. */
29
+ function validateTextPresentations(value, content) {
30
+ if (value === undefined)
31
+ return [];
32
+ if (!Array.isArray(value) || value.length > 8)
33
+ throw new Error('TEXT_PRESENTATION_INVALID');
34
+ const paths = new Set();
35
+ for (const entry of value) {
36
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
37
+ throw new Error('TEXT_PRESENTATION_INVALID');
38
+ if (Object.keys(entry).some(key => !['path', 'fit', 'lines'].includes(key)))
39
+ throw new Error('TEXT_PRESENTATION_INVALID');
40
+ if (typeof entry.path !== 'string' || !/^(?:title|body|eyebrow|cta|value|attribution|(?:items|blocks)\.\d+\.(?:(?:entries|rows|columns)\.\d+\.)?(?:title|heading|text|description|name|value|attribution|points\.\d+))$/.test(entry.path) || paths.has(entry.path))
41
+ throw new Error('TEXT_PRESENTATION_PATH');
42
+ paths.add(entry.path);
43
+ const copy = entry.path.split('.').reduce((node, key) => node?.[key], content);
44
+ if (typeof copy !== 'string' || !copy.trim())
45
+ throw new Error('TEXT_PRESENTATION_PATH');
46
+ if (!['natural', 'equal-width'].includes(entry.fit) || !Array.isArray(entry.lines) || entry.lines.length < 1 || entry.lines.length > 8)
47
+ throw new Error('TEXT_PRESENTATION_INVALID');
48
+ const source = normalize(copy);
49
+ let cursor = 0;
50
+ for (const line of entry.lines) {
51
+ if (!line || typeof line !== 'object' || Array.isArray(line) || Object.keys(line).some(key => !['text', 'font', 'scale', 'tone', 'decoration', 'italic'].includes(key)))
52
+ throw new Error('TEXT_PRESENTATION_INVALID');
53
+ if (typeof line.text !== 'string' || !normalize(line.text) || Array.from(line.text).length > 220 || !['display', 'body', 'mono'].includes(line.font) || !Number.isFinite(line.scale) || line.scale < 0.8 || line.scale > 1.35 || !['ink', 'accent', 'secondary'].includes(line.tone) || !['none', 'marker', 'underline'].includes(line.decoration) || typeof line.italic !== 'boolean')
54
+ throw new Error('TEXT_PRESENTATION_INVALID');
55
+ const phrase = normalize(line.text);
56
+ if (source[cursor] === ' ')
57
+ cursor++;
58
+ if (!source.startsWith(phrase, cursor))
59
+ throw new Error('TEXT_PRESENTATION_CONTENT');
60
+ if (cursor && source[cursor - 1] !== ' ' && word(source[cursor - 1]) && word(phrase[0]))
61
+ throw new Error('TEXT_PRESENTATION_WORD_BOUNDARY');
62
+ cursor += phrase.length;
63
+ }
64
+ if (cursor !== source.length)
65
+ throw new Error('TEXT_PRESENTATION_CONTENT');
66
+ }
67
+ return value;
68
+ }
@@ -0,0 +1,4 @@
1
+ /** Stable public display numbers, independent of runtime registration order. */
2
+ export declare const SEMANTIC_DISPLAY_NUMBERS: Readonly<Record<string, string>>;
3
+ /** Social numbers follow the established formal gallery, not registration order. */
4
+ export declare const SOCIAL_DISPLAY_NUMBERS: Readonly<Record<string, string>>;
@@ -0,0 +1,108 @@
1
+ /** Stable public display numbers, independent of runtime registration order. */
2
+ export const SEMANTIC_DISPLAY_NUMBERS = {
3
+ "semantic:presentation:bookend-cover-editorial": "M001",
4
+ "semantic:presentation:bookend-cover-panel": "M002",
5
+ "semantic:presentation:bookend-cover-split": "M003",
6
+ "semantic:presentation:bookend-closing-editorial": "M004",
7
+ "semantic:presentation:bookend-closing-panel": "M005",
8
+ "semantic:presentation:bookend-closing-split": "M006",
9
+ "semantic:presentation:brand-collection-grid": "M007",
10
+ "semantic:social:time-notice": "M099",
11
+ "semantic:social:ticket": "M100",
12
+ "semantic:social:webinar": "M101",
13
+ "semantic:social:pricing": "M102",
14
+ "semantic:social:offer": "M103",
15
+ "semantic:social:hiring": "M104",
16
+ "semantic:social:community": "M105",
17
+ "semantic:social:menu": "M106",
18
+ "semantic:social:itinerary": "M107",
19
+ "semantic:social:schedule": "M108",
20
+ "semantic:presentation:authored-plan-cards": "M109",
21
+ "semantic:presentation:authored-image-banner": "M009",
22
+ "semantic:presentation:authored-image-split": "M010",
23
+ "semantic:presentation:authored-image-editorial": "M011",
24
+ "semantic:presentation:authored-image-campaign": "M012",
25
+ "semantic:presentation:authored-image-inset": "M013",
26
+ "semantic:presentation:authored-prose-inset": "M014",
27
+ "semantic:presentation:authored-feature-focus": "M015",
28
+ "semantic:presentation:authored-feature-bento": "M016",
29
+ "semantic:presentation:authored-feature-index": "M017",
30
+ "semantic:presentation:authored-steps-journey": "M018",
31
+ "semantic:presentation:authored-steps-ribbon": "M019",
32
+ "semantic:presentation:authored-comparison-panels": "M020",
33
+ "semantic:presentation:authored-quote-editorial": "M021",
34
+ "semantic:presentation:authored-quote-poster": "M022",
35
+ "semantic:presentation:authored-code-editorial": "M023",
36
+ "semantic:presentation:authored-parameter-ledger": "M024",
37
+ "semantic:presentation:authored-parameter-cards": "M025",
38
+ "semantic:presentation:semantic-code-split": "M026",
39
+ "semantic:presentation:semantic-parameters-split": "M029",
40
+ "semantic:presentation:semantic-parameters-stack": "M030",
41
+ "semantic:presentation:semantic-steps-split": "M032",
42
+ "semantic:presentation:semantic-steps-stack": "M033",
43
+ "semantic:presentation:semantic-steps-editorial": "M034",
44
+ "semantic:presentation:semantic-comparison-split": "M035",
45
+ "semantic:presentation:semantic-features-split": "M038",
46
+ "semantic:presentation:semantic-features-stack": "M039",
47
+ "semantic:presentation:semantic-features-editorial": "M040",
48
+ "semantic:presentation:semantic-quote-split": "M041",
49
+ "semantic:presentation:semantic-chart-split": "M044",
50
+ "semantic:presentation:semantic-chart-stack": "M045",
51
+ "semantic:presentation:semantic-prose-split": "M050",
52
+ "semantic:presentation:semantic-prose-stack": "M051",
53
+ "semantic:presentation:semantic-prose-editorial": "M052",
54
+ "semantic:presentation:semantic-mixed-stack": "M053",
55
+ "semantic:social:semantic-hero-editorial": "M094"
56
+ };
57
+ /** Social numbers follow the established formal gallery, not registration order. */
58
+ export const SOCIAL_DISPLAY_NUMBERS = {
59
+ "note-question": "S001",
60
+ "note-marker": "S002",
61
+ "note-editorial": "S003",
62
+ "note-checklist": "S004",
63
+ "note-photo": "S005",
64
+ "note-news": "S006",
65
+ "chat-messenger": "S007",
66
+ "chat-imessage": "S008",
67
+ "chat-telegram": "S009",
68
+ "chat-workplace": "S010",
69
+ "time-notice": "S011",
70
+ "news-focus": "S012",
71
+ "usage-breakdown": "S013",
72
+ "organization": "S014",
73
+ "editorial-quote": "S015",
74
+ "research-highlight": "S016",
75
+ "product-showcase": "S017",
76
+ "manifesto": "S018",
77
+ "launch": "S019",
78
+ "bento": "S020",
79
+ "spotlight": "S021",
80
+ "ticket": "S022",
81
+ "webinar": "S023",
82
+ "quote": "S024",
83
+ "testimonial": "S025",
84
+ "metric": "S026",
85
+ "report": "S027",
86
+ "checklist": "S028",
87
+ "steps": "S029",
88
+ "comparison": "S030",
89
+ "myth": "S031",
90
+ "pricing": "S032",
91
+ "offer": "S033",
92
+ "editorial": "S034",
93
+ "story": "S035",
94
+ "hiring": "S036",
95
+ "team": "S037",
96
+ "course": "S038",
97
+ "case-study": "S039",
98
+ "notice": "S040",
99
+ "community": "S041",
100
+ "menu": "S042",
101
+ "specification": "S043",
102
+ "itinerary": "S044",
103
+ "schedule": "S045",
104
+ "safety": "S046",
105
+ "faq": "S047",
106
+ "split-bar-compare": "S048",
107
+ "benchmark-bars": "S049"
108
+ };
@@ -0,0 +1 @@
1
+ export declare function compositionUseCase(id: string): string;