@vectoriox/iox-ui 5.83.1 → 5.85.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.
Files changed (53) hide show
  1. package/actions-core/index.d.ts +18 -2
  2. package/composite-core/index.d.ts +93 -15
  3. package/design-core/index.d.ts +566 -4
  4. package/fesm2022/vectoriox-iox-ui-actions-core.mjs +18 -1
  5. package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
  6. package/fesm2022/vectoriox-iox-ui-composite-core.mjs +307 -39
  7. package/fesm2022/vectoriox-iox-ui-composite-core.mjs.map +1 -1
  8. package/fesm2022/vectoriox-iox-ui-design-core.mjs +680 -15
  9. package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
  10. package/fesm2022/vectoriox-iox-ui-generation-core.mjs +665 -24
  11. package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
  12. package/fesm2022/vectoriox-iox-ui-harness-core.mjs +39 -1
  13. package/fesm2022/vectoriox-iox-ui-harness-core.mjs.map +1 -1
  14. package/fesm2022/vectoriox-iox-ui-scene-core.mjs +101 -5
  15. package/fesm2022/vectoriox-iox-ui-scene-core.mjs.map +1 -1
  16. package/fesm2022/vectoriox-iox-ui.mjs +285 -12
  17. package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
  18. package/generation-core/index.d.ts +414 -8
  19. package/harness-core/index.d.ts +81 -3
  20. package/index.d.ts +143 -42
  21. package/package.json +1 -1
  22. package/scene-core/index.d.ts +32 -4
  23. package/src/actions-core/dialog-motion.ts +25 -0
  24. package/src/composite-core/header.ts +25 -0
  25. package/src/composite-core/public-api.ts +1 -0
  26. package/src/composite-core/slider-runtime.ts +82 -0
  27. package/src/composite-core/slider.ts +194 -27
  28. package/src/composite-core/structure.ts +14 -9
  29. package/src/design-core/flavor-spec.generated.ts +596 -9
  30. package/src/design-core/header-behaviour.ts +76 -0
  31. package/src/design-core/public-api.ts +1 -0
  32. package/src/design-core/theme-color.ts +21 -5
  33. package/src/generation-core/ai-run.ts +10 -2
  34. package/src/generation-core/art-direction.ts +122 -0
  35. package/src/generation-core/audit-site.ts +14 -0
  36. package/src/generation-core/compose-site.ts +8 -0
  37. package/src/generation-core/node-baselines.generated.ts +17 -3
  38. package/src/generation-core/patterns.ts +2 -0
  39. package/src/generation-core/pictures.ts +464 -0
  40. package/src/generation-core/plan-wire.ts +2 -0
  41. package/src/generation-core/public-api.ts +2 -0
  42. package/src/generation-core/site-build-run.ts +3 -0
  43. package/src/generation-core/site-plan.model.ts +4 -0
  44. package/src/generation-core/site-spec.generated.ts +126 -1
  45. package/src/generation-core/stock-photos.ts +40 -10
  46. package/src/generation-core/validate-site-plan.ts +14 -0
  47. package/src/harness-core/business-types.ts +3 -1
  48. package/src/harness-core/generation-view.ts +50 -1
  49. package/src/harness-core/harness-spec.generated.ts +30 -0
  50. package/src/scene-core/scene.ts +18 -0
  51. package/src/scene-core/scroll-driver.ts +83 -6
  52. package/styles/iox-design.css +232 -1
  53. package/styles/iox-forms.css +5 -2
@@ -1,8 +1,8 @@
1
- import { NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, elementStyleById, ownedTraitsOf, applyElementStyle, flavorPackById, parseHexColor, SPEC_AXES, EntranceParam, staggerDelay, LinkType, COLOR_SCHEMES, setColorScheme, resolveDesignChoices, ANIMATE_IDS, auditDesignContrast } from '@vectoriox/iox-ui/design-core';
1
+ import { NEW_NODE_STYLE_DEFAULTS, defaultElementStyleFor, elementStyleById, ownedTraitsOf, applyElementStyle, parseHexColor, rgbToOklab, flavorPackById, SPEC_AXES, EntranceParam, staggerDelay, LinkType, COLOR_SCHEMES, setColorScheme, resolveDesignChoices, ANIMATE_IDS, auditDesignContrast, parseCssColour, compositeOver, wcagContrastRatio, relativeLuminance } from '@vectoriox/iox-ui/design-core';
2
2
  import { formPresetById, FORM_ACTIONS_ROLE, channelOpensNewTab, FORM_CHANNEL_ROLE, planFormPresetIds, ControlType, FORM_NODE_TYPE, collectFormSpec, FIELD_NODE_TYPE, isChannelFilled } from '@vectoriox/iox-ui/forms-core';
3
3
  import { normalizeFrameSequence, filmScreensFor, FILM_PREPARED_PICTURES, FrameExtension } from '@vectoriox/iox-ui/media-core';
4
4
  import { SEO_DESCRIPTION_MAX, SEO_TITLE_MAX } from '@vectoriox/iox-ui/seo-core';
5
- import { pickTabsAutoBinds, pickAccordionAutoBinds, segmentedOptions, ITEM_OPEN_STATE, CardPart, CardRole, CardLayout, CARD_LAYOUTS, CARD_PARTS, BADGE_TONES, buildCardRecipe, findByRole, buildSegmentedRecipe, AccordionRole, buildAccordionRecipe, ACCORDION_WORDS, accordionItems, accordionQuestionOf, accordionAnswerOf, ACCORDION_BIND_PROP, ACCORDION_CONTENT_LIMIT, buildTabsRecipe, TABS_WORDS, tabsTriggers, tabLabelNodeOf, TABS_LABEL_BIND_PROP, tabsPanels, tabPanelTextOf, TABS_TEXT_BIND_PROP, tabPanelImageOf, tabsListOf, tabsPanelsOf, TABS_CONTENT_LIMIT, TABS_IMAGE_BIND_PROP, buildCarouselRecipe, SLIDE_TYPE, buildLightboxRecipe, LIGHTBOX_LABEL, SLIDE_TO_ROW } from '@vectoriox/iox-ui/composite-core';
5
+ import { pickTabsAutoBinds, pickAccordionAutoBinds, segmentedOptions, ITEM_OPEN_STATE, CardPart, CardRole, CardLayout, CARD_LAYOUTS, CARD_PARTS, BADGE_TONES, buildCardRecipe, findByRole, buildSegmentedRecipe, AccordionRole, buildAccordionRecipe, ACCORDION_WORDS, accordionItems, accordionQuestionOf, accordionAnswerOf, ACCORDION_BIND_PROP, ACCORDION_CONTENT_LIMIT, buildTabsRecipe, TABS_WORDS, tabsTriggers, tabLabelNodeOf, TABS_LABEL_BIND_PROP, tabsPanels, tabPanelTextOf, TABS_TEXT_BIND_PROP, tabPanelImageOf, tabsListOf, tabsPanelsOf, TABS_CONTENT_LIMIT, TABS_IMAGE_BIND_PROP, buildCarouselRecipe, SLIDE_TYPE, buildLightboxRecipe, LIGHTBOX_LABEL, SLIDE_TO_ROW, setHeaderBehaviour } from '@vectoriox/iox-ui/composite-core';
6
6
  import { ELEMENT_ACTION_DESCRIPTORS, resolveActionAvailability } from '@vectoriox/iox-ui/actions-core';
7
7
 
8
8
  // GENERATED from iox-ai-guidance flavor-spec.v1.json — do not edit; re-run scripts/gen-flavor-spec.mjs
@@ -20,11 +20,20 @@ const SPEC_SITE_PLAN_SHAPE = {
20
20
  "dataSources": "(cmsDataSource | relativeDataSource)[] — merged by alias into the org library",
21
21
  "pages": "page[]",
22
22
  "globals": "globals",
23
- "film?": "film — the owner's film as pictures (SC6b), set by the build and placed by placeFilm; never a model's"
23
+ "film?": "film — the owner's film as pictures (SC6b), set by the build and placed by placeFilm; never a model's",
24
+ "artDirection?": "artDirection — what every picture made for the site shares (M7): set by the generation from the business type and the look (generation-core artDirectionFor); never a model's"
24
25
  },
25
26
  "film": {
26
27
  "sequence": "object — a media-core FrameSequence: where the numbered pictures live, how many, their size"
27
28
  },
