@neatico/visual-core 0.14.26 → 0.14.65

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 (46) 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 +86 -9
  9. package/dist/cjs/exploreCollection.js +180 -0
  10. package/dist/cjs/fontCatalog.js +7 -1
  11. package/dist/cjs/formalCompositions.js +121 -0
  12. package/dist/cjs/index.js +14 -0
  13. package/dist/cjs/nativeSocialParameters.js +145 -0
  14. package/dist/cjs/pagination.js +14 -5
  15. package/dist/cjs/parameterDefaults.js +20 -0
  16. package/dist/cjs/recipeParameters.js +375 -0
  17. package/dist/cjs/styleManifest.js +107 -0
  18. package/dist/cjs/textPresentation.js +68 -0
  19. package/dist/compositionNumbers.d.ts +4 -0
  20. package/dist/compositionNumbers.js +108 -0
  21. package/dist/compositionUseCases.d.ts +1 -0
  22. package/dist/compositionUseCases.js +142 -0
  23. package/dist/content.d.ts +10 -0
  24. package/dist/content.js +46 -10
  25. package/dist/contentBrief.d.ts +16 -0
  26. package/dist/contentBrief.js +78 -0
  27. package/dist/designSystem.js +86 -9
  28. package/dist/exploreCollection.d.ts +7 -0
  29. package/dist/exploreCollection.js +176 -0
  30. package/dist/fontCatalog.js +7 -1
  31. package/dist/formalCompositions.d.ts +26 -0
  32. package/dist/formalCompositions.js +117 -0
  33. package/dist/index.d.ts +11 -0
  34. package/dist/index.js +11 -0
  35. package/dist/nativeSocialParameters.d.ts +38 -0
  36. package/dist/nativeSocialParameters.js +139 -0
  37. package/dist/pagination.js +14 -5
  38. package/dist/parameterDefaults.d.ts +2 -0
  39. package/dist/parameterDefaults.js +17 -0
  40. package/dist/recipeParameters.d.ts +34 -0
  41. package/dist/recipeParameters.js +371 -0
  42. package/dist/styleManifest.d.ts +20 -0
  43. package/dist/styleManifest.js +103 -0
  44. package/dist/textPresentation.d.ts +29 -0
  45. package/dist/textPresentation.js +64 -0
  46. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { type DesignTheme } from "./designSystem.js";
