@vectoriox/iox-ui 5.83.1 → 5.84.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, PlainObject, JsonSchema, SiteTemplate, ShapeEntry, AiGenerationStage, AiStep, SitePlan, PlanEdit, PlanChange, SiteAudit } from '@vectoriox/iox-ui/generation-core';
2
+ import { FactKey, PictureWorld, PathIssue, PlainObject, JsonSchema, SiteTemplate, ShapeEntry, AiGenerationStage, AiStep, SitePlan, PlanEdit, PlanChange, PictureRefusal, AiRefusalCode, SiteAudit } from '@vectoriox/iox-ui/generation-core';
3
3
  export { JsonSchema } from '@vectoriox/iox-ui/generation-core';
4
4
  import { FrameSequence } from '@vectoriox/iox-ui/media-core';
5
5
 
@@ -157,6 +157,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
157
157
  readonly between: "cinematic";
158
158
  };
159
159
  readonly ceiling: "expressive";
160
+ readonly world: "documentary";
160
161
  readonly imagery: "Your own photos only, large and uncropped; never stock.";
161
162
  readonly pages: readonly ["home", "work (collection)", "project (detail)", "about", "contact"];
162
163
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -220,6 +221,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
220
221
  readonly between: "cinematic";
221
222
  };
222
223
  readonly ceiling: "expressive";
224
+ readonly world: "architectural";
223
225
  readonly imagery: "Wide, bright room shots; details of materials.";
224
226
  readonly pages: readonly ["home", "projects (collection)", "project (detail)", "services", "about", "contact"];
225
227
  readonly needs: readonly ["collection", "detail", "services", "about", "contact"];
@@ -284,6 +286,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
284
286
  readonly between: "fade";
285
287
  };
286
288
  readonly ceiling: "expressive";
289
+ readonly world: "architectural";
287
290
  readonly imagery: "Large project photos and drawings.";
288
291
  readonly pages: readonly ["home", "projects (collection)", "project (detail)", "about", "contact"];
289
292
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -349,6 +352,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
349
352
  readonly between: "slide";
350
353
  };
351
354
  readonly ceiling: "expressive";
355
+ readonly world: "hard-light";
352
356
  readonly imagery: "Case-study images of your own work.";
353
357
  readonly pages: readonly ["home", "work (collection)", "case study (detail)", "services", "about", "contact"];
354
358
  readonly needs: readonly ["collection", "detail", "services", "about", "contact"];
@@ -404,6 +408,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
404
408
  readonly between: "fade";
405
409
  };
406
410
  readonly ceiling: "expressive";
411
+ readonly world: "drawing";
407
412
  readonly imagery: "Your pieces, photographed flat and true to colour.";
408
413
  readonly pages: readonly ["home", "works (collection)", "work (detail)", "about", "contact"];
409
414
  readonly needs: readonly ["collection", "about", "contact"];
@@ -461,6 +466,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
461
466
  readonly between: "slide";
462
467
  };
463
468
  readonly ceiling: "expressive";
469
+ readonly world: "macro";
464
470
  readonly imagery: "Your pieces and hands at work; natural light.";
465
471
  readonly pages: readonly ["home", "pieces (collection)", "piece (detail)", "workshops", "about", "contact"];
466
472
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -517,6 +523,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
517
523
  readonly between: "cinematic";
518
524
  };
519
525
  readonly ceiling: "expressive";
526
+ readonly world: "macro";
520
527
  readonly imagery: "Close-ups on a plain ground.";
521
528
  readonly pages: readonly ["home", "collection", "piece (detail)", "about", "contact"];
522
529
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -574,6 +581,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
574
581
  readonly between: "slide";
575
582
  };
576
583
  readonly ceiling: "expressive";
584
+ readonly world: "low-key";
577
585
  readonly imagery: "Healed work, clean photos.";
578
586
  readonly pages: readonly ["home", "work (collection)", "artists", "faq", "contact"];
579
587
  readonly needs: readonly ["collection", "faq", "contact"];
@@ -630,6 +638,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
630
638
  readonly between: "fade";
631
639
  };
632
640
  readonly ceiling: "standard";
641
+ readonly world: "high-key";
633
642
  readonly imagery: "Rooms, textures, hands; no faces needed.";
