@aglyn/plugins-ai 1.0.0-beta.269 → 1.0.0-beta.270

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 (40) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-card.component.d.ts +4 -3
  3. package/src/lib/components/ai-credits-card.component.js +25 -8
  4. package/src/lib/components/ai-credits-card.component.js.map +1 -1
  5. package/src/lib/components/ai-job-links.d.ts +6 -0
  6. package/src/lib/components/ai-job-links.js +9 -0
  7. package/src/lib/components/ai-job-links.js.map +1 -1
  8. package/src/lib/components/ai-products-hub-card.component.d.ts +6 -0
  9. package/src/lib/components/ai-products-hub-card.component.js +21 -6
  10. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  11. package/src/lib/components/ai-site-build-page.component.js +3 -2
  12. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  13. package/src/lib/components/ai-store-finish-card.component.d.ts +14 -3
  14. package/src/lib/components/ai-store-finish-card.component.js +67 -9
  15. package/src/lib/components/ai-store-finish-card.component.js.map +1 -1
  16. package/src/lib/components/assist-build-card.component.js +3 -2
  17. package/src/lib/components/assist-build-card.component.js.map +1 -1
  18. package/src/lib/jobs/ai-job-build-step.d.ts +3 -0
  19. package/src/lib/jobs/ai-job-build-step.js +8 -2
  20. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  21. package/src/lib/jobs/ai-job-form-step.d.ts +9 -0
  22. package/src/lib/jobs/ai-job-form-step.js +21 -2
  23. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  24. package/src/lib/jobs/ai-job-page-language.d.ts +22 -11
  25. package/src/lib/jobs/ai-job-page-language.js +31 -1
  26. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  27. package/src/lib/jobs/ai-job-page-step.js +4 -2
  28. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  29. package/src/lib/jobs/ai-layout-owner-photos.d.ts +57 -0
  30. package/src/lib/jobs/ai-layout-owner-photos.js +110 -0
  31. package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -0
  32. package/src/lib/jobs/ai-layout-stock-photos.d.ts +35 -2
  33. package/src/lib/jobs/ai-layout-stock-photos.js +334 -54
  34. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  35. package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.d.ts +29 -0
  36. package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.js +291 -0
  37. package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.js.map +1 -0
  38. package/src/lib/native-nav.d.ts +26 -0
  39. package/src/lib/native-nav.js +58 -0
  40. package/src/lib/native-nav.js.map +1 -0
@@ -1985,7 +1985,56 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
1985
1985
  const said = texts.filter(Boolean).join(' . ');
1986
1986
  return ADULT_AUDIENCE.test(said) && !YOUNG_AUDIENCE.test(said);
1987
1987
  }
