@vectoriox/iox-ui 5.62.1 → 5.63.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.
@@ -1,5 +1,5 @@
1
1
  import { MotionId, MoodId, TintId, DensityId, DesignMotion, DesignChoices, DesignMode, StylePreset, ShapeId, AnimateId } from '@vectoriox/iox-ui/design-core';
2
- import { FactKey, PathIssue, JsonSchema, SiteTemplate, ShapeEntry, AiGenerationStage, AiStep, SitePlan, PlanEdit, PlanChange, SiteAudit } from '@vectoriox/iox-ui/generation-core';
2
+ import { FactKey, PathIssue, PlainObject, JsonSchema, SiteTemplate, ShapeEntry, AiGenerationStage, AiStep, SitePlan, PlanEdit, PlanChange, SiteAudit } from '@vectoriox/iox-ui/generation-core';
3
3
  export { JsonSchema } from '@vectoriox/iox-ui/generation-core';
4
4
 
5
5
  declare const SPEC_HARNESS_SOURCE: {
@@ -2577,6 +2577,13 @@ declare function validateSiteAnalysis(value: unknown, ctx: AnalysisContext): Ana
2577
2577
  /** The name a provider shows the format under (`json_schema.name`). */
2578
2578
  declare const SITE_ANALYSIS_SCHEMA_NAME = "site_analysis";
2579
2579
  declare function siteAnalysisJsonSchema(): JsonSchema;
2580
+ /** A model's analysis (the wire) → the document, and the faults only the wire can have (a fact stated twice). Never repairs. */
2581
+ declare function analysisFromWire(wire: unknown): {
2582
+ analysis: unknown;
2583
+ issues: AnalysisIssue[];
2584
+ };
2585
+ /** The document → the wire (what a model writes). */
2586
+ declare function analysisToWire(analysis: SiteAnalysis): PlainObject;
2580
2587
 
2581
2588
  /**
2582
2589
  * What the PLAN prompt is given (AI plan rev 3 §3f "Retrieval, not the whole knowledge base"): the one matched
@@ -2801,7 +2808,7 @@ declare function validateSiteReading(value: unknown, ctx: {
2801
2808
  valid: boolean;
2802
2809
  issues: ReadingIssue[];
2803
2810
  };
2804
- /** A valid reading's answer → `SiteReading` (the analysis alone answers `{ analysis }`). */
2811
+ /** A valid reading's answer → `SiteReading` (the analysis alone answers `{ analysis }`; the facts made a map again). */
2805
2812
  declare function decodeSiteReading(value: unknown, pictureCount: number): SiteReading;
2806
2813
  /** A reference picture as the server read it: its measured palette (and where it is). */
2807
2814
  interface MeasuredPicture {
@@ -2987,5 +2994,5 @@ type SiteAuditView = SiteAudit & {
2987
2994
  advice: LintFinding[];
2988
2995
  };
2989
2996
 
2990
- export { ANIMATE_ADVICE_STEPS, AnalysisIssueCode, AnalysisLocale, AvoidCheck, BUSINESS_TYPES, BUSINESS_TYPE_IDS, BookingNeed, COPY_LENGTH_ORDER, CopyLength, GENERATION_LIMITS, HARNESS_SOURCE, LITTLE_MOVEMENT_AVOID, LITTLE_MOVEMENT_CAP, LintKind, MAX_ANALYSIS_MOODS, MAX_PIXEL_SAMPLES, MAX_READ_PICTURES, NEEDS, NEED_IDS, NeedSource, PALETTE_SIZE, PALETTE_THRESHOLDS, PictureRole, PlanChangeOutcome, PresetFit, RANK_WEIGHTS, READING_SHAPE, READ_ANIMATE, READ_WEIGHTS, RankReason, ReaderState, ReadingIssueCode, SITE_ANALYSIS_SCHEMA_NAME, SITE_READING_SCHEMA_NAME, SPEC_ANALYSIS_SHAPE, SPEC_BUSINESS_TYPES, SPEC_HARNESS_EXAMPLES, SPEC_HARNESS_SOURCE, SPEC_NEEDS, SPEC_STRUCTURES, SPEC_USER_AVOIDS, STRUCTURES, SiteReadRefusal, StyleReadGround, StyleReadHeadings, StyleReadMotion, StyleReadSourceKind, USER_AVOID_IDS, VoiceFormality, VoicePerson, addressesIn, analysisPromptContext, businessTypeById, calmerChoice, decodeSiteReading, inferNeeds, isLookRule, lintDesign, lookMatches, mapStyleRead, matchBusinessType, matchBusinessTypes, measurePalette, mergeStyleReads, motionAdvice, motionFor, normalizeAddress, oklchHue, paletteBrand, paletteGround, paletteSamples, pictureStyleRead, pixelSamples, presetDesign, presetHasSerifHeadings, presetLook, presetReason, rankStylePresets, rankWithReferences, readPalette, referenceSeed, seedPassesPreset, siteAnalysisJsonSchema, siteReadingJsonSchema, startingDesign, structureFor, userAvoidRule, validateSiteAnalysis, validateSiteReading, withoutAddress };
2997
+ export { ANIMATE_ADVICE_STEPS, AnalysisIssueCode, AnalysisLocale, AvoidCheck, BUSINESS_TYPES, BUSINESS_TYPE_IDS, BookingNeed, COPY_LENGTH_ORDER, CopyLength, GENERATION_LIMITS, HARNESS_SOURCE, LITTLE_MOVEMENT_AVOID, LITTLE_MOVEMENT_CAP, LintKind, MAX_ANALYSIS_MOODS, MAX_PIXEL_SAMPLES, MAX_READ_PICTURES, NEEDS, NEED_IDS, NeedSource, PALETTE_SIZE, PALETTE_THRESHOLDS, PictureRole, PlanChangeOutcome, PresetFit, RANK_WEIGHTS, READING_SHAPE, READ_ANIMATE, READ_WEIGHTS, RankReason, ReaderState, ReadingIssueCode, SITE_ANALYSIS_SCHEMA_NAME, SITE_READING_SCHEMA_NAME, SPEC_ANALYSIS_SHAPE, SPEC_BUSINESS_TYPES, SPEC_HARNESS_EXAMPLES, SPEC_HARNESS_SOURCE, SPEC_NEEDS, SPEC_STRUCTURES, SPEC_USER_AVOIDS, STRUCTURES, SiteReadRefusal, StyleReadGround, StyleReadHeadings, StyleReadMotion, StyleReadSourceKind, USER_AVOID_IDS, VoiceFormality, VoicePerson, addressesIn, analysisFromWire, analysisPromptContext, analysisToWire, businessTypeById, calmerChoice, decodeSiteReading, inferNeeds, isLookRule, lintDesign, lookMatches, mapStyleRead, matchBusinessType, matchBusinessTypes, measurePalette, mergeStyleReads, motionAdvice, motionFor, normalizeAddress, oklchHue, paletteBrand, paletteGround, paletteSamples, pictureStyleRead, pixelSamples, presetDesign, presetHasSerifHeadings, presetLook, presetReason, rankStylePresets, rankWithReferences, readPalette, referenceSeed, seedPassesPreset, siteAnalysisJsonSchema, siteReadingJsonSchema, startingDesign, structureFor, userAvoidRule, validateSiteAnalysis, validateSiteReading, withoutAddress };
2991
2998
  export type { AiGenerationView, AnalysisContext, AnalysisIssue, AnalysisValidation, AvoidRule, BusinessType, BusinessTypeMatch, CheckRule, ColourSample, FactRead, GenerationFileView, GenerationReadersView, GenerationStartRequest, GoodFit, LintFinding, LintSite, LookCondition, LookDesign, LookRule, MeasuredPicture, MotionBasis, MotionInput, MotionStart, NeedDef, NeedId, PaletteColour, PaletteReading, PictureStyle, PlanRevisionView, PromptContext, PromptContextOptions, PromptSection, RankInput, RankedPreset, ReadingIssue, ReferenceSeed, SiteAnalysis, SiteAuditView, SiteNeed, SiteReaderView, SiteReading, StartingDesign, StructureChoice, StructureId, StyleHints, StyleRead, UserAvoidId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.62.1",
3
+ "version": "5.63.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -1,6 +1,6 @@
1
1
  import { MOOD_IDS, MoodId } from '@vectoriox/iox-ui/design-core';
2
2
  import {
3
- describeShapeKey, FACT_KEYS, FactKey, isPlainObject, jsonClosedObject, jsonListOf, jsonOneOf, JsonSchema, jsonShapedObject, jsonText,
3
+ describeShapeKey, FACT_KEYS, FactKey, isPlainObject, jsonListOf, jsonOneOf, JsonSchema, jsonShapedObject, jsonText,
4
4
  PathIssue, PathIssueCollector, pathAt, PlainObject, requiredShapeKeys, ShapeEntry, shapeKeys,
5
5
  } from '@vectoriox/iox-ui/generation-core';
6
6
 
@@ -187,6 +187,11 @@ export function validateSiteAnalysis(value: unknown, ctx: AnalysisContext): Anal
187
187
  * native strict modes accept (Anthropic `output_config.format`, OpenRouter `json_schema` + `strict`): every object
188
188
  * closes with `additionalProperties: false`; no length, count or number bounds (the gate checks those). The
189
189
  * schema is a hint the provider enforces; `validateSiteAnalysis` stays the gate.
190
+ *
191
+ * ONE difference from the document, the plan wire's rule "maps as lists" (vectoriox/iox-cms#47 — Claude refuses a
192
+ * grammar compiled too large, and a map of the five facts, each `{ value, quote }`, was 15 of the reading's 47
193
+ * properties): on the wire `facts` is a list `[{ fact, value, quote }]`, a stated fact once each. `analysisFromWire`
194
+ * makes it the map `validateSiteAnalysis` judges; `analysisToWire` is its inverse (fakes and specs write it).
190
195
  */
191
196
 
192
197
  export type { JsonSchema } from '@vectoriox/iox-ui/generation-core';
@@ -201,19 +206,26 @@ const listOf = jsonListOf;
201
206
  /** An object of `shape`: exactly its keys, its required ones required, each described by the table. */
202
207
  const shaped = (shape: string, properties: Record<string, JsonSchema>): JsonSchema => jsonShapedObject(SHAPE[shape], properties, shape);
203
208
 
204
- /** `{ [fact]: factRead }`: the closed fact keys, each optional — a fact the prompt does not state is left out. */
205
- function factsSchema(factRead: JsonSchema): JsonSchema {
206
- return jsonClosedObject(Object.fromEntries(FACT_KEYS.map(key => [key, factRead])), [], describeShapeKey(SHAPE['analysis'], 'facts'));
207
- }
209
+ /** A stated fact on the wire: which fact, then the document's `factRead` keys in the table's own words. */
210
+ const FACT_LINE_SHAPE: ShapeEntry = {
211
+ fact: `${FACT_KEYS.map(k => `'${k}'`).join(' | ')} — which fact (site.facts), each at most once`,
212
+ ...SHAPE['factRead'],
213
+ };
214
+
215
+ /** The analysis as the wire carries it: `facts` a list of fact lines, described in the table's words. */
216
+ const ANALYSIS_WIRE_SHAPE: ShapeEntry = {
217
+ ...SHAPE['analysis'],
218
+ facts: `factLine[] — ${describeShapeKey(SHAPE['analysis'], 'facts').replace(/^\{ \[fact\]: factRead \} — /, '')}`,
219
+ };
208
220
 
209
221
  export function siteAnalysisJsonSchema(): JsonSchema {
210
- const factRead = shaped('factRead', { value: text(), quote: text() });
211
- return shaped('analysis', {
222
+ const factLine = jsonShapedObject(FACT_LINE_SHAPE, { fact: oneOf(FACT_KEYS), value: text(), quote: text() }, 'factLine');
223
+ return jsonShapedObject(ANALYSIS_WIRE_SHAPE, {
212
224
  businessType: oneOf(BUSINESS_TYPE_IDS),
213
225
  alternatives: listOf(oneOf(BUSINESS_TYPE_IDS)),
214
226
  business: shaped('business', { name: text(), audience: text() }),
215
227
  needs: listOf(shaped('need', { id: oneOf(NEED_IDS), from: oneOf(Object.values(NeedSource)), name: text() })),
216
- facts: factsSchema(factRead),
228
+ facts: listOf(factLine),
217
229
  missingFacts: listOf(oneOf(FACT_KEYS)),
218
230
  voice: shaped('voice', {
219
231
  tone: text(),
@@ -225,5 +237,28 @@ export function siteAnalysisJsonSchema(): JsonSchema {
225
237
  avoid: listOf(oneOf(USER_AVOID_IDS)),
226
238
  hero: shaped('hero', { headline: text(), lead: text() }),
227
239
  locale: oneOf(Object.values(AnalysisLocale)),
240
+ }, 'analysis');
241
+ }
242
+
243
+ /** A model's analysis (the wire) → the document, and the faults only the wire can have (a fact stated twice). Never repairs. */
244
+ export function analysisFromWire(wire: unknown): { analysis: unknown; issues: AnalysisIssue[] } {
245
+ if (!isPlainObject(wire)) return { analysis: wire, issues: [] };
246
+ if (!Array.isArray(wire['facts'])) return { analysis: wire, issues: [{ code: AnalysisIssueCode.Shape, path: 'facts', message: 'must be a list of { fact, value, quote }' }] };
247
+ const facts: PlainObject = {};
248
+ const issues: AnalysisIssue[] = [];
249
+ (wire['facts'] as unknown[]).forEach((line, i) => {
250
+ if (!isPlainObject(line) || typeof line['fact'] !== 'string') {
251
+ issues.push({ code: AnalysisIssueCode.Shape, path: `facts[${i}]`, message: 'must be { fact, value, quote }' });
252
+ return;
253
+ }
254
+ const { fact, ...read } = line;
255
+ if (fact in facts) issues.push({ code: AnalysisIssueCode.Duplicate, path: `facts[${i}].fact`, message: `"${fact}" is stated twice` });
256
+ else facts[fact as string] = read;
228
257
  });
258
+ return { analysis: { ...wire, facts }, issues };
259
+ }
260
+
261
+ /** The document → the wire (what a model writes). */
262
+ export function analysisToWire(analysis: SiteAnalysis): PlainObject {
263
+ return { ...analysis, facts: Object.entries(analysis.facts).map(([fact, read]) => ({ fact, ...read })) };
229
264
  }
@@ -6,7 +6,7 @@ import {
6
6
 
7
7
  import { paletteSamples, readPalette } from './palette';
8
8
  import { StyleRead, StyleReadGround, StyleReadHeadings, StyleReadSourceKind } from './rank';
9
- import { AnalysisIssue, MAX_ANALYSIS_MOODS, SiteAnalysis, siteAnalysisJsonSchema, validateSiteAnalysis } from './site-analysis';
9
+ import { AnalysisIssue, analysisFromWire, MAX_ANALYSIS_MOODS, SiteAnalysis, siteAnalysisJsonSchema, validateSiteAnalysis } from './site-analysis';
10
10
 
11
11
  /**
12
12
  * The READING call (AI plan §3d step 1, §3k R2, M1-1): ONE `text.fast` call reads the owner's words AND the pictures
@@ -129,23 +129,32 @@ function validatePictures(c: Collector, raw: unknown, count: number): void {
129
129
  c.oneOf(style, 'density', Object.keys(SPEC_AXES.densities), 'pictures.style');
130
130
  }
131
131
 
132
+ /** The analysis on the wire, judged as the document it becomes: the wire's own faults, then `validateSiteAnalysis`'s. */
133
+ function analysisIssues(wire: unknown, prompt: string): AnalysisIssue[] {
134
+ const read = analysisFromWire(wire);
135
+ return [...read.issues, ...validateSiteAnalysis(read.analysis, { prompt }).issues];
136
+ }
137
+
132
138
  /** The reading's gate: the analysis by `validateSiteAnalysis` (paths under `analysis.` when pictures came too), the pictures by `READING_SHAPE`. */
133
139
  export function validateSiteReading(value: unknown, ctx: { prompt: string; pictureCount: number }): { valid: boolean; issues: ReadingIssue[] } {
134
- if (ctx.pictureCount <= 0) return validateSiteAnalysis(value, { prompt: ctx.prompt });
140
+ if (ctx.pictureCount <= 0) {
141
+ const issues = analysisIssues(value, ctx.prompt);
142
+ return { valid: issues.length === 0, issues };
143
+ }
135
144
  const c = new Collector();
136
145
  const reading = c.object(value, 'reading', '');
137
146
  if (!reading) return { valid: false, issues: c.issues };
138
- const analysis = validateSiteAnalysis(reading['analysis'], { prompt: ctx.prompt });
139
- const issues: ReadingIssue[] = analysis.issues.map(i => ({ ...i, path: pathAt('analysis', i.path) }));
147
+ const issues: ReadingIssue[] = analysisIssues(reading['analysis'], ctx.prompt).map(i => ({ ...i, path: pathAt('analysis', i.path) }));
140
148
  validatePictures(c, reading['pictures'], ctx.pictureCount);
141
149
  issues.push(...c.issues);
142
150
  return { valid: issues.length === 0, issues };
143
151
  }
144
152
 
145
- /** A valid reading's answer → `SiteReading` (the analysis alone answers `{ analysis }`). */
153
+ /** A valid reading's answer → `SiteReading` (the analysis alone answers `{ analysis }`; the facts made a map again). */
146
154
  export function decodeSiteReading(value: unknown, pictureCount: number): SiteReading {
147
- if (pictureCount <= 0) return { analysis: value as SiteAnalysis };
148
- return value as SiteReading;
155
+ if (pictureCount <= 0) return { analysis: analysisFromWire(value).analysis as SiteAnalysis };
156
+ const reading = value as SiteReading;
157
+ return { ...reading, analysis: analysisFromWire(reading.analysis).analysis as SiteAnalysis };
149
158
  }
150
159
 
151
160
  // ── Readings → ONE StyleRead ───────────────────────────────────────────────────────────────────────────