634
643
  readonly pages: readonly ["home", "treatments (services)", "prices", "about", "contact"];
635
644
  readonly needs: readonly ["services", "prices", "about", "contact"];
@@ -696,6 +705,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
696
705
  readonly between: "fade";
697
706
  };
698
707
  readonly ceiling: "lively";
708
+ readonly world: "high-key";
699
709
  readonly imagery: "Your own before-and-after photos.";
700
710
  readonly pages: readonly ["home", "services", "prices", "gallery", "contact"];
701
711
  readonly needs: readonly ["services", "prices", "gallery", "contact"];
@@ -756,6 +766,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
756
766
  readonly between: "fade";
757
767
  };
758
768
  readonly ceiling: "lively";
769
+ readonly world: "high-key";
759
770
  readonly imagery: "The room, real classes, daylight.";
760
771
  readonly pages: readonly ["home", "classes", "schedule", "teachers", "prices", "contact"];
761
772
  readonly needs: readonly ["prices", "contact"];
@@ -817,6 +828,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
817
828
  readonly between: "slide";
818
829
  };
819
830
  readonly ceiling: "lively";
831
+ readonly world: "hard-light";
820
832
  readonly imagery: "Real people training; movement.";
821
833
  readonly pages: readonly ["home", "classes", "prices", "coaches", "contact"];
822
834
  readonly needs: readonly ["prices", "contact"];
@@ -874,6 +886,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
874
886
  readonly between: "none";
875
887
  };
876
888
  readonly ceiling: "subtle";
889
+ readonly world: "documentary";
877
890
  readonly imagery: "A calm room, daylight; your portrait.";
878
891
  readonly pages: readonly ["home", "how I work", "about", "faq", "contact"];
879
892
  readonly needs: readonly ["collection", "about", "faq", "contact"];
@@ -935,6 +948,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
935
948
  readonly between: "fade";
936
949
  };
937
950
  readonly ceiling: "subtle";
951
+ readonly world: "high-key";
938
952
  readonly imagery: "Your team and rooms; no stock doctors.";
939
953
  readonly pages: readonly ["home", "treatments (services)", "team", "faq", "contact"];
940
954
  readonly needs: readonly ["services", "team", "faq", "contact"];
@@ -997,6 +1011,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
997
1011
  readonly between: "fade";
998
1012
  };
999
1013
  readonly ceiling: "subtle";
1014
+ readonly world: "high-key";
1000
1015
  readonly imagery: "Your clinic and team.";
1001
1016
  readonly pages: readonly ["home", "treatments", "team", "faq", "contact"];
1002
1017
  readonly needs: readonly ["services", "team", "faq", "contact"];
@@ -1057,6 +1072,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1057
1072
  readonly between: "fade";
1058
1073
  };
1059
1074
  readonly ceiling: "standard";
1075
+ readonly world: "documentary";
1060
1076
  readonly imagery: "Real animals you treat (with permission).";
1061
1077
  readonly pages: readonly ["home", "services", "team", "faq", "contact"];
1062
1078
  readonly needs: readonly ["services", "team", "faq", "contact"];
@@ -1113,6 +1129,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1113
1129
  readonly between: "fade";
1114
1130
  };
1115
1131
  readonly ceiling: "expressive";
1132
+ readonly world: "low-key";
1116
1133
  readonly imagery: "Your own food and room photos.";
1117
1134
  readonly pages: readonly ["home", "menu", "about", "contact"];
1118
1135
  readonly needs: readonly ["menu", "about", "contact"];
@@ -1176,6 +1193,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1176
1193
  readonly between: "slide";
1177
1194
  };
1178
1195
  readonly ceiling: "lively";
1196
+ readonly world: "documentary";
1179
1197
  readonly imagery: "Your bakes and drinks, close up.";
1180
1198
  readonly pages: readonly ["home", "menu", "about", "contact"];
1181
1199
  readonly needs: readonly ["menu", "about", "contact"];
@@ -1236,6 +1254,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1236
1254
  readonly between: "cinematic";
1237
1255
  };
1238
1256
  readonly ceiling: "expressive";
1257
+ readonly world: "nocturne";
1239
1258
  readonly imagery: "The cellar, the bar, the bottle.";