2
+ /** Persist the resolved tokens, not just a mutable theme name. */
3
+ export interface StyleManifest {
4
+ version: 1;
5
+ themeId: string;
6
+ themeVersion: 1;
7
+ theme: DesignTheme;
8
+ fonts: {
9
+ heading: string;
10
+ body: string;
11
+ code: string;
12
+ };
13
+ imagery: {
14
+ medium: "native" | "photography" | "illustration" | "3d";
15
+ referenceAssetIds: string[];
16
+ direction: string;
17
+ };
18
+ }
19
+ export declare function createStyleManifest(themeId: string, copy?: string, imagery?: Partial<StyleManifest["imagery"]>): StyleManifest;
20
+ export declare function validateStyleManifest(value: unknown): StyleManifest;
@@ -0,0 +1,103 @@
1
+ import { DESIGN_THEMES, designTheme, } from "./designSystem.js";
2
+ import { languageFont } from "./fontCatalog.js";
3
+ export function createStyleManifest(themeId, copy = "", imagery) {
4
+ const theme = designTheme(themeId);
5
+ if (!DESIGN_THEMES.some((candidate) => candidate.id === themeId) &&
6
+ !["paper", "midnight", "ocean", "plum"].includes(themeId))
7
+ throw new Error("STYLE_UNKNOWN_THEME");
8
+ const manifest = {
9
+ version: 1,
10
+ themeId: theme.id,
11
+ themeVersion: 1,
12
+ theme: {
13
+ ...theme,
14
+ palette: { ...theme.palette },
15
+ typography: { ...theme.typography },
16
+ geometry: { ...theme.geometry },
17
+ },
18
+ fonts: {
19
+ heading: languageFont(copy, theme.typography.heading),
20
+ body: languageFont(copy, theme.typography.body),
21
+ code: theme.typography.code === "monospace"
22
+ ? "JetBrains Mono"
23
+ : theme.typography.code,
24
+ },
25
+ imagery: {
26
+ medium: "native",
27
+ referenceAssetIds: [],
28
+ direction: "",
29
+ ...imagery,
30
+ },
31
+ };
32
+ return validateStyleManifest(manifest);
33
+ }
34
+ export function validateStyleManifest(value) {
35
+ const manifest = value;
36
+ const fail = () => {
37
+ throw new Error("STYLE_INVALID_MANIFEST");
38
+ };
39
+ if (!manifest ||
40
+ manifest.version !== 1 ||
41
+ manifest.themeVersion !== 1 ||
42
+ typeof manifest.themeId !== "string" ||
43
+ !manifest.theme ||
44
+ manifest.theme.id !== manifest.themeId ||
45
+ !DESIGN_THEMES.some((theme) => theme.id === manifest.themeId))
46
+ return fail();
47
+ const { palette, typography, geometry } = manifest.theme;
48
+ if (!palette ||
49
+ ["background", "surface", "ink", "muted", "accent", "line"].some((key) => !/^#[\da-f]{6}$/i.test(palette[key] || "")))
50
+ return fail();
51
+ if (!typography ||
52
+ ["heading", "body", "code"].some((key) => typeof typography[key] !==
53
+ "string") ||
54
+ !Number.isInteger(typography.headingWeight) ||
55
+ typography.headingWeight < 100 ||
56
+ typography.headingWeight > 900)
57
+ return fail();
58
+ if (!geometry ||
59
+ !Number.isFinite(geometry.radius) ||
60
+ geometry.radius < 0 ||
61
+ geometry.radius > 100 ||
62
+ !Number.isFinite(geometry.spacing) ||
63
+ geometry.spacing <= 0 ||
64
+ geometry.spacing > 4 ||
65
+ !["none", "hairline", "accent"].includes(geometry.rule))
66
+ return fail();
67
+ if (!manifest.fonts ||
68
+ Object.keys(manifest.fonts).length !== 3 ||
69
+ ["heading", "body", "code"].some((key) => {
70
+ const font = manifest.fonts[key];
71
+ return typeof font !== "string" || !font.trim() || font.length > 100;
72
+ }))
73
+ return fail();
74
+ if (!manifest.imagery ||
75
+ !["native", "photography", "illustration", "3d"].includes(manifest.imagery.medium) ||
76
+ typeof manifest.imagery.direction !== "string" ||
77
+ manifest.imagery.direction.length > 2000 ||
78
+ !Array.isArray(manifest.imagery.referenceAssetIds) ||
79
+ manifest.imagery.referenceAssetIds.length > 16 ||
80
+ manifest.imagery.referenceAssetIds.some((id) => typeof id !== "string" || !id || id.length > 160))
81
+ return fail();
82
+ return {
83
+ version: 1,
84
+ themeId: manifest.themeId,
85
+ themeVersion: 1,
86
+ theme: {
87
+ id: manifest.themeId,
88
+ palette: { ...palette },
89
+ typography: { ...typography },
90
+ geometry: { ...geometry },
91
+ },
92
+ fonts: {
93
+ heading: manifest.fonts.heading,
94
+ body: manifest.fonts.body,
95
+ code: manifest.fonts.code,
96
+ },
97
+ imagery: {
98
+ medium: manifest.imagery.medium,
99
+ direction: manifest.imagery.direction,
100
+ referenceAssetIds: [...manifest.imagery.referenceAssetIds],
101
+ },
102
+ };
103
+ }
@@ -0,0 +1,29 @@
1
+ /** Closed presentation intent. Content stays in its original field, never in CSS. */
2
+ export interface TextPresentationLine {
3
+ text: string;
4
+ font: 'display' | 'body' | 'mono';
5
+ scale: number;
6
+ tone: 'ink' | 'accent' | 'secondary';
7
+ decoration: 'none' | 'marker' | 'underline';
8
+ italic: boolean;
9
+ }
10
+ export interface TextPresentation {
11
+ path: string;
12
+ fit: 'natural' | 'equal-width';
13
+ lines: TextPresentationLine[];
14
+ }
15
+ export declare function textPresentationSchema(): {
16
+ type: string;
17
+ minItems: number;
18
+ maxItems: number;
19
+ default: never[];
20
+ description: string;
21
+ items: {
22
+ type: string;
23
+ additionalProperties: boolean;
24
+ properties: Record<string, unknown>;
25
+ required: string[];
26
+ };
27
+ };
28
+ /** Validate exact source reconstruction; script-aware boundaries permit CJK breaks. */
29
+ export declare function validateTextPresentations(value: unknown, content: Record<string, unknown>): TextPresentation[];
@@ -0,0 +1,64 @@
1
+ const object = (properties) => ({
2
+ type: 'object', additionalProperties: false, properties, required: Object.keys(properties),
3
+ });
4
+ export function textPresentationSchema() {
5
+ return {
6
+ type: 'array', minItems: 0, maxItems: 8, default: [],
7
+ 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.',
8
+ items: object({
9
+ 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.' },
10
+ fit: { type: 'string', enum: ['natural', 'equal-width'], description: 'Equal-width adjusts each line independently within readable size limits; it never stretches glyphs.' },
11
+ lines: { type: 'array', minItems: 1, maxItems: 8, items: object({
12
+ 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.' },
13
+ font: { type: 'string', enum: ['display', 'body', 'mono'] },
14
+ scale: { type: 'number', minimum: 0.8, maximum: 1.35 },
15
+ tone: { type: 'string', enum: ['ink', 'accent', 'secondary'] },
16
+ decoration: { type: 'string', enum: ['none', 'marker', 'underline'] },
17
+ italic: { type: 'boolean' },
18
+ }) },
19
+ }),
20
+ };
21
+ }
22
+ const normalize = (text) => text.normalize('NFC').replace(/\s+/gu, ' ').trim();
23
+ const word = (character) => /[\p{Script=Latin}\p{Script=Cyrillic}\p{Script=Greek}\p{Number}]/u.test(character);
24
+ /** Validate exact source reconstruction; script-aware boundaries permit CJK breaks. */
25
+ export function validateTextPresentations(value, content) {
26
+ if (value === undefined)
27
+ return [];
28
+ if (!Array.isArray(value) || value.length > 8)
29
+ throw new Error('TEXT_PRESENTATION_INVALID');
30
+ const paths = new Set();
31
+ for (const entry of value) {
32
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
33
+ throw new Error('TEXT_PRESENTATION_INVALID');
34
+ if (Object.keys(entry).some(key => !['path', 'fit', 'lines'].includes(key)))
35
+ throw new Error('TEXT_PRESENTATION_INVALID');
36
+ 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))
37
+ throw new Error('TEXT_PRESENTATION_PATH');
38
+ paths.add(entry.path);
39
+ const copy = entry.path.split('.').reduce((node, key) => node?.[key], content);
40
+ if (typeof copy !== 'string' || !copy.trim())
41
+ throw new Error('TEXT_PRESENTATION_PATH');
42
+ if (!['natural', 'equal-width'].includes(entry.fit) || !Array.isArray(entry.lines) || entry.lines.length < 1 || entry.lines.length > 8)
43
+ throw new Error('TEXT_PRESENTATION_INVALID');
44
+ const source = normalize(copy);
45
+ let cursor = 0;
46
+ for (const line of entry.lines) {
47
+ if (!line || typeof line !== 'object' || Array.isArray(line) || Object.keys(line).some(key => !['text', 'font', 'scale', 'tone', 'decoration', 'italic'].includes(key)))
48
+ throw new Error('TEXT_PRESENTATION_INVALID');
49
+ 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')
50
+ throw new Error('TEXT_PRESENTATION_INVALID');
51
+ const phrase = normalize(line.text);
52
+ if (source[cursor] === ' ')
53
+ cursor++;
54
+ if (!source.startsWith(phrase, cursor))
55
+ throw new Error('TEXT_PRESENTATION_CONTENT');
56
+ if (cursor && source[cursor - 1] !== ' ' && word(source[cursor - 1]) && word(phrase[0]))
57
+ throw new Error('TEXT_PRESENTATION_WORD_BOUNDARY');
58
+ cursor += phrase.length;
59
+ }
60
+ if (cursor !== source.length)
61
+ throw new Error('TEXT_PRESENTATION_CONTENT');
62
+ }
63
+ return value;
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatico/visual-core",
3
- "version": "0.14.26",
3
+ "version": "0.14.65",
4
4
  "description": "Framework-independent geometry, measured text wrapping, and extensible scene registries for visual editors.",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",