@vectoriox/iox-ui 5.66.0 → 5.67.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.
- package/actions-core/index.d.ts +2 -1
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs +1 -1
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +333 -17
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/generation-core/index.d.ts +173 -16
- package/package.json +1 -1
- package/src/actions-core/dialog-motion.ts +3 -2
- package/src/generation-core/public-api.ts +1 -0
- package/src/generation-core/site-build-run.ts +3 -0
- package/src/generation-core/stock-match.ts +238 -0
- package/src/generation-core/stock-photos.ts +153 -22
|
@@ -11598,7 +11598,7 @@ function navShapeBreakpoint(globals) {
|
|
|
11598
11598
|
}
|
|
11599
11599
|
const prop = (n, name) => n.props?.[name];
|
|
11600
11600
|
const isPageLink = (n) => prop(n, 'linkType') === LinkType.Page && typeof prop(n, 'pageRoute') === 'string';
|
|
11601
|
-
function textOf(n) {
|
|
11601
|
+
function textOf$1(n) {
|
|
11602
11602
|
if (n.type === LINK)
|
|
11603
11603
|
return String(prop(n, 'label') ?? '');
|
|
11604
11604
|
let words = '';
|
|
@@ -11610,7 +11610,7 @@ function textOf(n) {
|
|
|
11610
11610
|
function navPages(root) {
|
|
11611
11611
|
const links = findAll([root], NavRole.Link);
|
|
11612
11612
|
const source = links.length ? links : collectHeaderLinks(root);
|
|
11613
|
-
return source.filter(isPageLink).map(n => ({ label: textOf(n), route: String(prop(n, 'pageRoute')) }));
|
|
11613
|
+
return source.filter(isPageLink).map(n => ({ label: textOf$1(n), route: String(prop(n, 'pageRoute')) }));
|
|
11614
11614
|
}
|
|
11615
11615
|
/** Where a phone menu's pages come from: the header's own page Links, else the studio's pages. */
|
|
11616
11616
|
var NavPagesSource;
|
|
@@ -11744,7 +11744,7 @@ function drawer(kit, root, pages, at) {
|
|
|
11744
11744
|
const dr = part(kit, 'drawer', DIALOG$1, {
|
|
11745
11745
|
placement: 'end', closeOnBackdrop: true, showCloseButton: false, ariaLabel: NAV_SHAPE_COPY.menu, closeAbove: at,
|
|
11746
11746
|
}, 'dialog', NavRole.Drawer, { width: '86vw', maxWidth: '360px', display: 'flex', flexDirection: 'column', gap: '20px' });
|
|
11747
|
-
const brandWords = textOf(find([root], NavRole.Brand) ?? root) || NAV_PART_LABELS[NavRole.Root];
|
|
11747
|
+
const brandWords = textOf$1(find([root], NavRole.Brand) ?? root) || NAV_PART_LABELS[NavRole.Root];
|
|
11748
11748
|
const head = part(kit, 'drawer-head', CONTAINER, {}, 'box', NavRole.DrawerHead, { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '12px' });
|
|
11749
11749
|
const close = part(kit, 'drawer-close', BUTTON, { tag: 'button', ariaLabel: NAV_SHAPE_COPY.closeMenu }, 'button-ghost', NavRole.DrawerClose, { ...MENU_BUTTON_LAY });
|
|
11750
11750
|
close.children = [iconNode(kit, 'drawer-close-icon', CLOSE_ICON)];
|
|
@@ -11974,7 +11974,7 @@ function nameParts(root) {
|
|
|
11974
11974
|
}
|
|
11975
11975
|
const cta = find([root], NavRole.Cta);
|
|
11976
11976
|
if (cta && !cta.label) {
|
|
11977
|
-
const words = textOf(cta);
|
|
11977
|
+
const words = textOf$1(cta);
|
|
11978
11978
|
if (words)
|
|
11979
11979
|
cta.label = words;
|
|
11980
11980
|
}
|
|
@@ -13352,6 +13352,9 @@ var BuildNoticeCode;
|
|
|
13352
13352
|
BuildNoticeCode["ContentTypeNotPublic"] = "content-type-not-public";
|
|
13353
13353
|
/** The stock library could not be read: the picture slots keep the empty box, and the run says so. */
|
|
13354
13354
|
BuildNoticeCode["StockUnavailable"] = "stock-unavailable";
|
|
13355
|
+
/** The stock library was read but holds no photo for this kind of business (never built, or reviewed down to
|
|
13356
|
+
* none): the picture slots keep the empty box, and the run says so (I2.1 — never silently). */
|
|
13357
|
+
BuildNoticeCode["StockMissing"] = "stock-missing";
|
|
13355
13358
|
})(BuildNoticeCode || (BuildNoticeCode = {}));
|
|
13356
13359
|
var PreflightRefusalCode;
|
|
13357
13360
|
(function (PreflightRefusalCode) {
|
|
@@ -15309,10 +15312,217 @@ function publishVerdict(audit, acknowledged = []) {
|
|
|
15309
15312
|
}
|
|
15310
15313
|
|
|
15311
15314
|
/**
|
|
15312
|
-
*
|
|
15315
|
+
* How a stock photo is judged against a picture slot (AI plan §3g, I2.1) — pure and deterministic, so the same plan and
|
|
15316
|
+
* the same library always place the same photos. A photo is described by what the library's manifest carries (the
|
|
15317
|
+
* provider's tags, its orientation, the reviewer's subject and notes — each optional, derived when absent); a slot by
|
|
15318
|
+
* its INTENT (what kind of picture its section wants), its aspect ratio and the plan's words around it.
|
|
15319
|
+
*/
|
|
15320
|
+
var StockOrientation;
|
|
15321
|
+
(function (StockOrientation) {
|
|
15322
|
+
StockOrientation["Horizontal"] = "horizontal";
|
|
15323
|
+
StockOrientation["Vertical"] = "vertical";
|
|
15324
|
+
StockOrientation["Square"] = "square";
|
|
15325
|
+
})(StockOrientation || (StockOrientation = {}));
|
|
15326
|
+
/** What a photo mainly shows — the reviewer may set it; otherwise it is read from the provider's tags. */
|
|
15327
|
+
var StockSubject;
|
|
15328
|
+
(function (StockSubject) {
|
|
15329
|
+
/** A room, a shop front, a garden — somewhere. */
|
|
15330
|
+
StockSubject["Place"] = "place";
|
|
15331
|
+
/** A close-up: a texture, a tool, an ingredient. */
|
|
15332
|
+
StockSubject["Detail"] = "detail";
|
|
15333
|
+
/** A thing a business sells or serves: a cake, a bottle, a bouquet. */
|
|
15334
|
+
StockSubject["Product"] = "product";
|
|
15335
|
+
})(StockSubject || (StockSubject = {}));
|
|
15336
|
+
/** What a slot wants to show. */
|
|
15337
|
+
var StockIntent;
|
|
15338
|
+
(function (StockIntent) {
|
|
15339
|
+
/** The hero / a page header / a call to action: wide and atmospheric. */
|
|
15340
|
+
StockIntent["Atmosphere"] = "atmosphere";
|
|
15341
|
+
/** A story or a visit section: the place. */
|
|
15342
|
+
StockIntent["Place"] = "place";
|
|
15343
|
+
/** A feature row: a detail of the work or the service. */
|
|
15344
|
+
StockIntent["Detail"] = "detail";
|
|
15345
|
+
/** A list item's picture (a product, a service, a dish). */
|
|
15346
|
+
StockIntent["Product"] = "product";
|
|
15347
|
+
})(StockIntent || (StockIntent = {}));
|
|
15348
|
+
var StockReviewVerdict;
|
|
15349
|
+
(function (StockReviewVerdict) {
|
|
15350
|
+
StockReviewVerdict["Approved"] = "approved";
|
|
15351
|
+
StockReviewVerdict["Rejected"] = "rejected";
|
|
15352
|
+
})(StockReviewVerdict || (StockReviewVerdict = {}));
|
|
15353
|
+
/** What the review found in a photo that keeps it out of every site (the licence: no person, no logo or brand). */
|
|
15354
|
+
var StockReviewFlag;
|
|
15355
|
+
(function (StockReviewFlag) {
|
|
15356
|
+
StockReviewFlag["Person"] = "person";
|
|
15357
|
+
StockReviewFlag["Logo"] = "logo";
|
|
15358
|
+
})(StockReviewFlag || (StockReviewFlag = {}));
|
|
15359
|
+
/** The section families a stock photo may stand in for a picture in, and what each wants. */
|
|
15360
|
+
const STOCK_FAMILY_INTENTS = {
|
|
15361
|
+
hero: StockIntent.Atmosphere,
|
|
15362
|
+
header: StockIntent.Atmosphere,
|
|
15363
|
+
cta: StockIntent.Atmosphere,
|
|
15364
|
+
story: StockIntent.Place,
|
|
15365
|
+
contact: StockIntent.Place,
|
|
15366
|
+
feature: StockIntent.Detail,
|
|
15367
|
+
content: StockIntent.Detail,
|
|
15368
|
+
};
|
|
15369
|
+
/** Per intent, the subjects it takes, best first. */
|
|
15370
|
+
const STOCK_INTENT_SUBJECTS = {
|
|
15371
|
+
[StockIntent.Atmosphere]: [StockSubject.Place, StockSubject.Detail, StockSubject.Product],
|
|
15372
|
+
[StockIntent.Place]: [StockSubject.Place, StockSubject.Detail, StockSubject.Product],
|
|
15373
|
+
[StockIntent.Detail]: [StockSubject.Detail, StockSubject.Product, StockSubject.Place],
|
|
15374
|
+
[StockIntent.Product]: [StockSubject.Product, StockSubject.Detail, StockSubject.Place],
|
|
15375
|
+
};
|
|
15376
|
+
/** The order slots choose in: the hero picks first, a list item last. */
|
|
15377
|
+
const STOCK_INTENT_ORDER = [StockIntent.Atmosphere, StockIntent.Place, StockIntent.Detail, StockIntent.Product];
|
|
15378
|
+
/** The words that tell a photo's subject when the reviewer did not name it (singular, lower case). */
|
|
15379
|
+
const STOCK_SUBJECT_WORDS = {
|
|
15380
|
+
[StockSubject.Place]: [
|
|
15381
|
+
'room', 'interior', 'studio', 'salon', 'shop', 'store', 'boutique', 'restaurant', 'cafe', 'bar', 'bakery', 'cellar', 'kitchen',
|
|
15382
|
+
'gym', 'clinic', 'office', 'lobby', 'hall', 'building', 'architecture', 'facade', 'exterior', 'street', 'garden', 'terrace',
|
|
15383
|
+
'patio', 'pool', 'sauna', 'window', 'landscape', 'view', 'beach', 'forest', 'mountain', 'city', 'house', 'workshop', 'counter',
|
|
15384
|
+
'shelf', 'reception', 'spa', 'hotel', 'bedroom', 'bathroom', 'living', 'courtyard', 'vineyard', 'market',
|
|
15385
|
+
],
|
|
15386
|
+
[StockSubject.Detail]: [
|
|
15387
|
+
'texture', 'detail', 'closeup', 'macro', 'pattern', 'fabric', 'wood', 'wooden', 'stone', 'marble', 'leaf', 'towel', 'tool',
|
|
15388
|
+
'equipment', 'ingredient', 'spice', 'grain', 'bean', 'oil', 'water', 'steam', 'shadow', 'paper', 'thread', 'brush', 'ceramic',
|
|
15389
|
+
],
|
|
15390
|
+
[StockSubject.Product]: [
|
|
15391
|
+
'product', 'bottle', 'wine', 'beer', 'coffee', 'cup', 'mug', 'tea', 'cake', 'bread', 'pastry', 'croissant', 'cookie', 'dessert',
|
|
15392
|
+
'chocolate', 'food', 'dish', 'plate', 'meal', 'pizza', 'salad', 'soup', 'burger', 'fruit', 'flower', 'bouquet', 'jewelry',
|
|
15393
|
+
'jewellery', 'ring', 'necklace', 'cosmetic', 'cream', 'soap', 'perfume', 'candle', 'shoe', 'bag', 'dress', 'clothing', 'book', 'toy',
|
|
15394
|
+
],
|
|
15395
|
+
};
|
|
15396
|
+
/** Words that say nothing about what a picture shows (the plan's sample and placeholder words among them). */
|
|
15397
|
+
const STOP_WORDS = new Set([
|
|
15398
|
+
'the', 'and', 'with', 'for', 'your', 'our', 'you', 'from', 'into', 'this', 'that', 'here', 'there', 'about', 'page', 'section',
|
|
15399
|
+
'sample', 'replace', 'image', 'photo', 'picture', 'sentence', 'words', 'own', 'how', 'what', 'who', 'are', 'all', 'one', 'two',
|
|
15400
|
+
'three', 'more', 'less', 'very', 'tell', 'story', 'item', 'service', 'project',
|
|
15401
|
+
]);
|
|
15402
|
+
const MIN_WORD = 3;
|
|
15403
|
+
const HTML_TAG = /<[^>]*>/g;
|
|
15404
|
+
const WORD_SPLIT = /[^\p{L}\p{N}]+/u;
|
|
15405
|
+
/** Nominal ratios (width / height) of an orientation when the manifest gives no size. */
|
|
15406
|
+
const ORIENTATION_RATIO = {
|
|
15407
|
+
[StockOrientation.Horizontal]: 3 / 2,
|
|
15408
|
+
[StockOrientation.Vertical]: 2 / 3,
|
|
15409
|
+
[StockOrientation.Square]: 1,
|
|
15410
|
+
};
|
|
15411
|
+
/** The share of a photo a slot may crop away and still be a fit: a portrait never fills a 21 / 9 band. */
|
|
15412
|
+
const STOCK_MIN_KEPT = 0.5;
|
|
15413
|
+
/** What an unknown shape keeps, for scoring only (neither rewarded nor refused). */
|
|
15414
|
+
const UNKNOWN_KEPT = 0.75;
|
|
15415
|
+
/** Score weights — subject, then shape, then the plan's own words, then the business type's imagery words. */
|
|
15416
|
+
const SUBJECT_POINTS = [4, 2, 1];
|
|
15417
|
+
const UNKNOWN_SUBJECT_POINTS = 1.5;
|
|
15418
|
+
const SHAPE_POINTS = 4;
|
|
15419
|
+
const WORD_POINTS = 2;
|
|
15420
|
+
const MAX_WORD_MATCHES = 3;
|
|
15421
|
+
const IMAGERY_POINTS = 1;
|
|
15422
|
+
const MAX_IMAGERY_MATCHES = 2;
|
|
15423
|
+
/**
|
|
15424
|
+
* A word as compared — a canonical form, never shown: lower case, a plain plural made singular, and `-ie` / `-y` one
|
|
15425
|
+
* ending (English cannot tell `cookies` from `pastries` by rule, so `cookie`, `cookies`, `pastry`, `pastries` all
|
|
15426
|
+
* fold to the same shape as their singular).
|
|
15427
|
+
*/
|
|
15428
|
+
function stockWord(raw) {
|
|
15429
|
+
let w = raw.toLowerCase();
|
|
15430
|
+
if (w.length > 3 && w.endsWith('s') && !w.endsWith('ss') && !STOP_WORDS.has(w))
|
|
15431
|
+
w = w.slice(0, -1);
|
|
15432
|
+
return w.length > 3 && w.endsWith('ie') ? `${w.slice(0, -2)}y` : w;
|
|
15433
|
+
}
|
|
15434
|
+
/** The meaningful words of a text, in order, each once. */
|
|
15435
|
+
function stockWords(...texts) {
|
|
15436
|
+
const out = new Set();
|
|
15437
|
+
for (const text of texts) {
|
|
15438
|
+
if (typeof text !== 'string')
|
|
15439
|
+
continue;
|
|
15440
|
+
for (const raw of text.replace(HTML_TAG, ' ').split(WORD_SPLIT)) {
|
|
15441
|
+
const w = stockWord(raw);
|
|
15442
|
+
if (w.length >= MIN_WORD && !STOP_WORDS.has(w) && !/^\d+$/.test(w))
|
|
15443
|
+
out.add(w);
|
|
15444
|
+
}
|
|
15445
|
+
}
|
|
15446
|
+
return [...out];
|
|
15447
|
+
}
|
|
15448
|
+
/** A CSS aspect ratio (`4 / 3`, `1.5`) as width / height, or undefined. */
|
|
15449
|
+
function aspectRatioOf(aspect) {
|
|
15450
|
+
if (typeof aspect === 'number')
|
|
15451
|
+
return aspect > 0 ? aspect : undefined;
|
|
15452
|
+
if (typeof aspect !== 'string')
|
|
15453
|
+
return undefined;
|
|
15454
|
+
const [w, h] = aspect.split('/').map(s => Number(s.trim()));
|
|
15455
|
+
const ratio = h === undefined ? w : w / h;
|
|
15456
|
+
return Number.isFinite(ratio) && ratio > 0 ? ratio : undefined;
|
|
15457
|
+
}
|
|
15458
|
+
/** A photo may be placed: not rejected and nothing flagged by the review. */
|
|
15459
|
+
function stockPhotoUsable(photo) {
|
|
15460
|
+
return photo.review?.verdict !== StockReviewVerdict.Rejected && !(photo.review?.flags?.length);
|
|
15461
|
+
}
|
|
15462
|
+
/** The words a photo is placed by: its tags (else its alt — the provider's own words) and the reviewer's notes. */
|
|
15463
|
+
function stockPhotoWords(photo) {
|
|
15464
|
+
const tags = photo.tags?.length ? photo.tags.join(', ') : photo.alt;
|
|
15465
|
+
return stockWords(tags, photo.review?.notes);
|
|
15466
|
+
}
|
|
15467
|
+
/** Its width / height: the size when known, else its orientation's nominal shape, else undefined. */
|
|
15468
|
+
function stockPhotoRatio(photo) {
|
|
15469
|
+
if (photo.width && photo.height && photo.width > 0 && photo.height > 0)
|
|
15470
|
+
return photo.width / photo.height;
|
|
15471
|
+
return photo.orientation ? ORIENTATION_RATIO[photo.orientation] : undefined;
|
|
15472
|
+
}
|
|
15473
|
+
/** The orientation of a width / height ratio. */
|
|
15474
|
+
function orientationOfRatio(ratio) {
|
|
15475
|
+
if (ratio > 1.05)
|
|
15476
|
+
return StockOrientation.Horizontal;
|
|
15477
|
+
return ratio < 0.95 ? StockOrientation.Vertical : StockOrientation.Square;
|
|
15478
|
+
}
|
|
15479
|
+
/** The reviewer's subject, else the one its words name most (ties: the enum's order), else undefined. */
|
|
15480
|
+
function stockPhotoSubject(photo) {
|
|
15481
|
+
if (photo.subject && Object.values(StockSubject).includes(photo.subject))
|
|
15482
|
+
return photo.subject;
|
|
15483
|
+
const words = new Set(stockPhotoWords(photo));
|
|
15484
|
+
let best;
|
|
15485
|
+
let most = 0;
|
|
15486
|
+
for (const subject of Object.values(StockSubject)) {
|
|
15487
|
+
const n = STOCK_SUBJECT_WORDS[subject].filter(w => words.has(stockWord(w))).length;
|
|
15488
|
+
if (n > most) {
|
|
15489
|
+
most = n;
|
|
15490
|
+
best = subject;
|
|
15491
|
+
}
|
|
15492
|
+
}
|
|
15493
|
+
return best;
|
|
15494
|
+
}
|
|
15495
|
+
/** The share of the photo a slot shows (1 = the same shape), or undefined when either shape is unknown. */
|
|
15496
|
+
function stockKept(photo, aspect) {
|
|
15497
|
+
const ratio = stockPhotoRatio(photo);
|
|
15498
|
+
if (!ratio || !aspect)
|
|
15499
|
+
return undefined;
|
|
15500
|
+
return Math.min(ratio, aspect) / Math.max(ratio, aspect);
|
|
15501
|
+
}
|
|
15502
|
+
/** A photo fits a slot when the slot keeps at least half of it (an unknown shape fits). */
|
|
15503
|
+
function stockFits(photo, need) {
|
|
15504
|
+
const kept = stockKept(photo, need.aspect);
|
|
15505
|
+
return kept === undefined || kept >= STOCK_MIN_KEPT;
|
|
15506
|
+
}
|
|
15507
|
+
/** How well a photo answers a slot: its subject for the intent, its shape, the plan's words and the type's imagery. */
|
|
15508
|
+
function scoreStockPhoto(photo, need, imagery = []) {
|
|
15509
|
+
const subject = stockPhotoSubject(photo);
|
|
15510
|
+
const rank = subject ? STOCK_INTENT_SUBJECTS[need.intent].indexOf(subject) : -1;
|
|
15511
|
+
const subjectPoints = subject ? SUBJECT_POINTS[rank] ?? 0 : UNKNOWN_SUBJECT_POINTS;
|
|
15512
|
+
const shapePoints = SHAPE_POINTS * (stockKept(photo, need.aspect) ?? UNKNOWN_KEPT);
|
|
15513
|
+
const words = new Set(stockPhotoWords(photo));
|
|
15514
|
+
const matched = need.words.filter(w => words.has(w)).length;
|
|
15515
|
+
const imageryMatched = imagery.filter(w => words.has(w)).length;
|
|
15516
|
+
return subjectPoints + shapePoints + WORD_POINTS * Math.min(MAX_WORD_MATCHES, matched) + IMAGERY_POINTS * Math.min(MAX_IMAGERY_MATCHES, imageryMatched);
|
|
15517
|
+
}
|
|
15518
|
+
|
|
15519
|
+
/**
|
|
15520
|
+
* Stock photos in a built site (AI plan §3g / §3m, I2 + I2.1): a FREE picture from the platform's curated library, copied
|
|
15313
15521
|
* into the studio's own storage (never hot-linked), placed only in an EMPTY picture slot of a section that may show
|
|
15314
15522
|
* one — never the owner's work (a collection, a detail page, a gallery: what the nine portfolio types sell is theirs),
|
|
15315
|
-
* never a person (testimonials, the team), never anywhere when the business type says `stock: false
|
|
15523
|
+
* never a person (testimonials, the team), never anywhere when the business type says `stock: false` — and, since I2.1
|
|
15524
|
+
* (Doron, 2026-10-28), in the picture of a MARKED sample item (a product, a service, a dish) of a type that may show
|
|
15525
|
+
* stock. Which photo goes where is chosen by what the slot is about (`stock-match.ts`). Its alt text is
|
|
15316
15526
|
* the provider's description of what it shows (never a claim), and the credit line its licence requires — when it
|
|
15317
15527
|
* requires one — is placed in the footer. Pure: the build step does the copying.
|
|
15318
15528
|
*/
|
|
@@ -15341,30 +15551,124 @@ const STOCK_PROVIDER_TERMS = {
|
|
|
15341
15551
|
},
|
|
15342
15552
|
};
|
|
15343
15553
|
/** The section families a stock photo may stand in for a picture in. */
|
|
15344
|
-
const STOCK_FAMILIES = new Set(
|
|
15345
|
-
/** The
|
|
15554
|
+
const STOCK_FAMILIES = new Set(Object.keys(STOCK_FAMILY_INTENTS));
|
|
15555
|
+
/** The aspect a list item's picture is shown at (Card v2's Image top, `composite-core` card layouts). */
|
|
15556
|
+
const STOCK_ITEM_ASPECT = 4 / 3;
|
|
15557
|
+
var StockSlotKind;
|
|
15558
|
+
(function (StockSlotKind) {
|
|
15559
|
+
/** An empty Image of a composed section. */
|
|
15560
|
+
StockSlotKind["Section"] = "section";
|
|
15561
|
+
/** The image field of a marked sample item. */
|
|
15562
|
+
StockSlotKind["Item"] = "item";
|
|
15563
|
+
})(StockSlotKind || (StockSlotKind = {}));
|
|
15564
|
+
/** Where a photo may not appear twice: one page; one content type's samples (a list shows them together). */
|
|
15565
|
+
function stockGroupOf(target) {
|
|
15566
|
+
return target.kind === StockSlotKind.Section ? `page:${target.owner}` : `items:${target.contentType}`;
|
|
15567
|
+
}
|
|
15568
|
+
const TEXT_PROPS = ['content', 'label'];
|
|
15569
|
+
/** The words of a subtree's text (Headings, Text, Links), the given node left out. */
|
|
15570
|
+
function textOf(nodes, skip) {
|
|
15571
|
+
const out = [];
|
|
15572
|
+
walkSerialized(nodes, n => {
|
|
15573
|
+
if (n.id === skip)
|
|
15574
|
+
return;
|
|
15575
|
+
for (const prop of TEXT_PROPS)
|
|
15576
|
+
if (typeof n.props[prop] === 'string')
|
|
15577
|
+
out.push(n.props[prop]);
|
|
15578
|
+
});
|
|
15579
|
+
return out;
|
|
15580
|
+
}
|
|
15581
|
+
/**
|
|
15582
|
+
* The empty picture slots of a composed site a stock photo may fill, in page and section order — each with its intent
|
|
15583
|
+
* (the section's family), its aspect, and the words around it: the text beside it (its row), the section's slots and
|
|
15584
|
+
* the page's name and search words.
|
|
15585
|
+
*/
|
|
15346
15586
|
function stockSlots(plan, composed) {
|
|
15347
15587
|
const empty = new Set(composed.placeholders.filter(p => p.kind === PlaceholderKind.Image).map(p => p.nodeId));
|
|
15348
15588
|
const out = [];
|
|
15349
15589
|
composed.pages.forEach((page, p) => {
|
|
15590
|
+
const pagePlan = plan.pages[p];
|
|
15350
15591
|
page.layout.forEach((root, s) => {
|
|
15351
|
-
const
|
|
15592
|
+
const section = pagePlan?.sections[s];
|
|
15593
|
+
const pattern = section?.pattern;
|
|
15352
15594
|
const family = patternById(pattern)?.family;
|
|
15353
|
-
|
|
15595
|
+
const intent = family ? STOCK_FAMILY_INTENTS[family] : undefined;
|
|
15596
|
+
if (!pattern || !intent)
|
|
15354
15597
|
return;
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15598
|
+
const sectionWords = [...Object.values(section?.slots ?? {}), pagePlan?.name, pagePlan?.seo?.title, pagePlan?.seo?.description];
|
|
15599
|
+
walkSerialized([root], (n, ancestors) => {
|
|
15600
|
+
if (n.type !== 'Image' || !empty.has(n.id) || n.bindings.length)
|
|
15601
|
+
return;
|
|
15602
|
+
const row = ancestors[ancestors.length - 1];
|
|
15603
|
+
out.push({
|
|
15604
|
+
kind: StockSlotKind.Section, nodeId: n.id, owner: page.key, pattern, intent,
|
|
15605
|
+
aspect: aspectRatioOf(n.styleProps['aspectRatio']),
|
|
15606
|
+
words: stockWords(...(row ? textOf([row], n.id) : []), ...sectionWords),
|
|
15607
|
+
});
|
|
15358
15608
|
});
|
|
15359
15609
|
});
|
|
15360
15610
|
});
|
|
15361
15611
|
return out;
|
|
15362
15612
|
}
|
|
15363
|
-
/**
|
|
15364
|
-
|
|
15613
|
+
/**
|
|
15614
|
+
* The sample items' pictures a stock photo may fill: per content type with an `image` role, each MARKED sample whose
|
|
15615
|
+
* image is still a bundled sample (`{sampleImage:k}`), with the type's and the item's own words.
|
|
15616
|
+
*/
|
|
15617
|
+
function stockItemSlots(plan, composed) {
|
|
15618
|
+
const out = [];
|
|
15619
|
+
for (const type of plan.contentTypes) {
|
|
15620
|
+
const field = roleField(type, ContentRole.Image);
|
|
15621
|
+
if (!field)
|
|
15622
|
+
continue;
|
|
15623
|
+
const words = (item) => [type.label, type.name, ...[ContentRole.Title, ContentRole.Summary].map(r => item[roleField(type, r) ?? ''])];
|
|
15624
|
+
(composed.items[type.name] ?? []).forEach((item, index) => {
|
|
15625
|
+
if (!item.isSample || parseSampleImageRef(item[field]) === null)
|
|
15626
|
+
return;
|
|
15627
|
+
out.push({ kind: StockSlotKind.Item, contentType: type.name, index, field, intent: StockIntent.Product, aspect: STOCK_ITEM_ASPECT, words: stockWords(...words(item)) });
|
|
15628
|
+
});
|
|
15629
|
+
}
|
|
15630
|
+
return out;
|
|
15631
|
+
}
|
|
15632
|
+
const intentRank = (intent) => STOCK_INTENT_ORDER.indexOf(intent);
|
|
15633
|
+
/**
|
|
15634
|
+
* Which photo goes where (I2.1). Slots choose in intent order (the hero first, list items last; page order within),
|
|
15635
|
+
* each taking the best-scoring photo that FITS its shape (`stockFits`) and is not already on its page (or among its
|
|
15636
|
+
* type's samples) — a photo no slot has taken yet always before one another page shows. Ties go to the library's
|
|
15637
|
+
* order. A slot nothing fits keeps its empty box (a sample its abstract picture). None at all when the type says no
|
|
15638
|
+
* stock, and never a photo the review rejected or flagged. Returned in the slots' order.
|
|
15639
|
+
*/
|
|
15640
|
+
function planStockPhotos(slots, photos, stockAllowed, opts = {}) {
|
|
15365
15641
|
if (!stockAllowed)
|
|
15366
15642
|
return [];
|
|
15367
|
-
|
|
15643
|
+
const usable = photos.filter(stockPhotoUsable);
|
|
15644
|
+
const imagery = stockWords(opts.imagery);
|
|
15645
|
+
const order = slots.map((slot, i) => ({ slot, i })).sort((a, b) => intentRank(a.slot.intent) - intentRank(b.slot.intent) || a.i - b.i);
|
|
15646
|
+
const used = new Set();
|
|
15647
|
+
const shownIn = new Map();
|
|
15648
|
+
const placed = new Map();
|
|
15649
|
+
for (const { slot, i } of order) {
|
|
15650
|
+
const group = stockGroupOf(slot);
|
|
15651
|
+
const here = shownIn.get(group) ?? new Set();
|
|
15652
|
+
const candidates = usable.filter(p => !here.has(p.key) && stockFits(p, slot));
|
|
15653
|
+
const fresh = candidates.filter(p => !used.has(p.key));
|
|
15654
|
+
const pool = fresh.length ? fresh : candidates;
|
|
15655
|
+
let best;
|
|
15656
|
+
let bestScore = -Infinity;
|
|
15657
|
+
for (const photo of pool) {
|
|
15658
|
+
const score = scoreStockPhoto(photo, slot, imagery);
|
|
15659
|
+
if (score > bestScore) {
|
|
15660
|
+
best = photo;
|
|
15661
|
+
bestScore = score;
|
|
15662
|
+
}
|
|
15663
|
+
}
|
|
15664
|
+
if (!best)
|
|
15665
|
+
continue;
|
|
15666
|
+
used.add(best.key);
|
|
15667
|
+
here.add(best.key);
|
|
15668
|
+
shownIn.set(group, here);
|
|
15669
|
+
placed.set(i, { ...slot, photo: best });
|
|
15670
|
+
}
|
|
15671
|
+
return slots.map((_, i) => placed.get(i)).filter((p) => !!p);
|
|
15368
15672
|
}
|
|
15369
15673
|
/** A placed photo's copy in the studio's storage: the key the build writes it under. */
|
|
15370
15674
|
function stockCopyKey(photo) {
|
|
@@ -15384,6 +15688,18 @@ function applyStockPhotos(trees, placed) {
|
|
|
15384
15688
|
});
|
|
15385
15689
|
return changed;
|
|
15386
15690
|
}
|
|
15691
|
+
/** Put the studio's copy of each photo into its sample item's image field. Returns the items changed. */
|
|
15692
|
+
function applyStockItemPhotos(items, placed) {
|
|
15693
|
+
let changed = 0;
|
|
15694
|
+
for (const p of placed) {
|
|
15695
|
+
const item = items[p.contentType]?.[p.index];
|
|
15696
|
+
if (!item)
|
|
15697
|
+
continue;
|
|
15698
|
+
item[p.field] = p.url;
|
|
15699
|
+
changed++;
|
|
15700
|
+
}
|
|
15701
|
+
return changed;
|
|
15702
|
+
}
|
|
15387
15703
|
/** The line a picture's licence requires on the site, or null when it requires none (`STOCK_PROVIDER_TERMS`). */
|
|
15388
15704
|
function stockCreditLine(credit) {
|
|
15389
15705
|
return STOCK_PROVIDER_TERMS[credit.provider]?.credit ?? null;
|
|
@@ -15437,5 +15753,5 @@ function addStockCredits(footer, credits) {
|
|
|
15437
15753
|
* Generated bundle index. Do not edit.
|
|
15438
15754
|
*/
|
|
15439
15755
|
|
|
15440
|
-
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, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FINAL_GENERATION_STAGES, FINDING_SEVERITY, 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, 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_PROVIDER_TERMS, STRICT_SCHEMA_BUDGET, STRICT_SCHEMA_LIMITS, STUDIO_TEMPLATE, SiteBuildKind, SiteBuildStatus, SiteCheckId, SiteFindingCode, SiteLocale, SitePlanError, SitePlanIssueCode, SitePlanSourceKind, SiteTemplateError, SiteTemplateId, StepState, StockProviderId, addBarItem, addStockCredits, aiStepParts, applyPlanEdits, applyStockPhotos, archetypeIssues, 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, findNavRoot, isActiveBuild, isNonEmptyString, isPlainObject, isRtlLocale, jsonAnyOf, jsonBoolean, jsonClosedObject, jsonInteger, jsonListOf, jsonOneOf, jsonSchemaKeywords, jsonSchemaObjects, jsonShapedObject, jsonText, modelCanDo, moveBarItem, navPages, navShapeBreakpoint, navShapeOf, nextSourceOf, nodeBindPaths, pageToWire, parsePatternPath, parseSampleImageRef, pathAt, patternById, patternHasForm, patternLinkSlots, patternListGap, patternManifest, patternNeedsBinding, patternNodes, patternOptionalParts, patternRoleNeeds, patternScopes, patternSlotBudgets, patternSlots, patternTakesBinding, phoneMenuPages, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, planPartOfPath, planPartToWire, planStockPhotos, planToWire, planWireGaps, planWirePartJsonSchema, planWirePartShape, planWordIssues, publishRefusal, publishVerdict, recipeListBinds, releasedLayoutDefaults, removeBarItem, requiredAcknowledgements, requiredShapeKeys, roleField, routeMatches, routeParams, sampleImageRef, sectionEntrances, sectionPatternIds, sectionToWire, setStyleProps, shapeKeys, showcaseGlobals, showcasePages, showcasePlans, siteNavPages, slotBudget, stockCopyKey, stockCreditLine, stockCreditLines, stockCreditNode, stockCreditNodeId, stockSlots, strictSchemaComplexity, strictSchemaRefusal, typicalStepTokens, validatePlanEdits, validateSitePlan, walkSerialized, wireContentTypeJsonSchema, wireDataSourceJsonSchema, wireFieldJsonSchema, wirePageJsonSchema, wireSectionJsonSchema, withDerivedSources, wordPattern, writtenTexts };
|
|
15756
|
+
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, ContentRole, ContentTypeAction, CreatedKind, DANGLING_LINK_CODES, DEFAULT_PHONE_MENU_BREAKPOINT, DEFAULT_SITE_LOCALE, ENTRANCE_EASING, ENTRANCE_ORIGIN_LOOK, ENTRANCE_TRIGGER, EntrancePart, EntranceSide, FACT_KEYS, FINAL_GENERATION_STAGES, FINDING_SEVERITY, 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, 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, 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, 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, planEditOps, planEditToWire, planEditWireGaps, planEditWireKeys, planEditWireOps, planEditWireShape, planEditsFromWire, planEditsJsonSchema, planEditsToWire, planEntrances, planFromTemplate, planNavShape, 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, 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 };
|
|
15441
15757
|
//# sourceMappingURL=vectoriox-iox-ui-generation-core.mjs.map
|