1240
1259
  readonly pages: readonly ["home", "menu or wines", "our story", "visit", "contact"];
1241
1260
  readonly needs: readonly ["menu", "visit", "contact"];
@@ -1296,6 +1315,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1296
1315
  readonly between: "slide";
1297
1316
  };
1298
1317
  readonly ceiling: "expressive";
1318
+ readonly world: "high-key";
1299
1319
  readonly imagery: "Product photos on plain grounds; real people wearing them.";
1300
1320
  readonly pages: readonly ["home", "collection", "product (detail)", "about", "contact"];
1301
1321
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -1351,6 +1371,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1351
1371
  readonly between: "fade";
1352
1372
  };
1353
1373
  readonly ceiling: "lively";
1374
+ readonly world: "macro";
1354
1375
  readonly imagery: "Your own arrangements in daylight.";
1355
1376
  readonly pages: readonly ["home", "flowers (collection)", "weddings and events", "about", "contact"];
1356
1377
  readonly needs: readonly ["collection", "about", "contact"];
@@ -1407,6 +1428,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1407
1428
  readonly between: "fade";
1408
1429
  };
1409
1430
  readonly ceiling: "expressive";
1431
+ readonly world: "documentary";
1410
1432
  readonly imagery: "Pieces in real rooms and on plain grounds.";
1411
1433
  readonly pages: readonly ["home", "collection", "product (detail)", "about", "contact"];
1412
1434
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -1463,6 +1485,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1463
1485
  readonly between: "fade";
1464
1486
  };
1465
1487
  readonly ceiling: "standard";
1488
+ readonly world: "architectural";
1466
1489
  readonly imagery: "Your portrait; diagrams over stock.";
1467
1490
  readonly pages: readonly ["home", "services", "case studies", "about", "contact"];
1468
1491
  readonly needs: readonly ["services", "about", "contact"];
@@ -1521,6 +1544,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1521
1544
  readonly between: "fade";
1522
1545
  };
1523
1546
  readonly ceiling: "standard";
1547
+ readonly world: "documentary";
1524
1548
  readonly imagery: "Your portrait, working with people.";
1525
1549
  readonly pages: readonly ["home", "how it works", "programmes", "about", "contact"];
1526
1550
  readonly needs: readonly ["collection", "about", "contact"];
@@ -1573,6 +1597,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1573
1597
  readonly between: "none";
1574
1598
  };
1575
1599
  readonly ceiling: "subtle";
1600
+ readonly world: "architectural";
1576
1601
  readonly imagery: "Your team; your office.";
1577
1602
  readonly pages: readonly ["home", "practice areas (services)", "team", "about", "contact"];
1578
1603
  readonly needs: readonly ["services", "team", "about", "contact"];
@@ -1636,6 +1661,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1636
1661
  readonly between: "slide";
1637
1662
  };
1638
1663
  readonly ceiling: "lively";
1664
+ readonly world: "architectural";
1639
1665
  readonly imagery: "Your own property photos; never stock.";
1640
1666
  readonly pages: readonly ["home", "properties (collection)", "property (detail)", "about", "contact"];
1641
1667
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -1695,6 +1721,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1695
1721
  readonly between: "fade";
1696
1722
  };
1697
1723
  readonly ceiling: "standard";
1724
+ readonly world: "documentary";
1698
1725
  readonly imagery: "Real people you help (with permission).";
1699
1726
  readonly pages: readonly ["home", "what we do", "stories", "get involved", "contact"];
1700
1727
  readonly needs: readonly ["contact"];
@@ -1747,6 +1774,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1747
1774
  readonly between: "cinematic";
1748
1775
  };
1749
1776
  readonly ceiling: "expressive";
1777
+ readonly world: "nocturne";
1750
1778
  readonly imagery: "Real events at your venue.";
1751
1779
  readonly pages: readonly ["home", "spaces", "gallery", "faq", "contact"];
1752
1780
  readonly needs: readonly ["gallery", "faq", "contact"];
@@ -1806,6 +1834,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1806
1834
  readonly between: "cinematic";
1807
1835
  };
1808
1836
  readonly ceiling: "expressive";
1837
+ readonly world: "documentary";
1809
1838
  readonly imagery: "Rooms, views, breakfast; your own photos.";