29
+ "artDirection": {
30
+ "world": "string — a site.pictureWorlds id",
31
+ "medium": "string — the picture's medium and lens",
32
+ "light": "string — its light",
33
+ "grade": "string — its colour grade",
34
+ "texture": "string — its texture",
35
+ "negative": "string[] — what no picture of the site may show (site.pictureNever, then the world's own)"
36
+ },
28
37
  "sourceTemplate": {
29
38
  "kind": "'template'",
30
39
  "templateId": "string",
@@ -1137,6 +1146,10 @@ const SPEC_SITE_PATTERNS = {
1137
1146
  "label": "Navigation bar",
1138
1147
  "global": "before",
1139
1148
  "phoneMenu": "drawer",
1149
+ "header": {
1150
+ "behaviour": "hides",
1151
+ "edge": "fade"
1152
+ },
1140
1153
  "intent": "The brand on one side, the pages and one call to action on the other — the default header.",
1141
1154
  "node": {
1142
1155
  "t": "Container",
@@ -1206,6 +1219,10 @@ const SPEC_SITE_PATTERNS = {
1206
1219
  "label": "Navigation — the name centred",
1207
1220
  "global": "before",
1208
1221
  "phoneMenu": "drawer",
1222
+ "header": {
1223
+ "behaviour": "hides",
1224
+ "edge": "fade"
1225
+ },
1209
1226
  "intent": "The name centred above a centred row of pages — when the name is the brand.",
1210
1227
  "node": {
1211
1228
  "t": "Container",
@@ -5778,6 +5795,112 @@ const SPEC_SITE_LIBRARY = [
5778
5795
  "composed": true
5779
5796
  }
5780
5797
  ];
5798
+ const SPEC_SITE_PICTURE_WORLDS = {
5799
+ "documentary": {
5800
+ "label": "Documentary",
5801
+ "medium": "an unposed documentary photograph, 35mm lens, natural eye-level framing",
5802
+ "light": "available natural light, soft and directional",
5803
+ "grade": "true-to-life colour with gentle contrast",
5804
+ "texture": "fine natural grain and real, lived-in surfaces",
5805
+ "negative": [
5806
+ "staged poses",
5807
+ "a studio backdrop"
5808
+ ],
5809
+ "dark": "low-key"
5810
+ },
5811
+ "high-key": {
5812
+ "label": "High-key",
5813
+ "medium": "a bright editorial photograph, 50mm lens, a clean composition with generous air",
5814
+ "light": "soft, even daylight from a large window; few, pale shadows",
5815
+ "grade": "a light, airy palette with lifted whites and low contrast",
5816
+ "texture": "smooth surfaces and crisp, gentle detail",
5817
+ "negative": [
5818
+ "harsh shadows",
5819
+ "dark corners"
5820
+ ],
5821
+ "dark": "low-key"
5822
+ },
5823
+ "low-key": {
5824
+ "label": "Low-key",
5825
+ "medium": "a moody photograph, 50mm lens at a wide aperture, shallow depth of field",
5826
+ "light": "one warm, low light source; most of the frame falls into deep shadow",
5827
+ "grade": "rich blacks with warm, glowing highlights",
5828
+ "texture": "a soft haze in the air and a gentle film grain",
5829
+ "negative": [
5830
+ "flat lighting",
5831
+ "a bright background"
5832
+ ],
5833
+ "dark": "low-key"
5834
+ },
5835
+ "hard-light": {
5836
+ "label": "Hard light",
5837
+ "medium": "a graphic photograph, 35mm lens, bold and simple shapes",
5838
+ "light": "hard, direct sunlight with crisp, long cast shadows",
5839
+ "grade": "saturated colour and punchy contrast",
5840
+ "texture": "sharp edges and clean planes",
5841
+ "negative": [
5842
+ "haze",
5843
+ "muddy colour"
5844
+ ],
5845
+ "dark": "low-key"
5846
+ },
5847
+ "macro": {
5848
+ "label": "Macro",
5849
+ "medium": "a close-up macro photograph, 100mm macro lens, a very shallow plane of focus",
5850
+ "light": "soft side light raking across the surface",
5851
+ "grade": "natural colour with rich mid-tones",
5852
+ "texture": "tactile surface detail — grain, fibre, glaze, crumb",
5853
+ "negative": [
5854
+ "a wide view",
5855
+ "clutter in focus"
5856
+ ],
5857
+ "dark": "low-key"
5858
+ },
5859
+ "architectural": {
5860
+ "label": "Architectural",
5861
+ "medium": "an architectural photograph, 24mm tilt-shift lens, straight verticals and a calm composition",
5862
+ "light": "calm, diffuse daylight",
5863
+ "grade": "neutral, precise colour with balanced contrast",
5864
+ "texture": "clean lines and crisp materials — stone, wood, glass, plaster",
5865
+ "negative": [
5866
+ "converging verticals",
5867
+ "wide-angle distortion"
5868
+ ],
5869
+ "dark": "nocturne"
5870
+ },
5871
+ "nocturne": {
5872
+ "label": "Nocturne",
5873
+ "medium": "a night photograph, 35mm lens",
5874
+ "light": "practical lights only — lamps, candles, a warm window — against the dark",
5875
+ "grade": "deep blue-black shadows with warm pools of light",
5876
+ "texture": "soft bokeh and a light haze",
5877
+ "negative": [
5878
+ "daylight",
5879
+ "flash"
5880
+ ],
5881
+ "dark": "nocturne"
5882
+ },
5883
+ "drawing": {
5884
+ "label": "Drawing",
5885
+ "medium": "a hand-made illustration, ink and watercolour wash on paper",
5886
+ "light": "flat and even, never photographic",
5887
+ "grade": "a limited palette of two or three colours",
5888
+ "texture": "visible paper grain and brush strokes",
5889
+ "negative": [
5890
+ "photographic realism",
5891
+ "a 3D rendering"
5892
+ ],
5893
+ "dark": "drawing"
5894
+ }
5895
+ };
5896
+ const SPEC_SITE_PICTURE_NEVER = [
5897
+ "text, letters or numbers",
5898
+ "logos, brand names or signage",
5899
+ "a watermark",
5900
+ "a recognisable face",
5901
+ "borders or frames",
5902
+ "distorted hands or objects"
5903
+ ];
5781
5904
 
5782
5905
  const NODE_BASELINES = {
5783
5906
  "Button": {
@@ -6466,7 +6589,9 @@ const NODE_BASELINES = {
6466
6589
  "defaultOn",
6467
6590
  "activateOn",
6468
6591
  "orientation",
6469
- "ariaLabel"
6592
+ "ariaLabel",
6593
+ "headerBehaviour",
6594
+ "headerEdge"
6470
6595
  ],
6471
6596
  "styleTraits": [
6472
6597
  "position",
@@ -8889,6 +9014,10 @@ const NODE_BASELINES = {
8889
9014
  "slideToClickedSlide": false,
8890
9015
  "effect": "slide",
8891
9016
  "speed": 300,
9017
+ "scrollPlays": false,
9018
+ "controlsPlacement": "under",
9019
+ "enableCaption": false,
9020
+ "enableCount": false,
8892
9021
  "enableNavigation": false,
8893
9022
  "navigationColor": "#ffffff",
8894
9023
  "navigationSize": 44,
@@ -8996,7 +9125,11 @@ const NODE_BASELINES = {
8996
9125
  "__effectsLabel",
8997
9126
  "effect",
8998
9127
  "speed",
9128
+ "scrollPlays",
8999
9129
  "__navLabel",
9130
+ "controlsPlacement",
9131
+ "enableCaption",
9132
+ "enableCount",
9000
9133
  "enableNavigation",
9001
9134
  "navigationColor",
9002
9135
  "navigationSize",
@@ -9101,7 +9234,9 @@ const NODE_BASELINES = {
9101
9234
  "container": true
9102
9235
  },
9103
9236
  "SliderSlide": {
9104
- "props": {},
9237
+ "props": {
9238
+ "caption": ""
9239
+ },
9105
9240
  "styleProps": {
9106
9241
  "position": "static",
9107
9242
  "top": "auto",
@@ -9180,7 +9315,9 @@ const NODE_BASELINES = {
9180
9315
  "transitionDelay": "0ms",
9181
9316
  "transitionTimingFunction": "ease"
9182
9317
  },
9183
- "traits": [],
9318
+ "traits": [
9319
+ "caption"
9320
+ ],
9184
9321
  "styleTraits": [
9185
9322
  "position",
9186
9323
  "top",
@@ -11654,6 +11791,98 @@ function jsonSchemaObjects(node, out = []) {
11654
11791
  return out;
11655
11792
  }
11656
11793
 
11794
+ /**
11795
+ * A site's ART DIRECTION (premium-components M7, premium sites PS4): what every picture made for the site shares, so its
11796
+ * pictures read as one photographer's and never as a stock pool. A closed WORLD (the contract's `site.pictureWorlds`)
11797
+ * gives the five parts — medium, light, grade, texture and a negative list — and ONE preamble built from them is carried
11798
+ * VERBATIM at the head of every picture prompt of the site (`artDirectionPreamble`).
11799
+ *
11800
+ * Decided here, not by a model: the world is the business type's (harness-core `BusinessType.world`), a dark look moves it
11801
+ * to the world's `dark`, and the brand's hue joins the grade. Zero tokens, the same site always gets the same direction,
11802
+ * and the quality lever — each world's wording — is written once in the contract, as the proof sites' briefs were.
11803
+ */
11804
+ var PictureWorld;
11805
+ (function (PictureWorld) {
11806
+ PictureWorld["Documentary"] = "documentary";
11807
+ PictureWorld["HighKey"] = "high-key";
11808
+ PictureWorld["LowKey"] = "low-key";
11809
+ PictureWorld["HardLight"] = "hard-light";
11810
+ PictureWorld["Macro"] = "macro";
11811
+ PictureWorld["Architectural"] = "architectural";
11812
+ PictureWorld["Nocturne"] = "nocturne";
11813
+ PictureWorld["Drawing"] = "drawing";
11814
+ })(PictureWorld || (PictureWorld = {}));
11815
+ const PICTURE_WORLD_IDS = Object.values(PictureWorld);
11816
+ const PICTURE_WORLDS = SPEC_SITE_PICTURE_WORLDS;
11817
+ /** What no picture of any site shows — every world adds its own after these. */
11818
+ const PICTURE_NEVER = SPEC_SITE_PICTURE_NEVER;
11819
+ /** The five parts every art direction must carry, in the order the preamble says them. */
11820
+ const ART_DIRECTION_PARTS = ['medium', 'light', 'grade', 'texture', 'negative'];
11821
+ function isPictureWorld(value) {
11822
+ return typeof value === 'string' && PICTURE_WORLD_IDS.includes(value);
11823
+ }
11824
+ /** OKLCH hue bands (degrees, upper bound) → the word a grade says; below the chroma floor a brand is neutral. */
11825
+ const HUE_WORDS = [
11826
+ [12, 'rose'], [33, 'red'], [55, 'terracotta orange'], [80, 'warm amber'], [110, 'golden yellow'], [135, 'olive green'],
11827
+ [170, 'green'], [215, 'teal'], [255, 'blue'], [285, 'deep blue'], [315, 'violet'], [345, 'plum'], [360, 'rose'],
11828
+ ];
11829
+ const NEUTRAL_CHROMA = 0.04;
11830
+ const RADIANS_TO_DEGREES = 180 / Math.PI;
11831
+ const FULL_TURN = 360;
11832
+ /** The word for a brand colour's hue (`#9A4F2B` → "terracotta orange"), or null for a grey, a near-black or a bad value. */
11833
+ function brandHueWord(seed) {
11834
+ const rgb = seed ? parseHexColor(seed) : null;
11835
+ if (!rgb)
11836
+ return null;
11837
+ const { a, b } = rgbToOklab(rgb);
11838
+ if (Math.hypot(a, b) < NEUTRAL_CHROMA)
11839
+ return null;
11840
+ const hue = (Math.atan2(b, a) * RADIANS_TO_DEGREES + FULL_TURN) % FULL_TURN;
11841
+ return HUE_WORDS.find(([upTo]) => hue < upTo)?.[1] ?? null;
11842
+ }
11843
+ /**
11844
+ * The site's art direction: the business type's world (moved to its `dark` world when the look is dark), its five parts,
11845
+ * the brand's hue in the grade, PICTURE_NEVER before the world's own negatives. An unknown world is documentary.
11846
+ */
11847
+ function artDirectionFor(world, design) {
11848
+ const start = isPictureWorld(world) ? world : PictureWorld.Documentary;
11849
+ const moved = design.mode === 'dark' ? PICTURE_WORLDS[start].dark : start;
11850
+ const id = isPictureWorld(moved) ? moved : start;
11851
+ const spec = PICTURE_WORLDS[id];
11852
+ const hue = brandHueWord(design.seed);
11853
+ return {
11854
+ world: id,
11855
+ medium: spec.medium,
11856
+ light: spec.light,
11857
+ grade: hue ? `${spec.grade}, leaning toward ${hue} so the pictures sit with the brand` : spec.grade,
11858
+ texture: spec.texture,
11859
+ negative: [...PICTURE_NEVER, ...spec.negative],
11860
+ };
11861
+ }
11862
+ /** The ONE preamble a site's every picture prompt opens with — the same words, in the same order, every time. */
11863
+ function artDirectionPreamble(direction) {
11864
+ const medium = direction.medium.charAt(0).toUpperCase() + direction.medium.slice(1);
11865
+ return `${medium}. Light: ${direction.light}. Colour: ${direction.grade}. Texture: ${direction.texture}. Never: ${direction.negative.join('; ')}.`;
11866
+ }
11867
+ /** What is wrong with a value as an art direction — a path and a message each (`validateSitePlan` reports them). */
11868
+ function artDirectionIssues(value) {
11869
+ if (!value || typeof value !== 'object' || Array.isArray(value))
11870
+ return [{ path: '', message: 'must be an object' }];
11871
+ const v = value;
11872
+ const issues = [];
11873
+ if (!isPictureWorld(v['world']))
11874
+ issues.push({ path: 'world', message: `must be one of ${PICTURE_WORLD_IDS.join(', ')}` });
11875
+ for (const part of ART_DIRECTION_PARTS.filter(p => p !== 'negative')) {
11876
+ if (typeof v[part] !== 'string' || !v[part].trim())
11877
+ issues.push({ path: part, message: 'must be words' });
11878
+ }
11879
+ const negative = v['negative'];
11880
+ if (!Array.isArray(negative) || !negative.length || negative.some(n => typeof n !== 'string' || !n.trim())) {
11881
+ issues.push({ path: 'negative', message: 'must list what no picture may show' });
11882
+ }
11883
+ return issues;
11884
+ }
11885
+
11657
11886
  const LIST_RECIPES = {
11658
11887
  [PatternRecipe.Accordion]: { picks: f => ({ ...pickAccordionAutoBinds(f) }), required: ['question', 'answer'], needs: 'short-text field for the question and long-text field for the answer' },
11659
11888
  [PatternRecipe.Tabs]: { picks: f => ({ ...pickTabsAutoBinds(f) }), required: ['label', 'text'], needs: 'short-text field for the tab and long-text field for its panel' },
@@ -11739,6 +11968,8 @@ var SitePlanIssueCode;
11739
11968
  /** SC6b: the owner's film — pictures that cannot play, too few to play smoothly, or a film's pattern on a plan with
11740
11969
  * no film (or twice). Set by the build, never by a model. */
11741
11970
  SitePlanIssueCode["Film"] = "film";
11971
+ /** M7: the art direction is not a picture world with its five parts. Set by the generation, never by a model. */
11972
+ SitePlanIssueCode["ArtDirection"] = "art-direction";
11742
11973
  })(SitePlanIssueCode || (SitePlanIssueCode = {}));
11743
11974
  class SitePlanError extends Error {
11744
11975
  constructor(issues) {
@@ -11769,6 +12000,7 @@ const CMS_SOURCE_KEYS = shapeKeys(SHAPE['cmsDataSource']);
11769
12000
  const RELATIVE_SOURCE_KEYS = shapeKeys(SHAPE['relativeDataSource']);
11770
12001
  const PAGE_KEYS = shapeKeys(SHAPE['page']);
11771
12002
  const FILM_KEYS = shapeKeys(SHAPE['film']);
12003
+ const ART_DIRECTION_KEYS = shapeKeys(SHAPE['artDirection']);
11772
12004
  const SECTION_KEYS = shapeKeys(SHAPE['section']);
11773
12005
  const GLOBAL_POSITION = {
11774
12006
  [GlobalSlot.Header]: PatternGlobalPosition.Before,
@@ -12253,6 +12485,16 @@ function validateFilm(c, film, pages) {
12253
12485
  });
12254
12486
  });
12255
12487
  }
12488
+ /** M7: what every picture made for the site shares — a known world and its five parts (`artDirectionIssues`). */
12489
+ function validateArtDirection(c, direction) {
12490
+ if (direction === undefined)
12491
+ return;
12492
+ if (isPlainObject(direction))
12493
+ c.keys(direction, ART_DIRECTION_KEYS, 'artDirection');
12494
+ for (const issue of artDirectionIssues(direction)) {
12495
+ c.add(issue.path ? SitePlanIssueCode.ArtDirection : SitePlanIssueCode.Shape, issue.path ? `artDirection.${issue.path}` : 'artDirection', issue.message);
12496
+ }
12497
+ }
12256
12498
  function validatePageSeo(c, seo, path) {
12257
12499
  if (seo === undefined)
12258
12500
  return;
@@ -12307,6 +12549,7 @@ function validateSitePlan(plan) {
12307
12549
  validateGlobals(c, plan['globals']);
12308
12550
  validateScenes(c, c.array(plan, 'pages', ''), typeof design['animate'] === 'string' ? design['animate'] : null);
12309
12551
  validateFilm(c, plan['film'], c.array(plan, 'pages', ''));
12552
+ validateArtDirection(c, plan['artDirection']);
12310
12553
  return { valid: c.issues.length === 0, issues: c.issues };
12311
12554
  }
12312
12555
  /** Throw a `SitePlanError` carrying every issue unless the plan is valid. */
@@ -14190,6 +14433,13 @@ function composeSite(plan, context) {
14190
14433
  if (shape)
14191
14434
  globals = planNavShape(globals, shape, {}, slot).globals;
14192
14435
  }
14436
+ // Premium M4 (T2): a header pattern's behaviour as the page scrolls — the same edit the Header section makes.
14437
+ for (const slot of Object.values(GlobalSlot)) {
14438
+ const header = patternById(plan.globals[slot])?.header;
14439
+ const root = header ? globals.find(g => g.id === slot) : null;
14440
+ if (header && root)
14441
+ setHeaderBehaviour(root, header.behaviour, header.edge ?? null);
14442
+ }
14193
14443
  return {
14194
14444
  source: clone$1(plan.source),
14195
14445
  business: clone$1(plan.business),
@@ -14250,6 +14500,8 @@ function composeNavHeader(input, options) {
14250
14500
  root.globalPosition = pattern.global;
14251
14501
  const shape = pattern.phoneMenu;
14252
14502
  const header = shape ? planNavShape([root], shape, { newId: options.newId }, root.id).globals[0] : root;
14503
+ if (pattern.header)
14504
+ setHeaderBehaviour(header, pattern.header.behaviour, pattern.header.edge ?? null);
14253
14505
  return { header, issues };
14254
14506
  }
14255
14507
  // ── One pattern on its own (CC4b) ───────────────────────────────────────────────────────────────
@@ -14499,6 +14751,9 @@ var BuildNoticeCode;
14499
14751
  /** The stock library was read but holds no photo for this kind of business (never built, or reviewed down to
14500
14752
  * none): the picture slots keep the empty box, and the run says so (I2.1 — never silently). */
14501
14753
  BuildNoticeCode["StockMissing"] = "stock-missing";
14754
+ /** M7: a picture made for the site was refused by its review twice (or its second try did not fit the cost shown):
14755
+ * stock or the empty box stands in, and the run says so. */
14756
+ BuildNoticeCode["PictureFallback"] = "picture-fallback";
14502
14757
  })(BuildNoticeCode || (BuildNoticeCode = {}));
14503
14758
  var PreflightRefusalCode;
14504
14759
  (function (PreflightRefusalCode) {
@@ -14827,6 +15082,8 @@ const PLAN_WIRE_DERIVED = {
14827
15082
  design: "the owner's pick (the context)",
14828
15083
  'plan.film': "the owner's film, set by the build (SC6b)",
14829
15084
  film: "the owner's film, set by the build (SC6b)",
15085
+ 'plan.artDirection': 'the generation, from the business type and the look (M7 — artDirectionFor)',
15086
+ artDirection: 'the generation, from the business type and the look (M7 — artDirectionFor)',
14830
15087
  };
14831
15088
  const wireText = (spec) => {
14832
15089
  const [entry, planKey] = spec.refs[0].split('.');
@@ -15170,6 +15427,8 @@ var AiStep;
15170
15427
  AiStep["PlanSite"] = "plan";
15171
15428
  AiStep["ChangePlan"] = "planEdit";
15172
15429
  AiStep["MakePictures"] = "image";
15430
+ /** M7: a small vision model looks at each made picture against its slot (one structured answer). */
15431
+ AiStep["CheckPictures"] = "imageReview";
15173
15432
  AiStep["NameStructures"] = "scanNaming";
15174
15433
  })(AiStep || (AiStep = {}));
15175
15434
  const AI_STEPS = Object.values(AiStep);
@@ -15179,6 +15438,7 @@ const AI_STEP_LABELS = {
15179
15438
  [AiStep.PlanSite]: 'Plan the site',
15180
15439
  [AiStep.ChangePlan]: 'Change the plan',
15181
15440
  [AiStep.MakePictures]: 'Make pictures',
15441
+ [AiStep.CheckPictures]: 'Check pictures',
15182
15442
  [AiStep.NameStructures]: 'Name repeated structures',
15183
15443
  };
15184
15444
  /** What a model must be able to do for a step to run on it. */
@@ -15193,6 +15453,7 @@ const AI_STEP_NEEDS = {
15193
15453
  [AiStep.PlanSite]: [AiCapability.StructuredOutput],
15194
15454
  [AiStep.ChangePlan]: [AiCapability.StructuredOutput],
15195
15455
  [AiStep.MakePictures]: [AiCapability.ImageOutput],
15456
+ [AiStep.CheckPictures]: [AiCapability.StructuredOutput, AiCapability.ImageInput],
15196
15457
  [AiStep.NameStructures]: [AiCapability.StructuredOutput],
15197
15458
  };
15198
15459
  /**
@@ -15227,8 +15488,12 @@ const AI_STEP_TYPICAL_TOKENS = {
15227
15488
  [AiStep.ReadMessage]: { input: 8_800, output: 1_500, cacheWrite: 0, cacheRead: 0 },
15228
15489
  [AiStep.PlanSite]: partsTotal(AiStep.PlanSite),
15229
15490
  [AiStep.ChangePlan]: partsTotal(AiStep.ChangePlan),
15230
- // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4
15231
- [AiStep.MakePictures]: { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
15491
+ // a 1 K picture is 1,120 image tokens (Google's figure for Nano Banana 2) — I-D4; the prompt (the art direction's
15492
+ // preamble, the slot's subject, its frame and empty area) about 400 more in (M7)
15493
+ [AiStep.MakePictures]: { input: 400, output: 0, cacheWrite: 0, cacheRead: 0, imageOutput: 1_120 },
15494
+ // M7: one picture downscaled to 512 px (≈ 350 image tokens for Claude, ≈ 260 for Gemini) + the slot's question,
15495
+ // and a short structured answer
15496
+ [AiStep.CheckPictures]: { input: 1_200, output: 150, cacheWrite: 0, cacheRead: 0 },
15232
15497
  [AiStep.NameStructures]: { input: 80_000, output: 10_000, cacheWrite: 0, cacheRead: 0 },
15233
15498
  };
15234
15499
  /** 1 credit = 1 US cent of what the AI actually cost (decision D4). */
@@ -16177,6 +16442,10 @@ var SiteFindingCode;
16177
16442
  SiteFindingCode["CopyPlaceholder"] = "copy-placeholder";
16178
16443
  SiteFindingCode["SampleItems"] = "sample-items";
16179
16444
  SiteFindingCode["StockPhoto"] = "stock-photo";
16445
+ /** M7: a picture made with AI for the site still stands where one of the studio's own could. */
16446
+ SiteFindingCode["MadePicture"] = "made-picture";
16447
+ /** M7: a picture the build could not make (its review said no twice) and nothing stood in for: an empty box. */
16448
+ SiteFindingCode["PictureMissing"] = "picture-missing";
16180
16449
  SiteFindingCode["NoPrivacyPage"] = "no-privacy-page";
16181
16450
  SiteFindingCode["PrivacyPagePlaceholder"] = "privacy-page-placeholder";
16182
16451
  SiteFindingCode["LowContrast"] = "low-contrast";
@@ -16201,6 +16470,8 @@ const FINDING_SEVERITY = {
16201
16470
  [SiteFindingCode.CopyPlaceholder]: CheckSeverity.Ask,
16202
16471
  [SiteFindingCode.SampleItems]: CheckSeverity.Ask,
16203
16472
  [SiteFindingCode.StockPhoto]: CheckSeverity.Ask,
16473
+ [SiteFindingCode.MadePicture]: CheckSeverity.Ask,
16474
+ [SiteFindingCode.PictureMissing]: CheckSeverity.Warn,
16204
16475
  [SiteFindingCode.NoPrivacyPage]: CheckSeverity.Ask,
16205
16476
  [SiteFindingCode.PrivacyPagePlaceholder]: CheckSeverity.Block,
16206
16477
  [SiteFindingCode.LowContrast]: CheckSeverity.Warn,
@@ -16224,6 +16495,8 @@ const CHECK_OF = {
16224
16495
  [SiteFindingCode.CopyPlaceholder]: SiteCheckId.Facts,
16225
16496
  [SiteFindingCode.SampleItems]: SiteCheckId.Facts,
16226
16497
  [SiteFindingCode.StockPhoto]: SiteCheckId.Facts,
16498
+ [SiteFindingCode.MadePicture]: SiteCheckId.Facts,
16499
+ [SiteFindingCode.PictureMissing]: SiteCheckId.Facts,
16227
16500
  [SiteFindingCode.NoPrivacyPage]: SiteCheckId.Facts,
16228
16501
  [SiteFindingCode.PrivacyPagePlaceholder]: SiteCheckId.Facts,
16229
16502
  [SiteFindingCode.LowContrast]: SiteCheckId.Accessibility,
@@ -16346,6 +16619,10 @@ function checkFacts(ctx, snapshot, pages, personal) {
16346
16619
  add(ctx, SiteFindingCode.SampleItems, `"${s.label ?? s.id}" is a sample — replace or remove it.`, { nodeId: s.id });
16347
16620
  for (const s of (snapshot.stock ?? []).filter(inScope))
16348
16621
  add(ctx, SiteFindingCode.StockPhoto, 'A stock photo stands in for one of yours.', { pageId: s.pageId, nodeId: s.nodeId, owner: s.owner });
16622
+ for (const s of (snapshot.made ?? []).filter(inScope))
16623
+ add(ctx, SiteFindingCode.MadePicture, 'A picture made with AI stands in for one of yours.', { pageId: s.pageId, nodeId: s.nodeId, owner: s.owner });
16624
+ for (const s of (snapshot.missing ?? []).filter(inScope))
16625
+ 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 });
16349
16626
  if (!personal)
16350
16627
  return;
16351
16628
  const privacy = snapshot.pages.find(p => PRIVACY_PAGE.test(p.route) || PRIVACY_PAGE.test(p.name));
@@ -16844,21 +17121,47 @@ function stockItemSlots(plan, composed) {
16844
17121
  }
16845
17122
  return out;
16846
17123
  }
16847
- const intentRank = (intent) => STOCK_INTENT_ORDER.indexOf(intent);
17124
+ /** How far below the best score a photo may be and still be the seed's choice (one word of the plan's, about). */
17125
+ const STOCK_SEED_MARGIN = 2;
17126
+ /** A stable 32-bit hash (FNV-1a) — the seed's order of the library, the same in every runtime. */
17127
+ function seededRank(seed, key) {
17128
+ let hash = 0x811c9dc5;
17129
+ for (const ch of `${seed}:${key}`) {
17130
+ hash ^= ch.codePointAt(0);
17131
+ hash = Math.imul(hash, 0x01000193) >>> 0;
17132
+ }
17133
+ return hash;
17134
+ }
17135
+ /** The best of `pool` for `slot`: the highest score (ties: the library's order), or — with a seed — the seed's pick among
17136
+ * those within `STOCK_SEED_MARGIN` of it. */
17137
+ function bestFor(pool, slot, imagery, seed) {
17138
+ const scored = pool.map(photo => ({ photo, score: scoreStockPhoto(photo, slot, imagery) }));
17139
+ const top = scored.reduce((m, s) => Math.max(m, s.score), -Infinity);
17140
+ if (!seed)
17141
+ return scored.find(s => s.score === top)?.photo;
17142
+ const near = scored.filter(s => s.score >= top - STOCK_SEED_MARGIN);
17143
+ return near.reduce((best, s) => {
17144
+ const rank = seededRank(seed, s.photo.key);
17145
+ return !best || rank < best.rank ? { photo: s.photo, rank } : best;
17146
+ }, undefined)?.photo;
17147
+ }
17148
+ const intentRank$1 = (intent) => STOCK_INTENT_ORDER.indexOf(intent);
16848
17149
  /**
16849
17150
  * Which photo goes where (I2.1). Slots choose in intent order (the hero first, list items last; page order within),
16850
17151
  * each taking the best-scoring photo that FITS its shape (`stockFits`) and is not already on its page (or among its
16851
17152
  * type's samples) — a photo no slot has taken yet always before one another page shows. Ties go to the library's
16852
- * order. A slot nothing fits keeps its empty box (a sample its abstract picture). None at all when the type says no
16853
- * stock, and never a photo the review rejected or flagged. Returned in the slots' order.
17153
+ * order — or, with a `seed` (M7), the site's own pick among those nearly as good, and another site's photos (`avoid`) only
17154
+ * when nothing fresh fits. A slot nothing fits keeps its empty box (a sample its abstract picture). None at all when the
17155
+ * type says no stock, and never a photo the review rejected or flagged. Returned in the slots' order.
16854
17156
  */
16855
17157
  function planStockPhotos(slots, photos, stockAllowed, opts = {}) {
16856
17158
  if (!stockAllowed)
16857
17159
  return [];
16858
17160
  const usable = photos.filter(stockPhotoUsable);
16859
17161
  const imagery = stockWords(opts.imagery);
16860
- const order = slots.map((slot, i) => ({ slot, i })).sort((a, b) => intentRank(a.slot.intent) - intentRank(b.slot.intent) || a.i - b.i);
16861
- const used = new Set();
17162
+ const order = slots.map((slot, i) => ({ slot, i })).sort((a, b) => intentRank$1(a.slot.intent) - intentRank$1(b.slot.intent) || a.i - b.i);
17163
+ // Another site's photos count as already shown: a fresh one that fits always comes first.
17164
+ const used = new Set(opts.avoid ?? []);
16862
17165
  const shownIn = new Map();
16863
17166
  const placed = new Map();
16864
17167
  for (const { slot, i } of order) {
@@ -16866,16 +17169,7 @@ function planStockPhotos(slots, photos, stockAllowed, opts = {}) {
16866
17169
  const here = shownIn.get(group) ?? new Set();
16867
17170
  const candidates = usable.filter(p => !here.has(p.key) && stockFits(p, slot));
16868
17171
  const fresh = candidates.filter(p => !used.has(p.key));
16869
- const pool = fresh.length ? fresh : candidates;
16870
- let best;
16871
- let bestScore = -Infinity;
16872
- for (const photo of pool) {
16873
- const score = scoreStockPhoto(photo, slot, imagery);
16874
- if (score > bestScore) {
16875
- best = photo;
16876
- bestScore = score;
16877
- }
16878
- }
17172
+ const best = bestFor(fresh.length ? fresh : candidates, slot, imagery, opts.seed);
16879
17173
  if (!best)
16880
17174
  continue;
16881
17175
  used.add(best.key);
@@ -16952,6 +17246,353 @@ function addStockCredits(footer, credits) {
16952
17246
  return added;
16953
17247
  }
16954
17248
 
17249
+ /**
17250
+ * Pictures MADE for a site (premium-components M7, AI plan I3, premium sites PS4) — pure and deterministic: which picture
17251
+ * slots of a composed site get a made picture, the prompt each is asked with (the site's ONE art direction first,
17252
+ * verbatim, then what the slot is about, its frame and — where words sit over it — the part of the frame left calm for
17253
+ * them), what the whole set costs before anything is asked, how a vision model's look at each picture is judged, and how
17254
+ * dark the scrim under the words must be for the picture actually made. iox-cms makes, reviews and stores the pictures;
17255
+ * the wizard shows the count and the cost from the same functions.
17256
+ *
17257
+ * The order of who fills a slot never changes: the OWNER's photos first (`planOwnPhotos`), then — only when the owner
17258
+ * opted in — a made picture where one may stand, then stock where the business type allows it, else the empty box.
17259
+ */
17260
+ /** Why a slot never gets a made picture — said to the owner, never silently skipped. */
17261
+ var PictureRefusal;
17262
+ (function (PictureRefusal) {
17263
+ /** The business sells its own work (a photographer, a maker): a made picture would pass as it. */
17264
+ PictureRefusal["OwnWork"] = "own-work";
17265
+ /** A product, a dish, a piece the business offers: a made one would show a thing it does not sell. */
17266
+ PictureRefusal["Product"] = "product";
17267
+ /** Where to find the business: a made picture would fake a real place. */
17268
+ PictureRefusal["RealPlace"] = "real-place";
17269
+ /** More slots than one site makes pictures for (`MAX_SITE_PICTURES`): the rest keep stock or the empty box. */
17270
+ PictureRefusal["Limit"] = "limit";
17271
+ })(PictureRefusal || (PictureRefusal = {}));
17272
+ /** The section families whose picture is a real place (the contact / visit section). */
17273
+ const PICTURE_REAL_PLACE_FAMILIES = new Set(['contact']);
17274
+ /** The most pictures one site is made — a ceiling on what one build may spend on pictures. */
17275
+ const MAX_SITE_PICTURES = 12;
17276
+ /** Which edge of the frame the words sit at. */
17277
+ var WordsEdge;
17278
+ (function (WordsEdge) {
17279
+ WordsEdge["Top"] = "top";
17280
+ WordsEdge["Bottom"] = "bottom";
17281
+ })(WordsEdge || (WordsEdge = {}));
17282
+ /** A scrim with no height of its own (it wraps its words) covers about this much of the frame. */
17283
+ const WORDS_AREA_DEFAULT_SHARE = 0.45;
17284
+ const MEDIA_SCRIM_STYLE = 'media-scrim';
17285
+ const ABSOLUTE = 'absolute';
17286
+ const ZERO = /^0(px|%)?$/;
17287
+ const PERCENT = /^(\d+(?:\.\d+)?)%$/;
17288
+ /** The frames an image model is asked for (`image_config.aspect_ratio`), as width / height. */
17289
+ const PICTURE_ASPECTS = {
17290
+ '1:1': 1, '2:3': 2 / 3, '3:2': 3 / 2, '3:4': 3 / 4, '4:3': 4 / 3, '4:5': 4 / 5, '5:4': 5 / 4, '9:16': 9 / 16, '16:9': 16 / 9, '21:9': 21 / 9,
17291
+ };
17292
+ /** A slot that fills its box (a Scene's picture, a full-bleed band) is made wide: a phone crops its middle. */
17293
+ const FILL_ASPECT = '16:9';
17294
+ /** The ONE list of picture slots a build fills — its composed sections' empty pictures, then its sample items'. */
17295
+ function pictureTargets(plan, composed) {
17296
+ return [...stockSlots(plan, composed), ...stockItemSlots(plan, composed)];
17297
+ }
17298
+ /** A slot's stable key. */
17299
+ function pictureSlotKey(target) {
17300
+ return target.kind === StockSlotKind.Section ? `page:${target.owner}:${target.nodeId}` : `item:${target.contentType}:${target.index}:${target.field}`;
17301
+ }
17302
+ /** The nearest frame a model is asked for (by the ratio of ratios); a slot with no shape fills its box. */
17303
+ function pictureAspect(aspect) {
17304
+ if (!aspect || !Number.isFinite(aspect) || aspect <= 0)
17305
+ return FILL_ASPECT;
17306
+ let best = FILL_ASPECT;
17307
+ let distance = Infinity;
17308
+ for (const [ratio, value] of Object.entries(PICTURE_ASPECTS)) {
17309
+ const d = Math.abs(Math.log(value / aspect));
17310
+ if (d < distance) {
17311
+ best = ratio;
17312
+ distance = d;
17313
+ }
17314
+ }
17315
+ return best;
17316
+ }
17317
+ function indexNodes(composed) {
17318
+ const at = new Map();
17319
+ composed.pages.forEach((page, p) => page.layout.forEach((root, s) => {
17320
+ walkSerialized([root], (node, ancestors) => at.set(node.id, { node, parent: ancestors[ancestors.length - 1], section: s, page: p }));
17321
+ }));
17322
+ return at;
17323
+ }
17324
+ /**
17325
+ * The words over a picture: a picture laid ABSOLUTE in its box whose sibling wears `media-scrim` (a Scene's stage, a
17326
+ * full-bleed band) carries the words that box holds. The area is the scrim's: its height when it says one, else
17327
+ * `WORDS_AREA_DEFAULT_SHARE`, at the edge it is pinned to.
17328
+ */
17329
+ function wordsOverPicture(node, parent) {
17330
+ if (!parent || node.styleProps['position'] !== ABSOLUTE)
17331
+ return null;
17332
+ const scrim = parent.children?.find(c => c !== node && c.elementStyle === MEDIA_SCRIM_STYLE);
17333
+ if (!scrim)
17334
+ return null;
17335
+ const top = String(scrim.styleProps['top'] ?? '');
17336
+ const bottom = String(scrim.styleProps['bottom'] ?? '');
17337
+ const edge = ZERO.test(top) && !ZERO.test(bottom) ? WordsEdge.Top : WordsEdge.Bottom;
17338
+ const height = PERCENT.exec(String(scrim.styleProps['height'] ?? ''));
17339
+ const share = height ? Math.min(1, Number(height[1]) / 100) : WORDS_AREA_DEFAULT_SHARE;
17340
+ return { area: { edge, share }, scrimNodeId: scrim.id };
17341
+ }
17342
+ const SECTION_TEXT_MAX = 160;
17343
+ /** What a section says (its slots, in the plan's words), cut to a prompt's length. */
17344
+ function sectionText(plan, page, section) {
17345
+ const slots = plan.pages[page]?.sections[section]?.slots ?? {};
17346
+ const text = Object.values(slots).filter((v) => typeof v === 'string' && !!v.trim()).join(' — ').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
17347
+ return text.length > SECTION_TEXT_MAX ? `${text.slice(0, SECTION_TEXT_MAX - 1).trimEnd()}…` : text;
17348
+ }
17349
+ /** Why `target` never gets a made picture, or null. */
17350
+ function pictureRefusalOf(target, stockAllowed) {
17351
+ if (!stockAllowed)
17352
+ return PictureRefusal.OwnWork;
17353
+ if (target.kind === StockSlotKind.Item)
17354
+ return PictureRefusal.Product;
17355
+ const family = patternById(target.pattern)?.family ?? '';
17356
+ return PICTURE_REAL_PLACE_FAMILIES.has(family) ? PictureRefusal.RealPlace : null;
17357
+ }
17358
+ const intentRank = (intent) => STOCK_INTENT_ORDER.indexOf(intent);
17359
+ /**
17360
+ * Which slots get a made picture: the owner's photos take theirs first (the same `planOwnPhotos` the build places them
17361
+ * with), then each slot left — the hero first, page order within — gets one unless it is refused (the work, a product,
17362
+ * a real place) or the site already has `max`. Pure: the wizard's count, the generation's run and the build agree.
17363
+ */
17364
+ function planPictures(plan, composed, opts) {
17365
+ const { placements: own, rest } = planOwnPhotos(pictureTargets(plan, composed), opts.ownPhotos ?? []);
17366
+ const nodes = indexNodes(composed);
17367
+ const max = opts.max ?? MAX_SITE_PICTURES;
17368
+ const ordered = rest.map((t, i) => ({ t, i })).sort((a, b) => intentRank(a.t.intent) - intentRank(b.t.intent) || a.i - b.i).map(x => x.t);
17369
+ const make = [];
17370
+ const refused = [];
17371
+ for (const target of ordered) {
17372
+ const key = pictureSlotKey(target);
17373
+ const reason = pictureRefusalOf(target, opts.stockAllowed) ?? (make.length >= max ? PictureRefusal.Limit : null);
17374
+ if (reason) {
17375
+ refused.push({ key, reason });
17376
+ continue;
17377
+ }
17378
+ make.push(pictureSlotOf(plan, target, key, nodes));
17379
+ }
17380
+ return { own, make, refused };
17381
+ }
17382
+ function pictureSlotOf(plan, target, key, nodes) {
17383
+ const base = { key, target, intent: target.intent, ratio: pictureAspect(target.aspect) };
17384
+ if (target.kind !== StockSlotKind.Section)
17385
+ return { ...base, family: '', text: '' };
17386
+ const at = nodes.get(target.nodeId);
17387
+ const over = at ? wordsOverPicture(at.node, at.parent) : null;
17388
+ return {
17389
+ ...base,
17390
+ family: patternById(target.pattern)?.family ?? '',
17391
+ text: at ? sectionText(plan, at.page, at.section) : '',
17392
+ ...(over ? { wordsArea: over.area, scrimNodeId: over.scrimNodeId } : {}),
17393
+ };
17394
+ }
17395
+ // ── The prompt ──────────────────────────────────────────────────────────────────────────────────────────────────────
17396
+ /** What each kind of slot asks to see (`business` = the kind of business, "a café or bakery"). */
17397
+ const INTENT_SUBJECT = {
17398
+ [StockIntent.Atmosphere]: b => `A wide, atmospheric picture that sets the mood of ${b}: depth, a sense of the place, nothing staged.`,
17399
+ [StockIntent.Place]: b => `The kind of place ${b} is: the room or the setting at eye level, quiet and inviting.`,
17400
+ [StockIntent.Detail]: b => `A close detail of the work of ${b}: its materials, its tools, a moment of making.`,
17401
+ [StockIntent.Product]: b => `One thing ${b} offers, alone on a plain ground.`,
17402
+ };
17403
+ const EDGE_WORD = { [WordsEdge.Top]: 'upper', [WordsEdge.Bottom]: 'lower' };
17404
+ /** The empty part a slot's words sit over, said to the model: where, how much, and what it must look like. */
17405
+ function emptyAreaPhrase(area) {
17406
+ const share = Math.round(area.share * 100);
17407
+ return `Leave the ${EDGE_WORD[area.edge]} ${share}% of the frame calm and uncluttered — soft focus, even tone, nothing important there — because the site's words sit over it.`;
17408
+ }
17409
+ /** The frame, said plainly. */
17410
+ function frameWords(ratio) {
17411
+ const value = PICTURE_ASPECTS[ratio] ?? 1;
17412
+ const shape = value > 1.05 ? 'landscape' : value < 0.95 ? 'portrait' : 'square';
17413
+ return `Frame: ${shape}, ${ratio}.`;
17414
+ }
17415
+ /**
17416
+ * The prompt a picture is asked with: the site's preamble VERBATIM, what the slot is about (its intent, then the
17417
+ * section's own words), its frame, the empty part the words need — and nothing else. The same slot of the same plan
17418
+ * is always asked the same words.
17419
+ */
17420
+ function picturePrompt(slot, direction, context) {
17421
+ return [
17422
+ artDirectionPreamble(direction),
17423
+ INTENT_SUBJECT[slot.intent](context.business),
17424
+ ...(slot.text ? [`It goes beside these words of the site: “${slot.text}”.`] : []),
17425
+ frameWords(slot.ratio),
17426
+ ...(slot.wordsArea ? [emptyAreaPhrase(slot.wordsArea)] : []),
17427
+ ].join('\n');
17428
+ }
17429
+ // ── The cost, shown before ──────────────────────────────────────────────────────────────────────────────────────────
17430
+ /** One picture in this many may be made a second time (a review said no) — paid for in the estimate shown. */
17431
+ const PICTURE_REROLL_EVERY = 6;
17432
+ /** How many second tries a set of `count` pictures has, shown and paid for up front. */
17433
+ function pictureRerolls(count) {
17434
+ return count > 0 ? Math.ceil(count / PICTURE_REROLL_EVERY) : 0;
17435
+ }
17436
+ /**
17437
+ * The cost shown before the owner says yes (I-D4): each picture and its review at the vetted list's prices, plus one
17438
+ * second try (a picture and its review) for every `PICTURE_REROLL_EVERY` pictures. The run never spends past `total` —
17439
+ * a second try that would cross it is not made — so the shown cost is a ceiling met exactly when every try is used.
17440
+ */
17441
+ function estimatePictures(count, prices) {
17442
+ const picture = estimateStepCredits(AiStep.MakePictures, prices.image);
17443
+ const review = estimateStepCredits(AiStep.CheckPictures, prices.review);
17444
+ const rerolls = pictureRerolls(count);
17445
+ return { count, rerolls, picture, review, total: creditsOfUsd(((count + rerolls) * (picture + review)) / 100) };
17446
+ }
17447
+ // ── The review ──────────────────────────────────────────────────────────────────────────────────────────────────────
17448
+ /** The structured answer's name (`output.name`). */
17449
+ const PICTURE_REVIEW_FORMAT = 'picture_review';
17450
+ /** The review's shape, each key in the words the model reads (every key required: no optional parameter). */
17451
+ const PICTURE_REVIEW_SHAPE = {
17452
+ subject: 'boolean — it shows what the slot asked for, in the art direction asked',
17453
+ calmArea: 'boolean — the part of the frame named for words is calm enough to read words over (true when none was named)',
17454
+ clean: 'boolean — no text, letters, numbers, logo, watermark, signage or frame anywhere',
17455
+ anatomy: 'boolean — no distorted hands, faces, bodies or objects; nothing melted or duplicated',
17456
+ alt: 'string — what the picture shows, in at most 12 plain words, in the language asked (the alt text)',
17457
+ note: 'string — why, when any answer is false; else empty',
17458
+ };
17459
+ const BOOLEAN_KEYS = ['subject', 'calmArea', 'clean', 'anatomy'];
17460
+ const TEXT_KEYS = ['alt', 'note'];
17461
+ /** The review's strict format: 6 properties, all required, closed — well inside every structured-output limit. */
17462
+ function pictureReviewJsonSchema() {
17463
+ const properties = {};
17464
+ for (const key of BOOLEAN_KEYS)
17465
+ properties[key] = { ...jsonBoolean(), description: PICTURE_REVIEW_SHAPE[key] };
17466
+ for (const key of TEXT_KEYS)
17467
+ properties[key] = { ...jsonText(), description: PICTURE_REVIEW_SHAPE[key] };
17468
+ return jsonClosedObject(properties, [...BOOLEAN_KEYS, ...TEXT_KEYS], 'A look at one picture made for a website, against what its slot asked for.');
17469
+ }
17470
+ /** What is wrong with an answer as a review — the gate the call service holds it to. */
17471
+ function pictureReviewIssues(value) {
17472
+ if (!value || typeof value !== 'object' || Array.isArray(value))
17473
+ return [{ code: 'shape', path: '', message: 'must be an object' }];
17474
+ const v = value;
17475
+ const issues = [];
17476
+ for (const key of BOOLEAN_KEYS)
17477
+ if (typeof v[key] !== 'boolean')
17478
+ issues.push({ code: 'shape', path: key, message: 'must be true or false' });
17479
+ for (const key of TEXT_KEYS)
17480
+ if (typeof v[key] !== 'string')
17481
+ issues.push({ code: 'shape', path: key, message: 'must be text' });
17482
+ for (const key of Object.keys(v))
17483
+ if (!(key in PICTURE_REVIEW_SHAPE))
17484
+ issues.push({ code: 'shape', path: key, message: 'is not a key of the review' });
17485
+ return issues;
17486
+ }
17487
+ /** A picture is used when it shows the subject, is clean and whole — and, where words sit over it, leaves them room. */
17488
+ function pictureReviewPasses(review, slot) {
17489
+ return review.subject && review.clean && review.anatomy && (!slot.wordsArea || review.calmArea);
17490
+ }
17491
+ // ── Words over the picture made ─────────────────────────────────────────────────────────────────────────────────────
17492
+ /** How strong the scrim under a picture's words is, weakest first. `Soft` is the element style's own gradient. */
17493
+ var ScrimLevel;
17494
+ (function (ScrimLevel) {
17495
+ ScrimLevel["Soft"] = "soft";
17496
+ ScrimLevel["Firm"] = "firm";
17497
+ ScrimLevel["Full"] = "full";
17498
+ })(ScrimLevel || (ScrimLevel = {}));
17499
+ const SCRIM_LEVELS = [ScrimLevel.Soft, ScrimLevel.Firm, ScrimLevel.Full];
17500
+ /** What each level writes on the scrim node (`backgroundColor`, token references only) — `null`: the style's own. */
17501
+ const SCRIM_BACKGROUND = {
17502
+ [ScrimLevel.Soft]: null,
17503
+ [ScrimLevel.Firm]: 'linear-gradient(180deg, transparent 0%, var(--iox-media-scrim) 18%)',
17504
+ [ScrimLevel.Full]: 'var(--iox-media-scrim)',
17505
+ };
17506
+ /** The least share of the scrim's own strength under the first line of words, at each level (the gradient's ramp). */
17507
+ const SCRIM_COVER = {
17508
+ [ScrimLevel.Soft]: 0.5,
17509
+ [ScrimLevel.Firm]: 0.85,
17510
+ [ScrimLevel.Full]: 1,
17511
+ };
17512
+ /** WCAG AA for body text — the words over a picture include a lead and a button label, never only a headline. */
17513
+ const PICTURE_TEXT_CONTRAST = 4.5;
17514
+ /** The share of pixels left out at each end, so a single sparkle does not decide the scrim. */
17515
+ const AREA_PERCENTILE = 0.05;
17516
+ /** The area is read a little past its edge: a Scene scales its picture in from the middle. */
17517
+ const AREA_MARGIN = 0.1;
17518
+ /** A phone shows the middle of a wide picture: read that column too. */
17519
+ const PHONE_COLUMN = [1 / 3, 2 / 3];
17520
+ const srgbToLinear = (v) => (v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
17521
+ const linearToSrgb = (v) => (v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055);
17522
+ const BYTE = 255;
17523
+ const RGBA = 4;
17524
+ function windowLuminance(rgba, width, height, rows, columns) {
17525
+ const values = [];
17526
+ const [y0, y1] = rows.map(r => Math.round(r * height));
17527
+ const [x0, x1] = columns.map(c => Math.round(c * width));
17528
+ for (let y = Math.max(0, y0); y < Math.min(height, y1); y++) {
17529
+ for (let x = Math.max(0, x0); x < Math.min(width, x1); x++) {
17530
+ const i = (y * width + x) * RGBA;
17531
+ values.push(0.2126 * srgbToLinear(rgba[i] / BYTE) + 0.7152 * srgbToLinear(rgba[i + 1] / BYTE) + 0.0722 * srgbToLinear(rgba[i + 2] / BYTE));
17532
+ }
17533
+ }
17534
+ if (!values.length)
17535
+ return { low: 0, high: 0, mean: 0 };
17536
+ values.sort((a, b) => a - b);
17537
+ const at = (q) => values[Math.min(values.length - 1, Math.max(0, Math.round(q * (values.length - 1))))];
17538
+ return { low: at(AREA_PERCENTILE), high: at(1 - AREA_PERCENTILE), mean: values.reduce((a, b) => a + b, 0) / values.length };
17539
+ }
17540
+ /**
17541
+ * The light of the part of a picture its words sit over (RGBA bytes, row by row) — read across the whole width (a laptop)
17542
+ * and in the middle third (a phone's crop of a wide picture); the darker low and the lighter high of the two.
17543
+ */
17544
+ function areaLuminance(rgba, width, height, area) {
17545
+ const reach = Math.min(1, area.share + AREA_MARGIN);
17546
+ const rows = area.edge === WordsEdge.Top ? [0, reach] : [1 - reach, 1];
17547
+ const laptop = windowLuminance(rgba, width, height, rows, [0, 1]);
17548
+ const phone = windowLuminance(rgba, width, height, rows, PHONE_COLUMN);
17549
+ return { low: Math.min(laptop.low, phone.low), high: Math.max(laptop.high, phone.high), mean: laptop.mean };
17550
+ }
17551
+ /** A grey of this relative luminance. */
17552
+ function greyOf(luminance) {
17553
+ const v = linearToSrgb(Math.min(1, Math.max(0, luminance)));
17554
+ return { r: v, g: v, b: v };
17555
+ }
17556
+ /**
17557
+ * The weakest scrim under which the words reach AA over the picture made: the scrim's own colour (`--iox-media-scrim`,
17558
+ * as the look writes it) at its least strength under the words, over the area's darkest and lightest, against the ink
17559
+ * (`--iox-media-scrim-ink`). `Full` when nothing weaker does — the contract already holds a full scrim over white to AA.
17560
+ */
17561
+ function chooseScrim(area, colours, minContrast = PICTURE_TEXT_CONTRAST) {
17562
+ const scrim = parseCssColour(colours.scrim);
17563
+ const ink = parseCssColour(colours.ink);
17564
+ if (!scrim || !ink)
17565
+ return { level: ScrimLevel.Full, contrast: 0 };
17566
+ const worstAt = (level) => Math.min(...[area.low, area.high].map(y => {
17567
+ const under = compositeOver({ ...scrim, a: scrim.a * SCRIM_COVER[level] }, greyOf(y));
17568
+ return wcagContrastRatio(ink, under);
17569
+ }));
17570
+ for (const level of SCRIM_LEVELS) {
17571
+ const contrast = worstAt(level);
17572
+ if (contrast >= minContrast)
17573
+ return { level, contrast };
17574
+ }
17575
+ return { level: ScrimLevel.Full, contrast: worstAt(ScrimLevel.Full) };
17576
+ }
17577
+ /** Set each chosen scrim on its node (`Soft` leaves the element style's own). Returns the nodes changed. */
17578
+ function applyPictureScrims(trees, chosen) {
17579
+ const byId = new Map(chosen.map(c => [c.nodeId, SCRIM_BACKGROUND[c.level]]));
17580
+ let changed = 0;
17581
+ walkSerialized(trees, node => {
17582
+ const background = byId.get(node.id);
17583
+ if (!background)
17584
+ return;
17585
+ node.styleProps['backgroundColor'] = background;
17586
+ changed++;
17587
+ });
17588
+ return changed;
17589
+ }
17590
+ /** The relative luminance of a CSS colour (for a review sheet or a log), or null. */
17591
+ function luminanceOf(colour) {
17592
+ const c = parseCssColour(colour);
17593
+ return c ? relativeLuminance(c) : null;
17594
+ }
17595
+
16955
17596
  /**
16956
17597
  * @vectoriox/iox-ui/generation-core — the shared, FRAMEWORK-FREE site-plan core (design-system plan
16957
17598
  * §6b, W1): the Site Plan shape + its validator, the pattern library, the page types and the three structures (Studio ·
@@ -16968,5 +17609,5 @@ function addStockCredits(footer, credits) {
16968
17609
  * Generated bundle index. Do not edit.
16969
17610
  */
16970
17611
 
16971
- export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_PART_TOKENS, AI_STEP_TYPICAL_TOKENS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentFieldControl, ContentFieldType, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FIELD_TYPE_OF_CONTROL, FILM_PATTERN, FINAL_GENERATION_STAGES, FINDING_SEVERITY, FilmUnplaced, GRAMMAR_TOO_LARGE_MESSAGE, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PLAN_EDITS_ANSWER_SHAPES, PLAN_EDITS_PART_SCHEMA_NAMES, PLAN_EDIT_OP_PART, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_PARTS, PLAN_PART_KEYS, PLAN_WIRE_DERIVED, PLAN_WIRE_PART_SCHEMA_NAMES, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanPart, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SCENE_LEVEL, SCENE_OTHER_PAGES, SHOWCASE_FILM, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_FAMILY_INTENTS, STOCK_INTENT_ORDER, STOCK_INTENT_SUBJECTS, STOCK_ITEM_ASPECT, STOCK_MIN_KEPT, STOCK_PROVIDER_TERMS, STOCK_SUBJECT_WORDS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockIntent, StockOrientation, StockProviderId, StockReviewFlag, StockReviewVerdict, StockSlotKind, StockSubject, addBarItem, addStockCredits, aiStepParts, applyOwnPhotoGalleries, applyPlanEdits, applyStockItemPhotos, applyStockPhotos, archetypeIssues, aspectRatioOf, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, buildKeys, buildStepFacts, builtHomePageId, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, derivedDataSources, describePlanData, describeShapeKey, entranceAction, entranceInteraction, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, filmOfPlan, filmPlacement, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, orientationOfRatio, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternListGap, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, placeFilm, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planOwnPhotos, planPartOfPath, planPartToWire, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, recipeListBinds, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, scoreStockPhoto, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, showcaseScenes, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockFits, stockGroupOf, stockItemSlots, stockKept, stockPhotoRatio, stockPhotoSubject, stockPhotoUsable, stockPhotoWords, stockSlots, stockWord, stockWords, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, withDerivedSources, wordPattern, writtenTexts };
17612
+ export { ACTIVE_BUILD_STATUSES, AI_REFUSAL_NEXT, AI_REFUSAL_STATUS, AI_STEPS, AI_STEP_LABELS, AI_STEP_NEEDS, AI_STEP_PART_TOKENS, AI_STEP_TYPICAL_TOKENS, ART_DIRECTION_PARTS, AiCallStatus, AiCapability, AiFeature, AiGenerationStage, AiKeyMode, AiLimit, AiNextKind, AiProviderId, AiRefusalCode, AiStep, BAR_ITEMS_MAX, BAR_ITEMS_MIN, BASELINE_ROLES, BUILD_STEPS, BaselineRole, BindScope, BuildNoticeCode, BuildStep, CONTENT_ROLE_MEANINGS, CREDITS_PER_USD, CardBindTarget, CheckSeverity, ComposeError, ComposeIssueCode, ContentFieldControl, ContentFieldType, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FIELD_TYPE_OF_CONTROL, FILL_ASPECT, FILM_PATTERN, FINAL_GENERATION_STAGES, FINDING_SEVERITY, FilmUnplaced, GRAMMAR_TOO_LARGE_MESSAGE, GlobalSlot, HEBREW_PREFIXES, JSON_SCHEMA_BANNED_KEYWORDS, JsonSchemaShapeError, KeptReason, MAX_SITE_PICTURES, NAV_PART_LABELS, NAV_SHAPE_COPY, NEUTRAL_STYLE_PROPS, NODE_BASELINES, NO_TOKENS, NavPagesSource, NavRole, NavShape, NavShapeError, PAGE_ARCHETYPES, PICTURE_ASPECTS, PICTURE_NEVER, PICTURE_REAL_PLACE_FAMILIES, PICTURE_REROLL_EVERY, PICTURE_REVIEW_FORMAT, PICTURE_REVIEW_SHAPE, PICTURE_TEXT_CONTRAST, PICTURE_WORLDS, PICTURE_WORLD_IDS, PLAN_EDITS_ANSWER_SHAPES, PLAN_EDITS_PART_SCHEMA_NAMES, PLAN_EDIT_OP_PART, PLAN_EDIT_SHAPE, PLAN_EDIT_WIRE_KEYS, PLAN_PARTS, PLAN_PART_KEYS, PLAN_WIRE_DERIVED, PLAN_WIRE_PART_SCHEMA_NAMES, PLAN_WIRE_SHAPE, PRICING_PATTERN, PUBLISH_BLOCKING_PLACEHOLDERS, PageDataKind, PathIssueCollector, PatternDirective, PatternGlobalPosition, PatternOpens, PatternPlaceholder, PatternRecipe, PhoneMenuBreakpoint, PictureRefusal, PictureWorld, PlaceholderKind, PlanChangeAction, PlanChangeKind, PlanDataSourceMode, PlanDataSourceType, PlanEditIssueCode, PlanEditOp, PlanEditsOutcome, PlanPart, PlanWireEntry, PlanWireShapeError, PlanWordIssueCode, PreflightRefusalCode, PublishRefusalCode, REQUIRED_CONTENT_ROLES, RelativeOp, SAMPLE_IMAGE_REF, SCENE_LEVEL, SCENE_OTHER_PAGES, SCRIM_BACKGROUND, SCRIM_COVER, SCRIM_LEVELS, SHOWCASE_FILM, SITE_CHECKS, SITE_FACT_LABELS, SITE_HEADER_PATTERN, SITE_LIBRARY, SITE_LOCALES, SITE_NEED_IDS, SITE_PATTERNS, SITE_PLAN_VERSION, SITE_TEMPLATES, SPEC_SITE_ARCHETYPES, SPEC_SITE_COPY_LENGTHS, SPEC_SITE_DENSITY_COPY, SPEC_SITE_FACTS, SPEC_SITE_LIBRARY, SPEC_SITE_LOCALES, SPEC_SITE_NEED_IDS, SPEC_SITE_PATTERNS, SPEC_SITE_PICTURE_NEVER, SPEC_SITE_PICTURE_WORLDS, SPEC_SITE_PLAN_SHAPE, SPEC_SITE_REQUIRED_ROLES, SPEC_SITE_ROLES, SPEC_SITE_TEMPLATES, STOCK_FAMILIES, STOCK_FAMILY_INTENTS, STOCK_INTENT_ORDER, STOCK_INTENT_SUBJECTS, STOCK_ITEM_ASPECT, STOCK_MIN_KEPT, STOCK_PROVIDER_TERMS, STOCK_SEED_MARGIN, STOCK_SUBJECT_WORDS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, ScrimLevel, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockIntent, StockOrientation, StockProviderId, StockReviewFlag, StockReviewVerdict, StockSlotKind, StockSubject, WORDS_AREA_DEFAULT_SHARE, WordsEdge, addBarItem, addStockCredits, aiStepParts, applyOwnPhotoGalleries, applyPictureScrims, applyPlanEdits, applyStockItemPhotos, applyStockPhotos, archetypeIssues, areaLuminance, artDirectionFor, artDirectionIssues, artDirectionPreamble, aspectRatioOf, assertStyleSlots, assertValidSitePlan, auditSite, barIconForRoute, blocksPublish, brandHueWord, buildKeys, buildStepFacts, builtHomePageId, chooseScrim, composeNavHeader, composeSection, composeSite, contentTypePreviewFields, contentTypeToWire, copyLengthOf, costUsd, createSerializedNode, creditsOfUsd, dataSourceToWire, derivedDataSources, describePlanData, describeShapeKey, emptyAreaPhrase, entranceAction, entranceInteraction, estimatePictures, estimateStepCredits, expandPlanWire, expandWireContentType, expandWireDataSource, expandWireEdit, expandWireField, expandWirePage, expandWireSection, failedBuildStep, fieldToWire, filmOfPlan, filmPlacement, findNavRoot, frameWords, isActiveBuild, isNonEmptyString, isPictureWorld, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, luminanceOf, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, orientationOfRatio, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternListGap, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, pictureAspect, picturePrompt, pictureRefusalOf, pictureRerolls, pictureReviewIssues, pictureReviewJsonSchema, pictureReviewPasses, pictureSlotKey, pictureTargets, placeFilm, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planOwnPhotos, planPartOfPath, planPartToWire, planPictures, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, recipeListBinds, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, scoreStockPhoto, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, showcaseScenes, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockFits, stockGroupOf, stockItemSlots, stockKept, stockPhotoRatio, stockPhotoSubject, stockPhotoUsable, stockPhotoWords, stockSlots, stockWord, stockWords, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, withDerivedSources, wordPattern, wordsOverPicture, writtenTexts };
16972
17613
  //# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map