@neatico/visual-core 0.14.69 → 0.14.71

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.
@@ -5,6 +5,7 @@ exports.parseSource = parseSource;
5
5
  exports.validateContentPlan = validateContentPlan;
6
6
  exports.contentBlockText = contentBlockText;
7
7
  exports.validateEditableContentPage = validateEditableContentPage;
8
+ const illustrationScenes_js_1 = require("./illustrationScenes.js");
8
9
  const textPresentation_js_1 = require("./textPresentation.js");
9
10
  const compositionScenes_js_1 = require("./compositionScenes.js");
10
11
  function emojiField(value, path) {
@@ -670,16 +671,7 @@ function visualDescriptor(item) {
670
671
  "BsFolder2Open",
671
672
  "BsGrid1X2",
672
673
  ]);
673
- const illustrations = new Set([
674
- "document",
675
- "collaboration",
676
- "review",
677
- "feedback",
678
- "symbol",
679
- "sequence",
680
- "comparison",
681
- "structure",
682
- ]);
674
+ const illustrations = new Set(illustrationScenes_js_1.COMPOSITION_ILLUSTRATIONS);
683
675
  if (item.icon != null &&
684
676
  (typeof item.icon !== "string" || !icons.has(item.icon)))
685
677
  throw new Error("Invalid semantic icon");
@@ -89,7 +89,7 @@ function article(id, kind, variant) {
89
89
  if (kind === 'faq')
90
90
  primary.properties.entries.items.properties.title.pattern = '^.+[??]$';
91
91
  }
92
- const coverDirections = ['An editorial document sculpture with layered planes and a strong headline.', 'A connected constellation with a single focal object and an editorial headline.', 'An architectural archive silhouette balanced against expressive editorial typography.'];
92
+ const coverDirections = ['An editorial split cover with a topic-specific native hero and a strong headline.', 'A bold cover with one topic-specific focal object and an editorial headline.', 'A framed topic-specific native scene balanced against expressive editorial typography.'];
93
93
  return { ...definition, ...(cover ? { parameterSchema: coverWithHero, capacity: { minEntries: 0, maxEntries: 0, requiresImage: false }, semanticDescription: coverDirections[variant] } : { parameterSchema: illustrationSchema }), recipe: undefined, rendererKey: `article-${kind}:${variant}`,
94
94
  semanticDescription: (0, compositionUseCases_js_1.compositionUseCase)(id) + (cover ? ' ' + coverDirections[variant] : '') };
95
95
  }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ILLUSTRATION_GUIDANCE = exports.COMPOSITION_ILLUSTRATIONS = void 0;