1810
1839
  readonly pages: readonly ["home", "rooms (collection)", "room (detail)", "about", "contact"];
1811
1840
  readonly needs: readonly ["collection", "detail", "about", "contact"];
@@ -1865,6 +1894,7 @@ declare const SPEC_BUSINESS_TYPES: readonly [{
1865
1894
  readonly between: "slide";
1866
1895
  };
1867
1896
  readonly ceiling: "lively";
1897
+ readonly world: "documentary";
1868
1898
  readonly imagery: "Real classes (with permission).";
1869
1899
  readonly pages: readonly ["home", "classes", "prices", "about", "contact"];
1870
1900
  readonly needs: readonly ["prices", "about", "contact"];
@@ -2273,6 +2303,8 @@ interface BusinessType {
2273
2303
  * so a type capped below it never gets one. Binds generated output only — the owner may choose any level. */
2274
2304
  readonly ceiling: DesignMotion['animate'];
2275
2305
  readonly imagery: string;
2306
+ /** M7: the picture world a site's made pictures start in (generation-core `artDirectionFor`; contract site.pictureWorlds). */
2307
+ readonly world: `${PictureWorld}`;
2276
2308
  readonly pages: ReadonlyArray<string>;
2277
2309
  /** The needs its page set implies, in page order (derived from `pages` at contract build). */
2278
2310
  readonly needs: ReadonlyArray<NeedId>;
@@ -2943,6 +2975,42 @@ interface PlanRevisionView {
2943
2975
  edits?: PlanEdit[];
2944
2976
  changed?: PlanChange[];
2945
2977
  }
2978
+ /** Why no picture can be made for this site right now (the Plan step says it instead of offering the switch). */
2979
+ declare enum PicturesUnavailable {
2980
+ /** No slot is left after the owner's photos and the refusals. */
2981
+ Nothing = "nothing",
2982
+ /** The key in use has no model that makes pictures (a Claude key does not). */
2983
+ NoModel = "no-model"
2984
+ }
2985
+ /**
2986
+ * M7: the pictures a generation may have MADE, as the Plan step shows them before the owner says yes, and while they are
2987
+ * made. `estimate` is the cost shown — every picture and its review plus the second tries (generation-core
2988
+ * `estimatePictures`) — and the most the run may spend on them.
2989
+ */
2990
+ interface GenerationPicturesView {
2991
+ /** Pictures that would be made (after the owner's photos and the refusals). */
2992
+ count: number;
2993
+ /** The owner's photos that go first. */
2994
+ own: number;
2995
+ /** Slots no picture is made for, by why. */
2996
+ refused: Partial<Record<`${PictureRefusal}`, number>>;
2997
+ /** Credits — the cost shown and the run's ceiling. */
2998
+ estimate: number;
2999
+ /** One picture with its review, in credits. */
3000
+ each: number;
3001
+ rerolls: number;
3002
+ /** The model that makes them, or null. */
3003
+ model: string | null;
3004
+ /** Why none can be made now — instead of the switch. A refusal code when credits or the plan say no. */
3005
+ unavailable?: `${PicturesUnavailable}` | `${AiRefusalCode}`;
3006
+ /** The owner said yes (Build with pictures): the run's progress. */
3007
+ wanted?: boolean;
3008
+ made?: number;
3009
+ /** Slots whose review said no to every try — stock or the empty box stands in. */
3010
+ failed?: number;
3011
+ /** Credits the pictures have cost so far (the ledger). */
3012
+ spent?: number;
3013
+ }
2946
3014
  interface AiGenerationView {
2947
3015
  _id: string;
2948
3016
  organization: string;
@@ -3001,6 +3069,16 @@ interface AiGenerationView {
3001
3069
  path: string;
3002
3070
  }>;
3003
3071
  };
3072
+ /** M7: the pictures that may be made (on the Plan step) or are being made. Absent: no plan yet. */
3073
+ pictures?: GenerationPicturesView;
3074
+ }
3075
+ /** `POST …/ai/generations/:id/build` (and `/auto`). */
3076
+ interface GenerationBuildRequest {
3077
+ replaceGlobals?: boolean;
3078
+ /** SC6b: the owner's film as pictures. */
3079
+ film?: FrameSequence;
3080
+ /** M7: make pictures for the site first (the cost shown on the Plan step). */
3081
+ pictures?: boolean;
3004
3082
  }
3005
3083
  /** `POST …/ai/generations`. */
3006
3084
  interface GenerationStartRequest {
@@ -3052,5 +3130,5 @@ type SiteAuditView = SiteAudit & {
3052
3130
  advice: LintFinding[];
3053
3131
  };
3054
3132
 
3055
- 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, bestRankedPreset, 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, presetsByFlavor, rankStylePresets, rankWithReferences, readPalette, referenceSeed, seedPassesPreset, siteAnalysisJsonSchema, siteReadingJsonSchema, startingDesign, structureFor, userAvoidRule, validateSiteAnalysis, validateSiteReading, withoutAddress };
3056
- 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, PresetFlavorGroup, PromptContext, PromptContextOptions, PromptSection, RankInput, RankedPreset, ReadingIssue, ReferenceSeed, SiteAnalysis, SiteAuditView, SiteNeed, SiteReaderView, SiteReading, StartingDesign, StructureChoice, StructureId, StyleHints, StyleRead, UserAvoidId };
3133
+ 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, PicturesUnavailable, 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, bestRankedPreset, 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, presetsByFlavor, rankStylePresets, rankWithReferences, readPalette, referenceSeed, seedPassesPreset, siteAnalysisJsonSchema, siteReadingJsonSchema, startingDesign, structureFor, userAvoidRule, validateSiteAnalysis, validateSiteReading, withoutAddress };
3134
+ export type { AiGenerationView, AnalysisContext, AnalysisIssue, AnalysisValidation, AvoidRule, BusinessType, BusinessTypeMatch, CheckRule, ColourSample, FactRead, GenerationBuildRequest, GenerationFileView, GenerationPicturesView, GenerationReadersView, GenerationStartRequest, GoodFit, LintFinding, LintSite, LookCondition, LookDesign, LookRule, MeasuredPicture, MotionBasis, MotionInput, MotionStart, NeedDef, NeedId, PaletteColour, PaletteReading, PictureStyle, PlanRevisionView, PresetFlavorGroup, 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.83.1",
3
+ "version": "5.84.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -27,6 +27,8 @@ export enum AiStep {
27
27
  PlanSite = 'plan',
28
28
  ChangePlan = 'planEdit',
29
29
  MakePictures = 'image',
30
+ /** M7: a small vision model looks at each made picture against its slot (one structured answer). */
31
+ CheckPictures = 'imageReview',
30
32
  NameStructures = 'scanNaming',
31
33
  }