1988
- /** Where a picture of a moment is set, by the words that name it, and the word a search and a photo say it with. */ const SCENE_SETTINGS = [
1988
+ /**
1989
+ * Words a photo by the sea is tagged with: never one set in the mountains, a
1990
+ * forest or a city (AGL-3942). Never "palm" or "wave", a hand's as often.
1991
+ */ const SEASIDE = [
1992
+ 'beach',
1993
+ 'beaches',
1994
+ 'sea',
1995
+ 'seaside',
1996
+ 'seashore',
1997
+ 'ocean',
1998
+ 'coast',
1999
+ 'coastal',
2000
+ 'shore',
2001
+ 'shoreline',
2002
+ 'surf',
2003
+ 'waves',
2004
+ 'caribbean',
2005
+ 'tropical',
2006
+ 'island',
2007
+ 'lagoon',
2008
+ 'bay',
2009
+ 'pier'
2010
+ ];
2011
+ /** Words a photo of a desert is tagged with. */ const DESERT = [
2012
+ 'desert',
2013
+ 'dune',
2014
+ 'dunes',
2015
+ 'cactus',
2016
+ 'sahara'
2017
+ ];
2018
+ /** Words a photo of a city is tagged with. */ const CITYSCAPE = [
2019
+ 'city',
2020
+ 'urban',
2021
+ 'downtown',
2022
+ 'street',
2023
+ 'skyline',
2024
+ 'skyscraper',
2025
+ 'skyscrapers',
2026
+ 'subway',
2027
+ 'metro'
2028
+ ];
2029
+ /**
2030
+ * Where a picture of a moment is set, by the words that name it, the word a
2031
+ * search and a photo say it with, whether it is a place a photo can show on
2032
+ * its own (a landscape, not a season), and the words of the places it is
2033
+ * not, which none of a photo's first tags may name (AGL-3942): the beta.267
2034
+ * Copper Hollow Film & Portrait galleries, a Blue Ridge photographer's,
2035
+ * showed two couples on a beach, tagged "beach, couple, nature, mountain"
2036
+ * and "romantic wedding couple, the beach".
2037
+ */ const SCENE_SETTINGS = [
1989
2038
  {
1990
2039
  words: [
1991
2040
  'mountain',
@@ -1998,7 +2047,13 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
1998
2047
  'overlook',
1999
2048
  'parkway'
2000
2049
  ],
2001
- said: 'mountains'
2050
+ said: 'mountains',
2051
+ landscape: true,
2052
+ away: [
2053
+ ...SEASIDE,
2054
+ ...DESERT,
2055
+ ...CITYSCAPE
2056
+ ]
2002
2057
  },
2003
2058
  {
2004
2059
  words: [
@@ -2006,7 +2061,14 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2006
2061
  'autumn',
2007
2062
  'foliage'
2008
2063
  ],
2009
- said: 'autumn'
2064
+ said: 'autumn',
2065
+ away: [
2066
+ 'winter',
2067
+ 'snow',
2068
+ 'snowy',
2069
+ 'spring',
2070
+ 'blossom'
2071
+ ]
2010
2072
  },
2011
2073
  {
2012
2074
  words: [
@@ -2014,7 +2076,12 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2014
2076
  'snow',
2015
2077
  'snowy'
2016
2078
  ],
2017
- said: 'winter'
2079
+ said: 'winter',
2080
+ away: [
2081
+ ...SEASIDE,
2082
+ 'summer',
2083
+ 'tropical'
2084
+ ]
2018
2085
  },
2019
2086
  {
2020
2087
  words: [
@@ -2024,7 +2091,15 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2024
2091
  'shore',
2025
2092
  'ocean'
2026
2093
  ],
2027
- said: 'beach'
2094
+ said: 'beach',
2095
+ landscape: true,
2096
+ away: [
2097
+ 'snow',
2098
+ 'snowy',
2099
+ 'ski',
2100
+ 'skiing',
2101
+ ...CITYSCAPE
2102
+ ]
2028
2103
  },
2029
2104
  {
2030
2105
  words: [
@@ -2032,7 +2107,13 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2032
2107
  'woods',
2033
2108
  'woodland'
2034
2109
  ],
2035
- said: 'forest'
2110
+ said: 'forest',
2111
+ landscape: true,
2112
+ away: [
2113
+ ...SEASIDE,
2114
+ ...DESERT,
2115
+ ...CITYSCAPE
2116
+ ]
2036
2117
  },
2037
2118
  {
2038
2119
  words: [
@@ -2040,7 +2121,14 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2040
2121
  'blossom',
2041
2122
  'blossoms'
2042
2123
  ],
2043
- said: 'spring'
2124
+ said: 'spring',
2125
+ away: [
2126
+ 'winter',
2127
+ 'snow',
2128
+ 'snowy',
2129
+ 'autumn',
2130
+ 'fall'
2131
+ ]
2044
2132
  },
2045
2133
  {
2046
2134
  words: [
@@ -2048,16 +2136,27 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2048
2136
  'urban',
2049
2137
  'downtown'
2050
2138
  ],
2051
- said: 'city'
2139
+ said: 'city',
2140
+ landscape: true,
2141
+ away: [
2142
+ ...SEASIDE,
2143
+ ...DESERT
2144
+ ]
2052
2145
  }
2053
2146
  ];
2147
+ /** The stems of the places a setting is not ({@link SCENE_SETTINGS}); none for no setting. */ export function aiStockSettingAway(setting) {
2148
+ const entry = SCENE_SETTINGS.find((candidate)=>candidate.said === setting);
2149
+ return (entry == null ? void 0 : entry.away) ? [
2150
+ ...new Set(entry.away.map(aiStockStem))
2151
+ ] : [];
2152
+ }
2054
2153
  /**
2055
2154
  * The scene a picture's words name (AGL-3856): the people at a moment, and
2056
2155
  * where — "Ridgetop Elopement" a couple in the mountains, "Mountain Wedding
2057
2156
  * Day" a wedding couple in the mountains, "Family Session in the Fall" a
2058
2157
  * family in autumn — read from every word, never only the first clause.
2059
2158
  * `null` where it names no people.
2060
- */ export function aiStockScene(text) {
2159
+ */ export function aiStockScene(text, siteSetting = '') {
2061
2160
  const named = sceneWords(text);
2062
2161
  const scene = PEOPLE_SCENES.find((entry)=>entry.words.some((word)=>named.has(word)));
2063
2162
  if (!scene) return null;
@@ -2080,7 +2179,9 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2080
2179
  shun: scene.shun.filter((word)=>!named.has(word)).map(aiStockStem),
2081
2180
  named: [
2082
2181
  ...new Set(stemList(text))
2083
- ]
2182
+ ],
2183
+ // Searched where its own words set it; never shown anywhere the site is not.
2184
+ away: aiStockSettingAway(setting || siteSetting)
2084
2185
  };
2085
2186
  }
2086
2187
  /** A text's lowercase words, letters only. */ const sceneWords = (text)=>new Set(text.toLowerCase().split(/[^a-zÀ-ɏ]+/).filter(Boolean));
@@ -2131,9 +2232,53 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2131
2232
  shun: scene.shun.filter((word)=>!all.has(word)).map(aiStockStem),
2132
2233
  named: [
2133
2234
  ...new Set(stemList(texts.join(' ')))
2134
- ]
2235
+ ],
2236
+ away: aiStockSettingAway(setting)
2135
2237
  };
2136
2238
  }