4
+ exports.illustrationSubject = illustrationSubject;
5
+ /** One vocabulary for model contracts, validation and native editable renderers. */
6
+ exports.COMPOSITION_ILLUSTRATIONS = [
7
+ 'document', 'collaboration', 'review', 'feedback', 'sequence', 'comparison', 'structure', 'symbol',
8
+ 'software', 'security', 'analytics', 'education', 'health', 'nature', 'finance', 'logistics',
9
+ 'camping', 'landscape', 'route', 'cooking', 'climate', 'access',
10
+ ];
11
+ exports.ILLUSTRATION_GUIDANCE = 'Choose a concrete subject supported by this item: camping for a tent/campsite, landscape for scenery/desert, route for travel/navigation, cooking for food preparation, climate for weather/aridity, access for land-entry rules; software, security, analytics, education, health, nature, finance or logistics for those subjects. Use document, collaboration, review, feedback, sequence, comparison or structure only for an actual document, people working together, checking, revision, ordered actions, alternatives or organization. symbol is a neutral fallback. Never choose by list position or reuse an unrelated diagram to fill a slot.';
12
+ /** Repair legacy broad descriptors from visible copy, never from item position.
13
+ * This chooses decorative objects only; it adds no factual labels or geography.
14
+ */
15
+ function illustrationSubject(copy, context = '') {
16
+ if (/地块|土地.{0,8}(?:规则|准入|进入)|准入|访问规则|\b(?:land access|land rules|access note|entry rules|land permission)\b/iu.test(copy))
17
+ return 'access';
18
+ if (/风景|景观|\b(?:scenic|scenery|landscape)\b/iu.test(copy))
19
+ return 'landscape';
20
+ if (/气候|干旱|干燥|天气|\b(?:arid|aridity|dry conditions|climate|weather)\b/iu.test(copy))
21
+ return 'climate';
22
+ if (/烹饪|做饭|炊事|食物|餐饮|排骨|\b(?:cook\w*|food|meal|ribs|kitchen)\b/iu.test(copy))
23
+ return 'cooking';
24
+ if (/露营|帐篷|营地|\b(?:camp\w*|tent)\b/iu.test(copy))
25
+ return 'camping';
26
+ if (/沙漠|山川|\b(?:desert|mountain)\b/iu.test(copy))
27
+ return 'landscape';
28
+ if (/路线|路程|导航|交通|旅行|跨区域|\b(?:route|overland|navigation|travel|regional connection)\b/iu.test(copy))
29
+ return 'route';
30
+ if (/凭证|令牌|加密|权限|\b(?:credential|token|encrypt|permission|security)\b/iu.test(copy))
31
+ return 'security';
32
+ if (/接口|代码|软件|\b(?:api|code|software)\b/iu.test(copy))
33
+ return 'software';
34
+ if (/自然|森林|植物|\b(?:nature|forest|tree|plant)\b/iu.test(copy))
35
+ return 'nature';
36
+ if (/医疗|健康|\b(?:health|medical)\b/iu.test(copy))
37
+ return 'health';
38
+ if (/教育|课堂|\b(?:education|classroom|learning)\b/iu.test(copy))
39
+ return 'education';
40
+ if (/财务|金融|预算|\b(?:finance|budget|payment)\b/iu.test(copy))
41
+ return 'finance';
42
+ if (/货运|物流|配送|\b(?:logistics|shipping|delivery)\b/iu.test(copy))
43
+ return 'logistics';
44
+ if (/分析|统计|\b(?:analytics|statistics)\b/iu.test(copy))
45
+ return 'analytics';
46
+ // A solitude caption within a camping section is not a collaboration claim.
47
+ if (/远离人群|独处|\b(?:disconnect|solitude|crowded spaces)\b/iu.test(copy) && /露营|\bcamp\w*/iu.test(context))
48
+ return 'camping';
49
+ return undefined;
50
+ }
package/dist/cjs/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.SOCIAL_DISPLAY_NUMBERS = exports.compositionParameterDefaults = void 0;
17
+ exports.ILLUSTRATION_GUIDANCE = exports.illustrationSubject = exports.SOCIAL_DISPLAY_NUMBERS = exports.compositionParameterDefaults = void 0;
18
18
  __exportStar(require("./geometry.js"), exports);
19
19
  __exportStar(require("./text.js"), exports);
20
20
  __exportStar(require("./scenes.js"), exports);
@@ -43,3 +43,6 @@ var compositionNumbers_js_1 = require("./compositionNumbers.js");
43
43
  Object.defineProperty(exports, "SOCIAL_DISPLAY_NUMBERS", { enumerable: true, get: function () { return compositionNumbers_js_1.SOCIAL_DISPLAY_NUMBERS; } });
44
44
  __exportStar(require("./textPresentation.js"), exports);
45
45
  __exportStar(require("./exploreCollection.js"), exports);
46
+ var illustrationScenes_js_1 = require("./illustrationScenes.js");
47
+ Object.defineProperty(exports, "illustrationSubject", { enumerable: true, get: function () { return illustrationScenes_js_1.illustrationSubject; } });
48
+ Object.defineProperty(exports, "ILLUSTRATION_GUIDANCE", { enumerable: true, get: function () { return illustrationScenes_js_1.ILLUSTRATION_GUIDANCE; } });
@@ -4,6 +4,7 @@ exports.COMPOSITION_ILLUSTRATIONS = void 0;
4
4
  exports.nativeSocialParameterContract = nativeSocialParameterContract;