32
34
 
@@ -38,6 +40,7 @@ export const AI_STEP_LABELS: Readonly<Record<AiStep, string>> = {
38
40
  [AiStep.PlanSite]: 'Plan the site',
39
41
  [AiStep.ChangePlan]: 'Change the plan',
40
42
  [AiStep.MakePictures]: 'Make pictures',
43
+ [AiStep.CheckPictures]: 'Check pictures',
41
44
  [AiStep.NameStructures]: 'Name repeated structures',
42
45
  };
43
46
 
@@ -53,6 +56,7 @@ export const AI_STEP_NEEDS: Readonly<Record<AiStep, ReadonlyArray<AiCapability>>
53
56
  [AiStep.PlanSite]: [AiCapability.StructuredOutput],
54
57
  [AiStep.ChangePlan]: [AiCapability.StructuredOutput],
55
58
  [AiStep.MakePictures]: [AiCapability.ImageOutput],
59
+ [AiStep.CheckPictures]: [AiCapability.StructuredOutput, AiCapability.ImageInput],
56
60
  [AiStep.NameStructures]: [AiCapability.StructuredOutput],
57
61
  };
58
62
 
@@ -91,8 +95,12 @@ export const AI_STEP_TYPICAL_TOKENS: Readonly<Record<AiStep, AiTokenCounts>> = {
91
95
  [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
92
96
  [AiStep.PlanSite]: partsTotal(AiStep.PlanSite),
93
97
  [AiStep.ChangePlan]: partsTotal(AiStep.ChangePlan),
94
- // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
95
- [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
98
+ // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4; the prompt (the art direction's
99
+ // preamble, the slot's subject, its frame and empty area) about 400 more in (M7)
100
+ [AiStep.MakePictures]: { input: 400, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
101
+ // M7: one picture downscaled to 512 px (≈ 350 image tokens for Claude, ≈ 260 for Gemini) + the slot's question,
102
+ // and a short structured answer
103
+ [AiStep.CheckPictures]: { input: 1_200, output: 150, cacheWrite: 0, cacheRead: 0 },
96
104
  [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
97
105
  };
98
106
 
@@ -0,0 +1,122 @@
1
+ import { DesignChoices, parseHexColor, rgbToOklab } from '@vectoriox/iox-ui/design-core';
2
+
3
+ import { SPEC_SITE_PICTURE_NEVER, SPEC_SITE_PICTURE_WORLDS } from './site-spec.generated';
4
+
5
+ /**
6
+ * A site's ART DIRECTION (premium-components M7, premium sites PS4): what every picture made for the site shares, so its
7
+ * pictures read as one photographer's and never as a stock pool. A closed WORLD (the contract's `site.pictureWorlds`)
8
+ * gives the five parts — medium, light, grade, texture and a negative list — and ONE preamble built from them is carried
9
+ * VERBATIM at the head of every picture prompt of the site (`artDirectionPreamble`).
10
+ *
11
+ * Decided here, not by a model: the world is the business type's (harness-core `BusinessType.world`), a dark look moves it
12
+ * to the world's `dark`, and the brand's hue joins the grade. Zero tokens, the same site always gets the same direction,
13
+ * and the quality lever — each world's wording — is written once in the contract, as the proof sites' briefs were.
14
+ */
15
+
16
+ export enum PictureWorld {
17
+ Documentary = 'documentary',
18
+ HighKey = 'high-key',
19
+ LowKey = 'low-key',
20
+ HardLight = 'hard-light',
21
+ Macro = 'macro',
22
+ Architectural = 'architectural',
23
+ Nocturne = 'nocturne',
24
+ Drawing = 'drawing',
25
+ }
26
+
27
+ export const PICTURE_WORLD_IDS: ReadonlyArray<PictureWorld> = Object.values(PictureWorld);
28
+
29
+ /** One world as the contract writes it. */
30
+ export interface PictureWorldSpec {
31
+ readonly label: string;
32
+ readonly medium: string;
33
+ readonly light: string;
34
+ readonly grade: string;
35
+ readonly texture: string;
36
+ readonly negative: ReadonlyArray<string>;
37
+ /** Where a dark look's pictures go. */
38
+ readonly dark: string;
39
+ }
40
+
41
+ export const PICTURE_WORLDS = SPEC_SITE_PICTURE_WORLDS as unknown as Readonly<Record<PictureWorld, PictureWorldSpec>>;
42
+
43
+ /** What no picture of any site shows — every world adds its own after these. */
44
+ export const PICTURE_NEVER: ReadonlyArray<string> = SPEC_SITE_PICTURE_NEVER;
45
+
46
+ /** The art direction a plan carries (`plan.artDirection`): the world and its five parts as this site wears them. */
47
+ export interface ArtDirection {
48
+ readonly world: `${PictureWorld}`;
49
+ readonly medium: string;
50
+ readonly light: string;
51
+ readonly grade: string;
52
+ readonly texture: string;
53
+ readonly negative: ReadonlyArray<string>;
54
+ }
55
+
56
+ /** The five parts every art direction must carry, in the order the preamble says them. */
57
+ export const ART_DIRECTION_PARTS = ['medium', 'light', 'grade', 'texture', 'negative'] as const;
58
+
59
+ export function isPictureWorld(value: unknown): value is PictureWorld {
60
+ return typeof value === 'string' && (PICTURE_WORLD_IDS as ReadonlyArray<string>).includes(value);
61
+ }
62
+
63
+ /** OKLCH hue bands (degrees, upper bound) → the word a grade says; below the chroma floor a brand is neutral. */
64
+ const HUE_WORDS: ReadonlyArray<readonly [number, string]> = [
65
+ [12, 'rose'], [33, 'red'], [55, 'terracotta orange'], [80, 'warm amber'], [110, 'golden yellow'], [135, 'olive green'],
66
+ [170, 'green'], [215, 'teal'], [255, 'blue'], [285, 'deep blue'], [315, 'violet'], [345, 'plum'], [360, 'rose'],
67
+ ];
68
+ const NEUTRAL_CHROMA = 0.04;
69
+ const RADIANS_TO_DEGREES = 180 / Math.PI;
70
+ const FULL_TURN = 360;
71
+
72
+ /** The word for a brand colour's hue (`#9A4F2B` → "terracotta orange"), or null for a grey, a near-black or a bad value. */
73
+ export function brandHueWord(seed: string | null | undefined): string | null {
74
+ const rgb = seed ? parseHexColor(seed) : null;
75
+ if (!rgb) return null;
76
+ const { a, b } = rgbToOklab(rgb);
77
+ if (Math.hypot(a, b) < NEUTRAL_CHROMA) return null;
78
+ const hue = (Math.atan2(b, a) * RADIANS_TO_DEGREES + FULL_TURN) % FULL_TURN;
79
+ return HUE_WORDS.find(([upTo]) => hue < upTo)?.[1] ?? null;
80
+ }
81
+
82
+ /**
83
+ * The site's art direction: the business type's world (moved to its `dark` world when the look is dark), its five parts,
84
+ * the brand's hue in the grade, PICTURE_NEVER before the world's own negatives. An unknown world is documentary.
85
+ */
86
+ export function artDirectionFor(world: string | null | undefined, design: Pick<DesignChoices, 'mode' | 'seed'>): ArtDirection {
87
+ const start = isPictureWorld(world) ? world : PictureWorld.Documentary;
88
+ const moved = design.mode === 'dark' ? PICTURE_WORLDS[start].dark : start;
89
+ const id = isPictureWorld(moved) ? moved : start;
90
+ const spec = PICTURE_WORLDS[id];
91
+ const hue = brandHueWord(design.seed);
92
+ return {
93
+ world: id,
94
+ medium: spec.medium,
95
+ light: spec.light,
96
+ grade: hue ? `${spec.grade}, leaning toward ${hue} so the pictures sit with the brand` : spec.grade,
97
+ texture: spec.texture,
98
+ negative: [...PICTURE_NEVER, ...spec.negative],
99
+ };
100
+ }
101
+
102
+ /** The ONE preamble a site's every picture prompt opens with — the same words, in the same order, every time. */
103
+ export function artDirectionPreamble(direction: ArtDirection): string {
104
+ const medium = direction.medium.charAt(0).toUpperCase() + direction.medium.slice(1);
105
+ return `${medium}. Light: ${direction.light}. Colour: ${direction.grade}. Texture: ${direction.texture}. Never: ${direction.negative.join('; ')}.`;
106
+ }
107
+
108
+ /** What is wrong with a value as an art direction — a path and a message each (`validateSitePlan` reports them). */
109
+ export function artDirectionIssues(value: unknown): Array<{ path: string; message: string }> {
110
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return [{ path: '', message: 'must be an object' }];
111
+ const v = value as Record<string, unknown>;
112
+ const issues: Array<{ path: string; message: string }> = [];
113
+ if (!isPictureWorld(v['world'])) issues.push({ path: 'world', message: `must be one of ${PICTURE_WORLD_IDS.join(', ')}` });
114
+ for (const part of ART_DIRECTION_PARTS.filter(p => p !== 'negative')) {
115
+ if (typeof v[part] !== 'string' || !(v[part] as string).trim()) issues.push({ path: part, message: 'must be words' });
116
+ }
117
+ const negative = v['negative'];
118
+ if (!Array.isArray(negative) || !negative.length || negative.some(n => typeof n !== 'string' || !n.trim())) {
119
+ issues.push({ path: 'negative', message: 'must list what no picture may show' });
120
+ }
121
+ return issues;
122
+ }
@@ -51,6 +51,10 @@ export enum SiteFindingCode {
51
51
  CopyPlaceholder = 'copy-placeholder',
52
52
  SampleItems = 'sample-items',
53
53
  StockPhoto = 'stock-photo',
54
+ /** M7: a picture made with AI for the site still stands where one of the studio's own could. */
55
+ MadePicture = 'made-picture',
56
+ /** M7: a picture the build could not make (its review said no twice) and nothing stood in for: an empty box. */
57
+ PictureMissing = 'picture-missing',
54
58
  NoPrivacyPage = 'no-privacy-page',
55
59
  PrivacyPagePlaceholder = 'privacy-page-placeholder',
56
60
  LowContrast = 'low-contrast',
@@ -76,6 +80,8 @@ export const FINDING_SEVERITY: Readonly<Record<SiteFindingCode, CheckSeverity>>
76
80
  [SiteFindingCode.CopyPlaceholder]: CheckSeverity.Ask,
77
81
  [SiteFindingCode.SampleItems]: CheckSeverity.Ask,
78
82
  [SiteFindingCode.StockPhoto]: CheckSeverity.Ask,
83
+ [SiteFindingCode.MadePicture]: CheckSeverity.Ask,
84
+ [SiteFindingCode.PictureMissing]: CheckSeverity.Warn,
79
85
  [SiteFindingCode.NoPrivacyPage]: CheckSeverity.Ask,
80
86
  [SiteFindingCode.PrivacyPagePlaceholder]: CheckSeverity.Block,
81
87
  [SiteFindingCode.LowContrast]: CheckSeverity.Warn,
@@ -100,6 +106,8 @@ const CHECK_OF: Readonly<Record<SiteFindingCode, SiteCheckId>> = {
100
106
  [SiteFindingCode.CopyPlaceholder]: SiteCheckId.Facts,
101
107
  [SiteFindingCode.SampleItems]: SiteCheckId.Facts,
102
108
  [SiteFindingCode.StockPhoto]: SiteCheckId.Facts,
109
+ [SiteFindingCode.MadePicture]: SiteCheckId.Facts,
110
+ [SiteFindingCode.PictureMissing]: SiteCheckId.Facts,
103
111
  [SiteFindingCode.NoPrivacyPage]: SiteCheckId.Facts,
104
112
  [SiteFindingCode.PrivacyPagePlaceholder]: SiteCheckId.Facts,
105
113
  [SiteFindingCode.LowContrast]: SiteCheckId.Accessibility,
@@ -164,6 +172,10 @@ export interface SiteAuditSnapshot {
164
172
  samples?: ReadonlyArray<{ contentType: string; id: string; label?: string }>;
165
173
  /** Stock photos a build placed that are still on the page. */
166
174
  stock?: ReadonlyArray<{ nodeId: string; pageId?: string; owner?: string }>;
175
+ /** M7: pictures made with AI that are still on the page. */
176
+ made?: ReadonlyArray<{ nodeId: string; pageId?: string; owner?: string }>;
177
+ /** M7: picture slots the build could not make a picture for, still empty. */
178
+ missing?: ReadonlyArray<{ nodeId: string; pageId?: string; owner?: string }>;
167
179
  }
168
180
 
169
181
  export interface SiteAuditOptions {
@@ -280,6 +292,8 @@ function checkFacts(ctx: Ctx, snapshot: SiteAuditSnapshot, pages: ReadonlyArray<
280
292
  }
281
293
  for (const s of snapshot.samples ?? []) add(ctx, SiteFindingCode.SampleItems, `"${s.label ?? s.id}" is a sample — replace or remove it.`, { nodeId: s.id });
282
294
  for (const s of (snapshot.stock ?? []).filter(inScope)) add(ctx, SiteFindingCode.StockPhoto, 'A stock photo stands in for one of yours.', { pageId: s.pageId, nodeId: s.nodeId, owner: s.owner });
295
+ for (const s of (snapshot.made ?? []).filter(inScope)) add(ctx, SiteFindingCode.MadePicture, 'A picture made with AI stands in for one of yours.', { pageId: s.pageId, nodeId: s.nodeId, owner: s.owner });
296
+ for (const s of (snapshot.missing ?? []).filter(inScope)) add(ctx, SiteFindingCode.PictureMissing, 'A picture could not be made for this spot — add one of yours.', { pageId: s.pageId, nodeId: s.nodeId, owner: s.owner });
283
297
  if (!personal) return;
284
298
  const privacy = snapshot.pages.find(p => PRIVACY_PAGE.test(p.route) || PRIVACY_PAGE.test(p.name));
285
299
  if (!privacy) return add(ctx, SiteFindingCode.NoPrivacyPage, 'A form asks for personal details, and the site has no privacy page saying what you do with them.');