2239
+ /**
2240
+ * The work a craft whose pictures are people at a moment shows (AGL-3942):
2241
+ * the scenes its site's own words name, in the order they name them, each
2242
+ * set where the site is — a wedding photographer "for couples planning
2243
+ * mountain weddings, and families" shows a wedding couple in the mountains
2244
+ * and a family in the mountains. A family session still never shows a
2245
+ * bride, whatever else the site names. Empty where its words name no people.
2246
+ */ export function aiStockWorkScenes(text, setting = '') {
2247
+ const list = text.toLowerCase().split(/[^a-zÀ-ɏ]+/).filter(Boolean);
2248
+ const found = PEOPLE_SCENES.map((scene)=>({
2249
+ scene,
2250
+ at: list.findIndex((word)=>scene.words.includes(word))
2251
+ })).filter((entry)=>entry.at >= 0).sort((a, b)=>a.at - b.at);
2252
+ return found.map(({ scene })=>{
2253
+ const named = new Set(list.filter((word)=>scene.words.includes(word)));
2254
+ const query = scene.query(named);
2255
+ return {
2256
+ queries: [
2257
+ ...new Set([
2258
+ setting ? `${query} ${setting}` : '',
2259
+ query
2260
+ ].filter(Boolean))
2261
+ ],
2262
+ subject: [
2263
+ scene.subject,
2264
+ setting
2265
+ ].filter(Boolean).join(' '),
2266
+ object: scene.subject,
2267
+ lead: scene.lead.filter((phrase)=>!RECORD_SCENE_UNPEOPLED.has(phrase)).map((phrase)=>stemList(phrase).join(' ')),
2268
+ people: scene.people.map(aiStockStem),
2269
+ shun: scene.shun.map(aiStockStem),
2270
+ named: [
2271
+ ...new Set([
2272
+ ...named
2273
+ ].map(aiStockStem))
2274
+ ],
2275
+ away: aiStockSettingAway(setting)
2276
+ };
2277
+ });
2278
+ }
2279
+ /** Whether a setting is a place a photo shows on its own, a landscape searched by its name: "mountains", never "autumn". */ export function aiStockSettingIsLandscape(setting) {
2280
+ return SCENE_SETTINGS.some((entry)=>entry.said === setting && entry.landscape === true);
2281
+ }
2137
2282
  /**
2138
2283
  * A scene's lead words a record's cover never leads with alone
2139
2284
  * ({@link aiStockRecordScene}): the moment with no one in it (a wedding's table
@@ -2373,7 +2518,7 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2373
2518
  * asks first for the role its words or its list's name (`sectionName`) say,
2374
2519
  * in the site's world: "teacher classroom" for a "Math instructor" (AGL-3856).
2375
2520
  */ export function aiStockSearchesFor(slot, terms, subject, part, sectionName = '') {
2376
- var _terms_core, _terms_making, _ref, _ref1, _terms_setting, _ref2, _terms_maker, _ref3;
2521
+ var _terms_core, _terms_making, _ref, _ref1, _terms_setting, _terms_setting1, _ref2, _terms_maker, _terms_maker1, _ref3;
2377
2522
  var _aiStockPeopleRole, _slot_record, _slot_record1;
2378
2523
  const orientation = aiStockOrientation(slot.aspect);
2379
2524
  const size = slot.role === 'hero' ? {
@@ -2389,6 +2534,21 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2389
2534
  // searched "photographer mountain elopements" and placed a press
2390
2535
  // photographer, a toy frog with a camera and a cat (AGL-3856).
2391
2536
  const work = aiStockShowsTheWork(slot, terms);
2537
+ // Every other picture of such a craft's site shows its work where a page
2538
+ // would show the maker (AGL-3942): the beta.267 Copper Hollow Film &
2539
+ // Portrait about page searched "photographer at work" and "photographer
2540
+ // camera", and every picture on it was a stock photographer.
2541
+ const itsWork = !work && aiStockShowsItsWork(slot, terms);
2542
+ /** Whether a query names the craft by its own name, the profession's noun: "wedding photographer". */ const namesCraft = (query)=>stemList(query).some((stem)=>{
2543
+ var _terms_core;
2544
+ return ((_terms_core = terms.core) != null ? _terms_core : []).includes(stem);
2545
+ });
2546
+ /** Whether a query names the maker, their tools or the craft: "photographer at work", "photographer camera". */ const namesMaker = (query)=>namesCraft(query) || stemList(query).some((stem)=>{
2547
+ var _terms_maker;
2548
+ return ((_terms_maker = terms.maker) != null ? _terms_maker : []).includes(stem);
2549
+ });
2550
+ /** A rung of the maker's own photos, left out on a site that shows its work. */ const shown = (query)=>!itsWork || !namesMaker(query);
2551
+ /** A search of the slot's own words, left out on such a site only where they name the profession. */ const said = (query)=>!itsWork || !namesCraft(query);
2392
2552
  const object = aiStockObjectWord(subject);
2393
2553
  const subjectStems = stemList(subject);
2394
2554
  const kin = object ? objectKin(object, [
@@ -2409,13 +2569,13 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2409
2569
  // own object among the first tags, and searched by its object's kin, never
2410
2570
  // the category's world alone (AGL-3660).
2411
2571
  const collection = part === 'collection' && Boolean(terms.craft) && !work;
2412
- const broad = kin && collection || work ? [] : terms.broad.map((query)=>({
2572
+ const broad = kin && collection || work ? [] : terms.broad.filter(shown).map((query)=>({
2413
2573
  query,
2414
2574
  broad: true,
2415
2575
  category: collection
2416
2576
  }));
2417
2577
  // The maker at work, held to the craft: never "hands" (AGL-3660).
2418
- const making = (work ? [] : (_terms_making = terms.making) != null ? _terms_making : []).map((query)=>({
2578
+ const makers = (work ? [] : (_terms_making = terms.making) != null ? _terms_making : []).filter(shown).map((query)=>({
2419
2579
  query,
2420
2580
  broad: true,
2421
2581
  category: true
@@ -2424,10 +2584,18 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2424
2584
  const roleWord = slot.people ? (_ref = (_aiStockPeopleRole = aiStockPeopleRole(slot.alt, sectionName)) == null ? void 0 : _aiStockPeopleRole.query) != null ? _ref : '' : '';
2425
2585
  const role = roleWord ? stemList(terms.business).includes(roleWord) ? terms.business : `${roleWord} ${terms.business}`.trim() : '';
2426
2586
  // A scene's people are read from every word of the picture's, never its first clause alone.
2427
- const scene = work ? aiStockRecordScene(slot.alt, (_ref1 = (_slot_record = slot.record) == null ? void 0 : _slot_record.category) != null ? _ref1 : '', (_terms_setting = terms.setting) != null ? _terms_setting : '') : terms.scenes && slot.role !== 'hero' && part !== 'collection' ? aiStockScene(slot.alt) : null;
2587
+ const scene = work ? aiStockRecordScene(slot.alt, (_ref1 = (_slot_record = slot.record) == null ? void 0 : _slot_record.category) != null ? _ref1 : '', (_terms_setting = terms.setting) != null ? _terms_setting : '') : terms.scenes && slot.role !== 'hero' && part !== 'collection' ? aiStockScene(slot.alt, (_terms_setting1 = terms.setting) != null ? _terms_setting1 : '') : null;
2588
+ // The site's own scenes, turned per slot so a page's pictures show each of them.
2589
+ const sceneList = [];
2590
+ if (itsWork) {
2591
+ var _terms_work;
2592
+ const own = (_terms_work = terms.work) != null ? _terms_work : [];
2593
+ const turn = own.length ? aiLayoutSeedNumber(`${sectionName}|${slot.alt}`) % own.length : 0;
2594
+ sceneList.push(...own.slice(turn), ...own.slice(0, turn));
2595
+ }
2428
2596
  // The work's own subject is never qualified by the craft: "getting ready", never "photographer getting ready".
2429
2597
  const own = work ? (phrase)=>phrase : qualify;
2430
- const thing = subject ? [
2598
+ const thing = (subject ? [
2431
2599
  {
2432
2600
  query: own(subject),
2433
2601
  held: true
@@ -2441,7 +2609,7 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2441
2609
  held: true
2442
2610
  }
2443
2611
  ]
2444
- ] : [];
2612
+ ] : []).filter((query)=>said(query.query));
2445
2613
  // Never the maker, the camera, an animal or a toy, unless the record names it.
2446
2614
  const recordNamed = work ? new Set(stemList(`${slot.alt} ${(_ref2 = (_slot_record1 = slot.record) == null ? void 0 : _slot_record1.category) != null ? _ref2 : ''}`)) : null;
2447
2615
  const refuseLead = recordNamed ? [
@@ -2452,12 +2620,23 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2452
2620
  ].map(aiStockStem)
2453
2621
  ])
2454
2622
  ].filter((stem)=>!recordNamed.has(stem)) : [];
2623
+ // Where the maker's rungs were, a site that shows its work searches its work.
2624
+ const shows = {
2625
+ query: '',
2626
+ work: true
2627
+ };
2628
+ const making = itsWork ? [
2629
+ shows
2630
+ ] : makers;
2455
2631
  const queries = slot.role === 'hero' ? [
2456
2632
  {
2457
2633
  query: terms.business,
2458
2634
  broad: true
2459
2635
  },
2460
- ...broad
2636
+ ...broad,
2637
+ ...itsWork ? [
2638
+ shows
2639
+ ] : []
2461
2640
  ] : slot.role === 'about' ? [
2462
2641
  ...role ? [
2463
2642
  {
@@ -2467,50 +2646,109 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2467
2646
  }
2468
2647
  ] : [],
2469
2648
  ...part === 'story' ? making : [],
2470
- {
2471
- query: `${terms.business} ${subject}`,
2472
- broad: true,
2473
- people: true
2474
- },
2475
- {
2476
- query: terms.business,
2477
- broad: true,
2478
- people: true
2479
- },
2649
+ ...[
2650
+ {
2651
+ query: `${terms.business} ${subject}`,
2652
+ broad: true,
2653
+ people: true
2654
+ },
2655
+ {
2656
+ query: terms.business,
2657
+ broad: true,
2658
+ people: true
2659
+ }
2660
+ ].filter((query)=>said(query.query)),
2480
2661
  ...part === 'story' ? [] : making,
2481
2662
  ...broad
2482
2663
  ] : [
2483
2664
  ...part === 'story' ? making : [],
2484
2665
  ...scene ? [] : thing,
2666
+ ...itsWork ? [
2667
+ shows
2668
+ ] : [],
2485
2669
  ...broad
2486
2670
  ];
2487
2671
  const seen = new Set();
2488
2672
  const searches = [];
2489
2673
  // A picture of people at a moment asks for them, and where, before anything
2490
2674
  // else, and takes only a photo leading with them (AGL-3856): never the
2491
- // roses of a "Mountain Wedding Day", nor a bride for a family session.
2492
- for (const query of (_ref3 = scene == null ? void 0 : scene.queries) != null ? _ref3 : []){
2493
- seen.add(`${query}|false`);
2494
- searches.push(_extends({
2675
+ // roses of a "Mountain Wedding Day", nor a bride for a family session. A
2676
+ // picture of the work never leads with the maker or a camera (AGL-3942).
2677
+ const workLead = itsWork ? (_terms_maker1 = terms.maker) != null ? _terms_maker1 : [] : [];
2678
+ // The site's work is held to its own scene, never to the craft's world.
2679
+ const sceneSearch = (one, query, ofWork)=>{
2680
+ const refused = [
2681
+ ...new Set([
2682
+ ...refuseLead,
2683
+ ...workLead.filter((stem)=>!one.named.includes(stem))
2684
+ ])
2685
+ ];
2686
+ return _extends({
2495
2687
  query,
2496
2688
  orientation
2497
2689
  }, size, {
2498
2690
  strict: true,
2499
- subject: scene.subject,
2500
- object: scene.object,
2501
- kin: scene.lead,
2691
+ subject: one.subject,
2692
+ object: one.object,
2693
+ kin: one.lead,
2502
2694
  lead: true,
2503
- needs: scene.people,
2504
- allow: scene.named
2505
- }, scene.shun.length ? {
2506
- refuse: scene.shun
2507
- } : {}, refuseLead.length ? {
2508
- refuseLead
2509
- } : {}, work ? {
2695
+ needs: one.people,
2696
+ allow: one.named
2697
+ }, one.shun.length ? {
2698
+ refuse: one.shun
2699
+ } : {}, refused.length ? {
2700
+ refuseLead: refused
2701
+ } : {}, one.away.length ? {
2702
+ away: one.away
2703
+ } : {}, work || ofWork ? {
2510
2704
  worldless: true
2511
- } : {}));
2705
+ } : {});
2706
+ };
2707
+ for (const query of (_ref3 = scene == null ? void 0 : scene.queries) != null ? _ref3 : []){
2708
+ seen.add(`${query}|false`);
2709
+ searches.push(sceneSearch(scene, query, false));
2512
2710
  }
2711
+ /** The site's own scenes, then the place itself where it is a landscape: "mountains". */ const workSearches = ()=>{
2712
+ var _ref;
2713
+ var _SCENE_SETTINGS_find;
2714
+ const found = [];
2715
+ for (const one of sceneList)for (const query of one.queries){
2716
+ if (seen.has(`${query}|false`)) continue;
2717
+ seen.add(`${query}|false`);
2718
+ found.push(sceneSearch(one, query, true));
2719
+ }
2720
+ const place = terms.setting && aiStockSettingIsLandscape(terms.setting) ? terms.setting : '';
2721
+ if (!place || seen.has(`${place}|false`)) return found;
2722
+ seen.add(`${place}|false`);
2723
+ const away = aiStockSettingAway(place);
2724
+ const stems = (_ref = (_SCENE_SETTINGS_find = SCENE_SETTINGS.find((entry)=>entry.said === place)) == null ? void 0 : _SCENE_SETTINGS_find.words.map(aiStockStem)) != null ? _ref : [];
2725
+ found.push(_extends({
2726
+ query: place,
2727
+ orientation
2728
+ }, size, {
2729
+ strict: true,
2730
+ subject: place,
2731
+ object: aiStockStem(place),
2732
+ kin: [
2733
+ ...new Set(stems)
2734
+ ],
2735
+ lead: true
2736
+ }, workLead.length ? {
2737
+ refuseLead: [
2738
+ ...workLead
2739
+ ]
2740
+ } : {}, away.length ? {
2741
+ away
2742
+ } : {}, {
2743
+ worldless: true
2744
+ }));
2745
+ return found;
2746
+ };
2513
2747
  for (const raw of queries){
2748
+ if (raw.work) {
2749
+ searches.push(...workSearches());
2750
+ continue;
2751
+ }
2514
2752
  const query = clip(raw.query);
2515
2753
  const key = `${query}|${raw.people === true}`;
2516
2754
  if (!query || seen.has(key)) continue;
@@ -2546,6 +2784,17 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2546
2784
  }
2547
2785
  return searches;
2548
2786
  }
2787
+ /**
2788
+ * Whether a picture of a craft's site whose pictures are people at a moment
2789
+ * shows that work wherever a page would show the maker (AGL-3942): an about
2790
+ * picture, a story of the craft, an intro's cover. Not a record (which shows
2791
+ * its own moment, {@link aiStockShowsTheWork}), a portrait of the business's
2792
+ * people, nor a product; and only where the site's words name the work.
2793
+ */ export function aiStockShowsItsWork(slot, terms) {
2794
+ var _ref;
2795
+ var _terms_work;
2796
+ return Boolean(terms.scenes) && ((_ref = (_terms_work = terms.work) == null ? void 0 : _terms_work.length) != null ? _ref : 0) > 0 && !slot.record && !slot.people && !slot.product;
2797
+ }
2549
2798
  /**
2550
2799
  * Whether a slot is a portfolio's record of a craft whose work is people at a
2551
2800
  * moment (AGL-3856): a photographer's "Mountain Elopements" shows an
@@ -2628,7 +2877,7 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2628
2877
  * site's world 1, plus a quarter when it is among the first tags.
2629
2878
  */ export function aiStockRelevance(photo, judgement) {
2630
2879
  var _judgement_rivals, _judgement_subject, _judgement_named, _judgement_shun, _judgement_refuse, _judgement_refuseLead, _judgement_needs, _judgement_shunLead, _judgement_leadAny, _judgement_kin, _judgement_requires, _judgement_category, _judgement_object, _judgement_domain, _judgement_roles;
2631
- var _judgement_roleScene, _judgement_roleRefuse;
2880
+ var _judgement_roleScene, _judgement_roleRefuse, _judgement_away;
2632
2881
  const hit = hitWords(photo);
2633
2882
  if (hit.rejected) return 0;
2634
2883
  if (aiStockSensitive(photo, judgement.named)) return 0;
@@ -2656,6 +2905,12 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2656
2905
  const read = new Set(stemList(((_photo_tags1 = photo.tags) != null ? _photo_tags1 : []).slice(0, ALT_TAGS_READ).join(' ')));
2657
2906
  if (((_judgement_roleRefuse1 = judgement.roleRefuse) != null ? _judgement_roleRefuse1 : []).some((stem)=>read.has(stem))) return 0;
2658
2907
  }
2908
+ // A Blue Ridge couple is never on a beach, however its later tags name a mountain (AGL-3942).
2909
+ if ((_judgement_away = judgement.away) == null ? void 0 : _judgement_away.length) {
2910
+ var _photo_tags2;
2911
+ const read = new Set(stemList(((_photo_tags2 = photo.tags) != null ? _photo_tags2 : []).slice(0, ALT_TAGS_READ).join(' ')));
2912
+ if (judgement.away.some((stem)=>read.has(stem))) return 0;
2913
+ }
2659
2914
  // A wedding day shows its people: a close-up of roses tagged "wedding" never fills it.
2660
2915
  const needs = (_judgement_needs = judgement.needs) != null ? _judgement_needs : [];
2661
2916
  if (needs.length && !needs.some((word)=>hit.all.has(word))) return 0;
@@ -2854,7 +3109,9 @@ function jobPhotos(key) {
2854
3109
  * Words a tag uses for how a photo looks or feels, never a thing it shows
2855
3110
  * (AGL-3856): "extravagant" of "wedding dress, bride, extravagant", "moody"
2856
3111
  * of "forest, human, photographer", a season or a people's name.
2857
- */ const DESCRIBING = words('love romance romantic luxury luxurious fancy extravagant happiness happy beauty emotion emotions mood lifestyle ' + 'concept aesthetic wallpaper background retro vintage antique old young cute sexy fun funny sad positive negative healthy fit ' + 'strong powerful homemade tasty yummy sweet sweetness nice dark bw monochrome macro sporty active married bridal ' + 'female male adult good bad poc bipoc moody sunny rainy foggy misty snowy cloudy stormy dreamy shiny trendy messy ' + 'casual blonde blond brunette muscular athletic clever intelligent concentrated curious lonely sleepy heavy ' + 'nature outdoors outdoor indoors indoor scenery scenic autumn fall winter spring summer ' + 'physical teenage traveling ' + // Adjectives a tag gives alone, never a thing: "students, multicultural" (AGL-3856).
3112
+ */ const DESCRIBING = words('love romance romantic luxury luxurious fancy extravagant happiness happy beauty emotion emotions mood lifestyle ' + 'concept aesthetic wallpaper background retro vintage antique old young cute sexy fun funny sad positive negative healthy fit ' + 'strong powerful homemade tasty yummy sweet sweetness nice dark bw monochrome macro sporty active married bridal ' + 'female male adult good bad poc bipoc moody sunny rainy foggy misty snowy cloudy stormy dreamy shiny trendy messy ' + 'casual blonde blond brunette muscular athletic clever intelligent concentrated curious lonely sleepy heavy ' + 'nature outdoors outdoor indoors indoor scenery scenic autumn fall winter spring summer ' + 'physical teenage traveling ' + // How a photo is laid out, never a thing it shows: "car wallpapers" is a car (AGL-3942).
3113
+ 'wallpaper wallpapers background backgrounds backdrop backdrops texture textures ' + // Words a tag gives that are no noun at all: "family, together" is a family (AGL-3942).
3114
+ 'together alone apart away abroad ahead nearby outside inside upstairs downstairs ' + // Adjectives a tag gives alone, never a thing: "students, multicultural" (AGL-3856).
2858
3115
  'multicultural multiethnic multiracial diverse international cultural traditional modern classic classical ' + 'domestic wild tropical rural urban ' + // How the photo was made, never what it shows: "cutout, perspective", "men, silhouettes".
2859
3116
  'silhouette silhouettes cutout perspective bokeh closeup ' + // A holiday names when, never a thing: "tet market" is a market.
2860
3117
  'tet christmas easter halloween diwali ramadan hanukkah thanksgiving ' + 'indian asian african american european caucasian vietnamese japanese chinese korean thai latino latina hispanic ' + 'japan vietnam viet nam thailand india china korea germany france italy spain england britain uk usa america ' + 'canada mexico brazil australia africa europe asia');
@@ -2871,7 +3128,7 @@ function jobPhotos(key) {
2871
3128
  * doing ("read", "lift", "fitness", "a workout"), a word for the photo itself
2872
3129
  * ("photo", "spot"). Left out, never said with an article: "A bookwork and a
2873
3130
  * read", "A kettlebell and fitness", "A board and training" said nothing.
2874
- */ const ABSTRACT_NOUNS = words('law justice advocacy business feedback review news return communication communicate discussion dialog conversation ' + 'success career work job satisfaction quality concept idea strategy marketing education technology health support ' + 'confirmation opinion rating evaluation planning project presentation optimization information internet finance ' + 'management leadership teamwork motivation growth strength service services help advice consulting consultation ' + 'marriage photography architecture travel transport transportation industry company corporate social e-business ' + 'fitness workout workouts exercise exercises crossfit sport sports skill skills knowledge exam exams lecture ' + 'session lesson course hobby leisure power energy wellness talent formation participation ' + 'press show day days night year years time holiday month months week weeks morning evening afternoon ' + 'kiss hug embrace ' + 'photo photos picture pictures image images shot shots shoot shoots spot spots ' + 'read write learn study lift move remove focus relax think sit smile laugh eat swim teach enjoy celebrate explore ' + 'hike can');
3131
+ */ const ABSTRACT_NOUNS = words('law justice advocacy business feedback review news return communication communicate discussion dialog conversation ' + 'success career work job satisfaction quality concept idea strategy marketing education technology health support ' + 'confirmation opinion rating evaluation planning project presentation optimization information internet finance ' + 'management leadership teamwork motivation growth strength service services help advice consulting consultation ' + 'marriage photography architecture travel transport transportation industry company corporate social e-business ' + 'fitness workout workouts exercise exercises crossfit sport sports skill skills knowledge exam exams lecture ' + 'session lesson course hobby leisure power energy wellness talent formation participation ' + 'press show day days night year years time holiday month months week weeks morning evening afternoon season seasons ' + 'kiss hug embrace ' + 'photo photos picture pictures image images shot shots shoot shoots spot spots ' + 'read write learn study lift move remove focus relax think sit smile laugh eat swim teach enjoy celebrate explore ' + 'hike can');
2875
3132
  /** Words ending as an idea does that are things: "a station", "a city", "a ship". */ const CONCRETE_ABSTRACT_ENDINGS = words('station mansion lotion potion ornament instrument apartment monument pavement basement garment compartment ' + 'equipment cement city university facility ship warship spaceship airship');
2876
3133
  /** Whether a word is an idea or a doing by how it ends: "participation", "unity", "empowerment". */ const saysAnIdea = (word)=>/(?:tion|sion|ment|ness|ity|ship|ism)s?$/.test(word) && !CONCRETE_ABSTRACT_ENDINGS.has(aiStockStem(word));
2877
3134
  /**
@@ -2983,6 +3240,8 @@ function jobPhotos(key) {
2983
3240
  if (/^(?:out|up|off|down|away|on|in)$/.test(list[list.length - 1])) return null;
2984
3241
  if (list.some((word)=>BRANDS.has(word))) return null;
2985
3242
  if (list.every((word)=>proper.has(word))) return list.map((word)=>`${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' ');
3243
+ // Two people named side by side are both shown: "bride groom" is a bride and groom (AGL-3942).
3244
+ if (list.length === 2 && list.every((word)=>PEOPLE_NOUNS.has(word)) && list[0] !== list[1]) return `${articleFor(list[0])} ${list[0]} and ${list[1]}`;
2986
3245
  const last = list[list.length - 1];
2987
3246
  // An idea or a doing, never a thing: "law", "feedback", "customer satisfaction", "participation", "weightlifting".
2988
3247
  if (ABSTRACT_NOUNS.has(last) || ABSTRACT_NOUNS.has(aiStockStem(last)) || saysAnIdea(last)) return null;
@@ -2990,10 +3249,18 @@ function jobPhotos(key) {
2990
3249
  if (doing) return null;
2991
3250
  const phrase = list.join(' ');
2992
3251
  const plural = PLURAL_NOUNS.has(last) || aiStockStem(last) !== last && !/^(?:lens|canvas|atlas|gas|bus)$/.test(last);
2993
- if (plural || MASS_NOUNS.has(last) || STOPWORDS.has(list[0]) && !SAID_PEOPLE.has(list[0])) return phrase;
2994
- return `${articleFor(phrase)} ${phrase}`;
3252
+ if (plural || MASS_NOUNS.has(last) || STOPWORDS.has(list[0]) && !SAID_PEOPLE.has(list[0])) return spelled(phrase);
3253
+ return `${articleFor(phrase)} ${spelled(phrase)}`;
2995
3254
  }
2996
- /** "a" or "an" before a phrase. */ const articleFor = (phrase)=>/^(?:[aeio]|u(?!ni|s[eu]))/.test(phrase) ? 'an' : 'a';
3255
+ /** Letters said one by one, written in capitals: "an SLR camera", "a DSLR" (AGL-3942). */ const INITIALISMS = words('slr dslr tv dj diy bbq suv atv uv vr');
3256
+ /** A phrase as a description writes it, its initialisms in capitals. */ const spelled = (phrase)=>phrase.split(' ').map((word)=>INITIALISMS.has(word) ? word.toUpperCase() : word).join(' ');
3257
+ /** "a" or "an" before a phrase, an initialism by how its first letter is said: "an SLR", "a DSLR". */ const articleFor = (phrase)=>{
3258
+ var _phrase_split_;
3259
+ const first = (_phrase_split_ = phrase.split(' ')[0]) != null ? _phrase_split_ : '';
3260
+ if (INITIALISMS.has(first)) return /^[aefhilmnorsx]/.test(first) ? 'an' : 'a';
3261
+ return /^(?:[aeio]|u(?!ni|s[eu]))/.test(phrase) ? 'an' : 'a';
3262
+ };
3263
+ /** Words a description never ends on: an article, a preposition, a conjunction. */ const DANGLING_END = /(?:\s+(?:a|an|the|and|or|with|in|on|at|by|of|for|to|from|into|onto))+$/i;
2997
3264
  /** Whether two words name one thing, one inside the other: "law" and "lawyer", "cam" and "camera". */ function nearSynonyms(a, b) {
2998
3265
  const [short, long] = a.length <= b.length ? [
2999
3266
  a,
@@ -3128,7 +3395,9 @@ function jobPhotos(key) {
3128
3395
  const where = (_PLACES_SAID_place_head = PLACES_SAID[place.head]) != null ? _PLACES_SAID_place_head : BARE_PLACES.has(place.head) && /^an? \S+$/.test(place.said) ? place.head : place.said;
3129
3396
  phrase = phrase ? `${phrase} ${preposition} ${where}` : where;
3130
3397
  }
3131
- if (!phrase) return null;
3398
+ // Never a dangling article or preposition, whatever a tag left behind.
3399
+ phrase = phrase.replace(DANGLING_END, '').trim();
3400
+ if (!phrase || /^(?:a|an|the)$/i.test(phrase)) return null;
3132
3401
  const out = `${phrase.charAt(0).toUpperCase()}${phrase.slice(1)}`;
3133
3402
  return out.length > AI_STOCK_ALT_MAX ? out.slice(0, AI_STOCK_ALT_MAX).replace(/[\s,]+\S*$/, '') : out;
3134
3403
  }
@@ -3260,13 +3529,18 @@ const EXTENSIONS = {
3260
3529
  * post — so no slot searches by words another slot is held against. A site for
3261
3530
  * adults alone ({@link aiStockAdultsOnly}) is told so, whatever its world.
3262
3531
  */ export function aiStockSourceTerms(input) {
3263
- var _input_audience;
3532
+ var _input_audience, _input_audience1;
3264
3533
  const terms = sourceWorldTerms(input);
3265
3534
  // Where the site's own words set it: a photographer "in the Blue Ridge" for "mountain weddings" (AGL-3856).
3266
3535
  const setting = aiStockSetting([
3267
3536
  input.business,
3268
3537
  (_input_audience = input.audience) != null ? _input_audience : ''
3269
3538
  ].join(' . '));
3539
+ // A craft whose pictures are people at a moment shows the moments its site names (AGL-3942).
3540
+ const work = terms.scenes ? aiStockWorkScenes([
3541
+ input.business,
3542
+ (_input_audience1 = input.audience) != null ? _input_audience1 : ''
3543
+ ].join(' . '), setting) : [];
3270
3544
  return _extends({}, terms, aiStockAdultsOnly([
3271
3545
  input.audience,
3272
3546
  input.name,
@@ -3275,6 +3549,8 @@ const EXTENSIONS = {
3275
3549
  adults: true
3276
3550
  } : {}, setting ? {
3277
3551
  setting
3552
+ } : {}, work.length ? {
3553
+ work
3278
3554
  } : {});
3279
3555
  }
3280
3556
  /** What a source's photos are searched by: its world, as {@link aiStockSourceTerms} reads it. */ function sourceWorldTerms(input) {
@@ -4484,6 +4760,7 @@ const WORKSPACE = {
4484
4760
  delete request.allow;
4485
4761
  delete request.refuseLead;
4486
4762
  delete request.worldless;
4763
+ delete request.away;
4487
4764
  const key = JSON.stringify([
4488
4765
  request.query,
4489
4766
  request.orientation,
@@ -4653,7 +4930,7 @@ const WORKSPACE = {
4653
4930
  * (AGL-3923). What lets a sensitive topic in is always the site's own words.
4654
4931
  */ const judgementOf = (slot, request, subject, world)=>{
4655
4932
  var _request_subject, _request_shun;
4656
- var _request_allow, _request_shun1, _request_leadAny, _request_refuse, _request_needs, _request_refuseLead;
4933
+ var _request_allow, _request_shun1, _request_leadAny, _request_refuse, _request_needs, _request_refuseLead, _request_away;
4657
4934
  return _extends({
4658
4935
  subject: (_request_subject = request.subject) != null ? _request_subject : request.broad ? '' : subject,
4659
4936
  domain: world.domain,
@@ -4698,6 +4975,8 @@ const WORKSPACE = {
4698
4975
  needs: request.needs
4699
4976
  } : {}, ((_request_refuseLead = request.refuseLead) == null ? void 0 : _request_refuseLead.length) ? {
4700
4977
  refuseLead: request.refuseLead
4978
+ } : {}, ((_request_away = request.away) == null ? void 0 : _request_away.length) ? {
4979
+ away: request.away
4701
4980
  } : {}, request.worldless ? {
4702
4981
  domain: [],
4703
4982
  rivals: []
@@ -4708,8 +4987,9 @@ const WORKSPACE = {
4708
4987
  * filled from (AGL-3660): its broad searches, any hit naming the craft
4709
4988
  * itself. Never a desk, a laptop or a living room for a soap shop.
4710
4989
  */ const craftSearches = (slot, world = terms)=>{
4711
- // A portfolio's record of the work is never filled by the craft's own photos (AGL-3856).
4712
- if (!world.core.length || aiStockShowsTheWork(slot, world)) return [];
4990
+ // A portfolio's record of the work is never filled by the craft's own
4991
+ // photos (AGL-3856), nor is any picture of a site that shows its work (AGL-3942).
4992
+ if (!world.core.length || aiStockShowsTheWork(slot, world) || aiStockShowsItsWork(slot, world)) return [];
4713
4993
  const orientation = aiStockOrientation(slot.aspect);
4714
4994
  const size = slot.role === 'hero' ? {
4715
4995
  minWidth: 1600