5
5
  exports.validateNativeSocialParameters = validateNativeSocialParameters;
6
6
  exports.nativeSocialContentPage = nativeSocialContentPage;
7
+ const illustrationScenes_js_1 = require("./illustrationScenes.js");
7
8
  const parameterDefaults_js_1 = require("./parameterDefaults.js");
8
9
  const textPresentation_js_1 = require("./textPresentation.js");
9
10
  const socialLayouts_js_1 = require("./socialLayouts.js");
@@ -25,7 +26,8 @@ const slots = {
25
26
  const copy = (maxLength, description, minLength = 1) => ({ type: 'string', minLength, maxLength, description });
26
27
  const object = (properties) => ({ type: 'object', additionalProperties: false, properties, required: Object.keys(properties) });
27
28
  const asset = () => copy(160, 'An existing supplied asset ID. Use neatico-editorial-placeholder when no matching source image exists.');
28
- exports.COMPOSITION_ILLUSTRATIONS = ['document', 'collaboration', 'review', 'feedback', 'sequence', 'comparison', 'structure'];
29
+ var illustrationScenes_js_2 = require("./illustrationScenes.js");
30
+ Object.defineProperty(exports, "COMPOSITION_ILLUSTRATIONS", { enumerable: true, get: function () { return illustrationScenes_js_2.COMPOSITION_ILLUSTRATIONS; } });
29
31
  const illustratedSlots = new Set(['bento', 'course', 'case-study', 'report', 'product-showcase', 'comparison', 'research-highlight']);
30
32
  const provenance = ['user-fact', 'derived', 'creative', 'simulated'];
31
33
  // These authored slots have been measured with complete provider sentences at
@@ -82,7 +84,7 @@ function nativeSocialParameterContract(id) {
82
84
  item.description = copy(24, 'Optional concise supporting detail. Use empty text when not needed.', 0);
83
85
  }
84
86
  if (illustratedSlots.has(id))
85
- item.illustration = { type: 'string', enum: exports.COMPOSITION_ILLUSTRATIONS, default: 'structure', description: 'An editable native illustration that explains this item. Choose document for material, review for checking, collaboration for people, feedback for revision, sequence for ordered actions, comparison for alternatives, or structure for organization. Never omit the visual slot.' };
87
+ item.illustration = { type: 'string', enum: illustrationScenes_js_1.COMPOSITION_ILLUSTRATIONS, default: 'symbol', description: illustrationScenes_js_1.ILLUSTRATION_GUIDANCE };
86
88
  if (spec.itemImages)
87
89
  item.assetId = asset();
88
90
  if (spec.quantities) {
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RECIPE_PARAMETER_CONTRACTS = void 0;
4
4
  exports.recipeParameterContract = recipeParameterContract;
5
- const nativeSocialParameters_js_1 = require("./nativeSocialParameters.js");
5
+ const illustrationScenes_js_1 = require("./illustrationScenes.js");
6
6
  const textPresentation_js_1 = require("./textPresentation.js");
7
7
  const designSystem_js_1 = require("./designSystem.js");
8
8
  const text = (description, maxLength = 600) => ({
@@ -166,9 +166,9 @@ function fixedBlocks(recipe) {
166
166
  entries.maxItems = count;
167
167
  entries.items.properties.illustration = {
168
168
  type: "string",
169
- enum: [...nativeSocialParameters_js_1.COMPOSITION_ILLUSTRATIONS, "symbol"],
170
- default: "structure",
171
- description: "Editable native illustration for the item. Choose the visual concept that explains its meaning. This field controls a graphic, not prose.",
169
+ enum: [...illustrationScenes_js_1.COMPOSITION_ILLUSTRATIONS],
170
+ default: "symbol",
171
+ description: illustrationScenes_js_1.ILLUSTRATION_GUIDANCE,
172
172
  };
173
173
  entries.items.required.push("illustration");
174
174
  entries.items.properties.icon = {
@@ -307,6 +307,18 @@ function fixedBlocks(recipe) {
307
307
  fields.values.items.properties.value.minimum = 0;
308
308
  }
309
309
  }
310
+ // These measured list/table renderers support variable rows. The old global
311
+ // three-item fixture bound discarded useful itinerary and budget detail.
312
+ // Authored card compositions retain their fixed number of slots.
313
+ if (/^semantic-(steps|parameters)-(stack|split)$/.test(id)) {
314
+ const values = kind === "steps" ? fields.entries : fields.rows;
315
+ if (values) {
316
+ values.minItems = 2;
317
+ values.maxItems = id.endsWith("stack") ? 8 : 6;
318
+ values.items.properties.description.maxLength = 24;
319
+ values.description = "Use enough rows to complete this page's requested detail: include every relevant time slot or budget item and total, within the fixed maximum. Do not collapse a full itinerary into three vague phases.";
320
+ }
321
+ }
310
322
  return schema;
311
323
  });
312
324
  return {
package/dist/content.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { COMPOSITION_ILLUSTRATIONS } from './illustrationScenes.js';
1
2
  import { validateTextPresentations } from "./textPresentation.js";
2
3
  import { COMPOSITION_SCENES } from "./compositionScenes.js";
3
4
  function emojiField(value, path) {
@@ -662,16 +663,7 @@ function visualDescriptor(item) {
662
663
  "BsFolder2Open",
663
664
  "BsGrid1X2",
664
665
  ]);
665
- const illustrations = new Set([
666
- "document",
667
- "collaboration",
668
- "review",
669
- "feedback",
670
- "symbol",
671
- "sequence",
672
- "comparison",
673
- "structure",
674
- ]);
666
+ const illustrations = new Set(COMPOSITION_ILLUSTRATIONS);
675
667
  if (item.icon != null &&
676
668
  (typeof item.icon !== "string" || !icons.has(item.icon)))
677
669
  throw new Error("Invalid semantic icon");
@@ -85,7 +85,7 @@ function article(id, kind, variant) {
85
85
  if (kind === 'faq')
86
86
  primary.properties.entries.items.properties.title.pattern = '^.+[??]$';
87
87
  }
88
- const coverDirections = ['An editorial document sculpture with layered planes and a strong headline.', 'A connected constellation with a single focal object and an editorial headline.', 'An architectural archive silhouette balanced against expressive editorial typography.'];
88
+ const coverDirections = ['An editorial split cover with a topic-specific native hero and a strong headline.', 'A bold cover with one topic-specific focal object and an editorial headline.', 'A framed topic-specific native scene balanced against expressive editorial typography.'];
89
89
  return { ...definition, ...(cover ? { parameterSchema: coverWithHero, capacity: { minEntries: 0, maxEntries: 0, requiresImage: false }, semanticDescription: coverDirections[variant] } : { parameterSchema: illustrationSchema }), recipe: undefined, rendererKey: `article-${kind}:${variant}`,
90
90
  semanticDescription: compositionUseCase(id) + (cover ? ' ' + coverDirections[variant] : '') };
91
91
  }
@@ -0,0 +1,8 @@
1
+ /** One vocabulary for model contracts, validation and native editable renderers. */
2
+ export declare const COMPOSITION_ILLUSTRATIONS: readonly ["document", "collaboration", "review", "feedback", "sequence", "comparison", "structure", "symbol", "software", "security", "analytics", "education", "health", "nature", "finance", "logistics", "camping", "landscape", "route", "cooking", "climate", "access"];
3
+ export type IllustrationScene = typeof COMPOSITION_ILLUSTRATIONS[number];
4
+ export declare const ILLUSTRATION_GUIDANCE = "Choose a concrete subject supported by this item: camping for a tent/campsite, landscape for scenery/desert, route for travel/navigation, cooking for food preparation, climate for weather/aridity, access for land-entry rules; software, security, analytics, education, health, nature, finance or logistics for those subjects. Use document, collaboration, review, feedback, sequence, comparison or structure only for an actual document, people working together, checking, revision, ordered actions, alternatives or organization. symbol is a neutral fallback. Never choose by list position or reuse an unrelated diagram to fill a slot.";
5
+ /** Repair legacy broad descriptors from visible copy, never from item position.
6
+ * This chooses decorative objects only; it adds no factual labels or geography.
7
+ */
8
+ export declare function illustrationSubject(copy: string, context?: string): IllustrationScene | undefined;
@@ -0,0 +1,46 @@
1
+ /** One vocabulary for model contracts, validation and native editable renderers. */
2
+ export const COMPOSITION_ILLUSTRATIONS = [
3
+ 'document', 'collaboration', 'review', 'feedback', 'sequence', 'comparison', 'structure', 'symbol',
4
+ 'software', 'security', 'analytics', 'education', 'health', 'nature', 'finance', 'logistics',
5
+ 'camping', 'landscape', 'route', 'cooking', 'climate', 'access',
6
+ ];
7
+ export const ILLUSTRATION_GUIDANCE = 'Choose a concrete subject supported by this item: camping for a tent/campsite, landscape for scenery/desert, route for travel/navigation, cooking for food preparation, climate for weather/aridity, access for land-entry rules; software, security, analytics, education, health, nature, finance or logistics for those subjects. Use document, collaboration, review, feedback, sequence, comparison or structure only for an actual document, people working together, checking, revision, ordered actions, alternatives or organization. symbol is a neutral fallback. Never choose by list position or reuse an unrelated diagram to fill a slot.';
8
+ /** Repair legacy broad descriptors from visible copy, never from item position.
9
+ * This chooses decorative objects only; it adds no factual labels or geography.
10
+ */
11
+ export function illustrationSubject(copy, context = '') {
12
+ if (/地块|土地.{0,8}(?:规则|准入|进入)|准入|访问规则|\b(?:land access|land rules|access note|entry rules|land permission)\b/iu.test(copy))
13
+ return 'access';
14
+ if (/风景|景观|\b(?:scenic|scenery|landscape)\b/iu.test(copy))
15
+ return 'landscape';
16
+ if (/气候|干旱|干燥|天气|\b(?:arid|aridity|dry conditions|climate|weather)\b/iu.test(copy))
17
+ return 'climate';
18
+ if (/烹饪|做饭|炊事|食物|餐饮|排骨|\b(?:cook\w*|food|meal|ribs|kitchen)\b/iu.test(copy))
19
+ return 'cooking';
20
+ if (/露营|帐篷|营地|\b(?:camp\w*|tent)\b/iu.test(copy))
21
+ return 'camping';
22
+ if (/沙漠|山川|\b(?:desert|mountain)\b/iu.test(copy))
23
+ return 'landscape';
24
+ if (/路线|路程|导航|交通|旅行|跨区域|\b(?:route|overland|navigation|travel|regional connection)\b/iu.test(copy))
25
+ return 'route';
26
+ if (/凭证|令牌|加密|权限|\b(?:credential|token|encrypt|permission|security)\b/iu.test(copy))
27
+ return 'security';
28
+ if (/接口|代码|软件|\b(?:api|code|software)\b/iu.test(copy))
29
+ return 'software';
30
+ if (/自然|森林|植物|\b(?:nature|forest|tree|plant)\b/iu.test(copy))
31
+ return 'nature';
32
+ if (/医疗|健康|\b(?:health|medical)\b/iu.test(copy))
33
+ return 'health';
34
+ if (/教育|课堂|\b(?:education|classroom|learning)\b/iu.test(copy))
35
+ return 'education';
36
+ if (/财务|金融|预算|\b(?:finance|budget|payment)\b/iu.test(copy))
37
+ return 'finance';
38
+ if (/货运|物流|配送|\b(?:logistics|shipping|delivery)\b/iu.test(copy))
39
+ return 'logistics';
40
+ if (/分析|统计|\b(?:analytics|statistics)\b/iu.test(copy))
41
+ return 'analytics';
42
+ // A solitude caption within a camping section is not a collaboration claim.
43
+ if (/远离人群|独处|\b(?:disconnect|solitude|crowded spaces)\b/iu.test(copy) && /露营|\bcamp\w*/iu.test(context))
44
+ return 'camping';
45
+ return undefined;
46
+ }
package/dist/index.d.ts CHANGED
@@ -24,3 +24,4 @@ export { compositionParameterDefaults } from './parameterDefaults.js';
24
24
  export { SOCIAL_DISPLAY_NUMBERS } from './compositionNumbers.js';
25
25
  export * from './textPresentation.js';
26
26
  export * from './exploreCollection.js';
27
+ export { illustrationSubject, ILLUSTRATION_GUIDANCE, type IllustrationScene } from './illustrationScenes.js';
package/dist/index.js CHANGED
@@ -24,3 +24,4 @@ export { compositionParameterDefaults } from './parameterDefaults.js';
24
24
  export { SOCIAL_DISPLAY_NUMBERS } from './compositionNumbers.js';
25
25
  export * from './textPresentation.js';
26
26
  export * from './exploreCollection.js';
27
+ export { illustrationSubject, ILLUSTRATION_GUIDANCE } from './illustrationScenes.js';
@@ -1,3 +1,4 @@
1
+ import { type IllustrationScene } from './illustrationScenes.js';
1
2
  import { type TextPresentation } from './textPresentation.js';
2
3
  import type { ParameterSchema } from './recipeParameters.js';
3
4
  /** Native slots keep renderer-specific content separate from semantic page projection. */
@@ -18,7 +19,7 @@ export interface NativeSocialParameters {
18
19
  title: string;
19
20
  description: string;
20
21
  assetId?: string;
21
- illustration?: 'document' | 'collaboration' | 'review' | 'feedback' | 'sequence' | 'comparison' | 'structure';
22
+ illustration?: IllustrationScene;
22
23
  primaryValue?: number;
23
24
  secondaryValue?: number;
24
25
  }[];
@@ -29,7 +30,7 @@ export interface NativeSocialContract {
29
30
  importance: Readonly<Record<string, number>>;
30
31
  generationPolicy: Readonly<Record<string, readonly string[]>>;
31
32
  }
32
- export declare const COMPOSITION_ILLUSTRATIONS: readonly ["document", "collaboration", "review", "feedback", "sequence", "comparison", "structure"];
33
+ export { COMPOSITION_ILLUSTRATIONS } from './illustrationScenes.js';
33
34
  /** Every field corresponds to an authored visible slot; no scene-based eligibility filter. */
34
35
  export declare function nativeSocialParameterContract(id: string): NativeSocialContract;
35
36
  /** Apply the small closed schema vocabulary used by native slots on every local render. */
@@ -1,3 +1,4 @@
1
+ import { COMPOSITION_ILLUSTRATIONS, ILLUSTRATION_GUIDANCE } from './illustrationScenes.js';
1
2
  import { compositionParameterDefaults } from './parameterDefaults.js';
2
3
  import { textPresentationSchema, validateTextPresentations } from './textPresentation.js';
3
4
  import { SOCIAL_LAYOUT_CATALOG } from './socialLayouts.js';
@@ -19,7 +20,7 @@ const slots = {
19
20
  const copy = (maxLength, description, minLength = 1) => ({ type: 'string', minLength, maxLength, description });
20
21
  const object = (properties) => ({ type: 'object', additionalProperties: false, properties, required: Object.keys(properties) });
21
22
  const asset = () => copy(160, 'An existing supplied asset ID. Use neatico-editorial-placeholder when no matching source image exists.');
22
- export const COMPOSITION_ILLUSTRATIONS = ['document', 'collaboration', 'review', 'feedback', 'sequence', 'comparison', 'structure'];
23
+ export { COMPOSITION_ILLUSTRATIONS } from './illustrationScenes.js';
23
24
  const illustratedSlots = new Set(['bento', 'course', 'case-study', 'report', 'product-showcase', 'comparison', 'research-highlight']);
24
25
  const provenance = ['user-fact', 'derived', 'creative', 'simulated'];
25
26
  // These authored slots have been measured with complete provider sentences at
@@ -76,7 +77,7 @@ export function nativeSocialParameterContract(id) {
76
77
  item.description = copy(24, 'Optional concise supporting detail. Use empty text when not needed.', 0);
77
78
  }
78
79
  if (illustratedSlots.has(id))
79
- item.illustration = { type: 'string', enum: COMPOSITION_ILLUSTRATIONS, default: 'structure', description: 'An editable native illustration that explains this item. Choose document for material, review for checking, collaboration for people, feedback for revision, sequence for ordered actions, comparison for alternatives, or structure for organization. Never omit the visual slot.' };
80
+ item.illustration = { type: 'string', enum: COMPOSITION_ILLUSTRATIONS, default: 'symbol', description: ILLUSTRATION_GUIDANCE };
80
81
  if (spec.itemImages)
81
82
  item.assetId = asset();
82
83
  if (spec.quantities) {
@@ -1,4 +1,4 @@
1
- import { COMPOSITION_ILLUSTRATIONS } from "./nativeSocialParameters.js";
1
+ import { COMPOSITION_ILLUSTRATIONS, ILLUSTRATION_GUIDANCE } from "./illustrationScenes.js";
2
2
  import { textPresentationSchema } from "./textPresentation.js";
3
3
  import { DESIGN_RECIPES } from "./designSystem.js";
4
4
  const text = (description, maxLength = 600) => ({
@@ -162,9 +162,9 @@ function fixedBlocks(recipe) {
162
162
  entries.maxItems = count;
163
163
  entries.items.properties.illustration = {
164
164
  type: "string",
165
- enum: [...COMPOSITION_ILLUSTRATIONS, "symbol"],
166
- default: "structure",
167
- description: "Editable native illustration for the item. Choose the visual concept that explains its meaning. This field controls a graphic, not prose.",
165
+ enum: [...COMPOSITION_ILLUSTRATIONS],
166
+ default: "symbol",
167
+ description: ILLUSTRATION_GUIDANCE,
168
168
  };
169
169
  entries.items.required.push("illustration");
170
170
  entries.items.properties.icon = {
@@ -303,6 +303,18 @@ function fixedBlocks(recipe) {
303
303
  fields.values.items.properties.value.minimum = 0;
304
304
  }
305
305
  }
306
+ // These measured list/table renderers support variable rows. The old global
307
+ // three-item fixture bound discarded useful itinerary and budget detail.
308
+ // Authored card compositions retain their fixed number of slots.
309
+ if (/^semantic-(steps|parameters)-(stack|split)$/.test(id)) {
310
+ const values = kind === "steps" ? fields.entries : fields.rows;
311
+ if (values) {
312
+ values.minItems = 2;
313
+ values.maxItems = id.endsWith("stack") ? 8 : 6;
314
+ values.items.properties.description.maxLength = 24;
315
+ values.description = "Use enough rows to complete this page's requested detail: include every relevant time slot or budget item and total, within the fixed maximum. Do not collapse a full itinerary into three vague phases.";
316
+ }
317
+ }
306
318
  return schema;
307
319
  });
308
320
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatico/visual-core",
3
- "version": "0.14.69",
3
+ "version": "0.14.71",
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",