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

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 (60) hide show
  1. package/package.json +18 -18
  2. package/src/lib/components/ai-job-site.d.ts +8 -1
  3. package/src/lib/components/ai-job-site.js +7 -0
  4. package/src/lib/components/ai-job-site.js.map +1 -1
  5. package/src/lib/components/ai-site-build-page.component.js +37 -0
  6. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +25 -6
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/components/ai-site-start-steps.component.d.ts +8 -2
  10. package/src/lib/components/ai-site-start-steps.component.js +55 -26
  11. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  12. package/src/lib/components/ai-site-start-tracking.d.ts +11 -3
  13. package/src/lib/components/ai-site-start-tracking.js +28 -9
  14. package/src/lib/components/ai-site-start-tracking.js.map +1 -1
  15. package/src/lib/components/assist-jobs-drawer.component.js +24 -2
  16. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  17. package/src/lib/jobs/ai-empty-site-sweep.d.ts +154 -0
  18. package/src/lib/jobs/ai-empty-site-sweep.js +346 -0
  19. package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -0
  20. package/src/lib/jobs/ai-job-page-language.d.ts +7 -1
  21. package/src/lib/jobs/ai-job-page-language.js +16 -3
  22. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  23. package/src/lib/jobs/ai-job-site-look.d.ts +2 -1
  24. package/src/lib/jobs/ai-job-site-look.js +6 -2
  25. package/src/lib/jobs/ai-job-site-look.js.map +1 -1
  26. package/src/lib/jobs/ai-job-site-step.js +11 -6
  27. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  28. package/src/lib/jobs/ai-jobs-beat.d.ts +11 -3
  29. package/src/lib/jobs/ai-jobs-beat.js +23 -6
  30. package/src/lib/jobs/ai-jobs-beat.js.map +1 -1
  31. package/src/lib/jobs/ai-jobs.js +46 -7
  32. package/src/lib/jobs/ai-jobs.js.map +1 -1
  33. package/src/lib/jobs/ai-layout-stock-photos.d.ts +43 -0
  34. package/src/lib/jobs/ai-layout-stock-photos.js +735 -22
  35. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  36. package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.d.ts +35 -0
  37. package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.js +379 -0
  38. package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.js.map +1 -0
  39. package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.d.ts +35 -0
  40. package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.js +294 -0
  41. package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.js.map +1 -0
  42. package/src/lib/jobs/fixtures/ai-stock-photo-mexicano-recordings.d.ts +31 -0
  43. package/src/lib/jobs/fixtures/ai-stock-photo-mexicano-recordings.js +325 -0
  44. package/src/lib/jobs/fixtures/ai-stock-photo-mexicano-recordings.js.map +1 -0
  45. package/src/lib/layout-language/ai-layout-design.js +5 -0
  46. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  47. package/src/lib/layout-language/ai-layout-motion.d.ts +60 -7
  48. package/src/lib/layout-language/ai-layout-motion.js +231 -4
  49. package/src/lib/layout-language/ai-layout-motion.js.map +1 -1
  50. package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
  51. package/src/lib/layout-language/ai-layout-pictures.js +68 -4
  52. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  53. package/src/lib/model/ai-jobs.types.d.ts +20 -0
  54. package/src/lib/model/ai-jobs.types.js.map +1 -1
  55. package/src/lib/model/ai-site-look.d.ts +10 -1
  56. package/src/lib/model/ai-site-look.js +14 -3
  57. package/src/lib/model/ai-site-look.js.map +1 -1
  58. package/src/lib/model/ai-site-start-handoff.d.ts +49 -0
  59. package/src/lib/model/ai-site-start-handoff.js +88 -0
  60. package/src/lib/model/ai-site-start-handoff.js.map +1 -0
@@ -19,7 +19,7 @@ import { STOCK_PHOTO_QUERY_MAX_CHARS, stockPhotoProvider, stockPhotoSourceKey }
19
19
  import { AI_LAYOUT_NO_PHOTO, aiLayoutSeedNumber } from "../layout-language/ai-layout-pictures.js";
20
20
  import { aiStorefrontSays } from "../layout-language/ai-layout-storefront.js";
21
21
  import { aiLayoutLabel, aiLayoutSpeaksOtherThanEnglish } from "../layout-language/ai-layout-labels.js";
22
- import { aiSiteKindOfInputs } from "../model/ai-site-kinds.js";
22
+ import { aiSiteKind, aiSiteKindOfInputs } from "../model/ai-site-kinds.js";
23
23
  import { aiStockBusinessNoun } from "./ai-stock-business-nouns.js";
24
24
  /**
25
25
  * STOCK PHOTOS FOR A LANGUAGE PAGE'S PICTURE SLOTS (AGL-3660).
@@ -157,6 +157,18 @@ import { aiStockBusinessNoun } from "./ai-stock-business-nouns.js";
157
157
  /** The accepted hits a slot keeps in hand, in case a copy fails. */ const AI_LAYOUT_STOCK_TRIES_PER_SLOT = 3;
158
158
  /** Copies made side by side. */ const AI_LAYOUT_STOCK_COPIES_AT_ONCE = 4;
159
159
  /** The largest photo copied: inside one direct upload. */ export const AI_LAYOUT_STOCK_MAX_BYTES = 3 * 1024 * 1024;
160
+ /**
161
+ * The media library's refusals that are about ONE photo's own bytes, as the
162
+ * console's `ingestMedia` returns them (AGL-3923): 415 for a type or contents
163
+ * it does not accept, 413 for a photo too large to store, 451 for a file
164
+ * held in quarantine. The slot then tries its next hit, and the page goes on
165
+ * storing. Every other refusal (403 from the storage band, the site's scope
166
+ * or a lockdown; 500) holds for the whole page.
167
+ */ export const AI_STOCK_PHOTO_OWN_REFUSALS = new Set([
168
+ 413,
169
+ 415,
170
+ 451
171
+ ]);
160
172
  /**
161
173
  * The unit job input naming the screen drafts the job's other pages were
162
174
  * written to, whose photos a page does not place again (AGL-3660).
@@ -1529,8 +1541,14 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
1529
1541
  ]
1530
1542
  });
1531
1543
  }
1544
+ /**
1545
+ * Words that say a picture is one of a set, never what it shows (AGL-3923):
1546
+ * the beta.266 Mexicano portfolio's gallery was "Project one" to "Project
1547
+ * four", and searched "project" (students at a whiteboard), "project four"
1548
+ * (lavender) and "four" (a running track's lane numbers).
1549
+ */ const PLACEHOLDER_SUBJECTS = words('project projects piece pieces item items example examples sample samples entry entries untitled work works ' + 'selected recent latest ' + 'four five six seven eight nine ten first second third fourth fifth sixth seventh eighth ninth tenth');
1532
1550
  /** A phrase's content words without its filler; all filler, and nothing is left. */ function subjectOf(list) {
1533
- return list.filter((word)=>!FILLER.has(word));
1551
+ return list.filter((word)=>!FILLER.has(word) && !PLACEHOLDER_SUBJECTS.has(word));
1534
1552
  }
1535
1553
  /**
1536
1554
  * A picture's subject in a few words (AGL-3660): the noun phrase its alt
@@ -2812,6 +2830,7 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
2812
2830
  * later does not place what an earlier one did, while a pass repeated over
2813
2831
  * its own page keeps its own photos. Bounded; the oldest job goes first.
2814
2832
  */ const JOB_PHOTOS = new Map();
2833
+ /** What a job's photos map holds for a photo the media library refused for its own bytes: no page's scope (AGL-3923). */ const REFUSED_BY_THE_LIBRARY = '\u0000refused';
2815
2834
  const JOB_PHOTOS_MAX = 200;
2816
2835
  function jobPhotos(key) {
2817
2836
  if (!key) return null;
@@ -3425,6 +3444,16 @@ const EXTENSIONS = {
3425
3444
  broad: [
3426
3445
  'artist studio',
3427
3446
  'creative workspace'
3447
+ ],
3448
+ // "Artist" names a bee and a tattooist as often as a painter (AGL-3923):
3449
+ // the beta.266 Mexicano hero's first pick was "artist bee, giant artist bee".
3450
+ shunLead: [
3451
+ 'bee',
3452
+ 'insect',
3453
+ 'insects',
3454
+ 'tattoo',
3455
+ 'makeup',
3456
+ 'cosmetics'
3428
3457
  ]
3429
3458
  },
3430
3459
  studio: {
@@ -3776,7 +3805,7 @@ const EXTENSIONS = {
3776
3805
  }
3777
3806
  /** What a kind of site's pictures are searched and judged by ({@link KIND_SUBJECTS}); `null` for a kind not listed. */ export function aiStockKindTerms(kind) {
3778
3807
  var _entry_broad;
3779
- var _entry_kin;
3808
+ var _entry_kin, _entry_shunLead;
3780
3809
  const entry = KIND_SUBJECTS[kind];
3781
3810
  if (!entry) return null;
3782
3811
  const base = aiStockSiteTerms(entry.subject);
@@ -3799,7 +3828,562 @@ const EXTENSIONS = {
3799
3828
  ...kin
3800
3829
  ])
3801
3830
  ]
3802
- });
3831
+ }, ((_entry_shunLead = entry.shunLead) == null ? void 0 : _entry_shunLead.length) ? {
3832
+ shunLead: [
3833
+ ...new Set([
3834
+ ...base.shunLead,
3835
+ ...entry.shunLead.map(aiStockStem)
3836
+ ])
3837
+ ]
3838
+ } : {});
3839
+ }
3840
+ /**
3841
+ * The kind of site each starter is the shape of (`AiSiteKind.starter`): what
3842
+ * a picture is searched by, widened past the kind the person picked, where
3843
+ * nothing narrower found one (AGL-3923).
3844
+ */ const STARTER_KINDS = {
3845
+ business: 'business',
3846
+ 'physical-shop': 'store',
3847
+ portfolio: 'portfolio',
3848
+ landing: 'landing'
3849
+ };
3850
+ const OFFICE = {
3851
+ kin: [
3852
+ 'office',
3853
+ 'workspace',
3854
+ 'desk',
3855
+ 'laptop',
3856
+ 'computer',
3857
+ 'meeting',
3858
+ 'business',
3859
+ 'work',
3860
+ 'workplace',
3861
+ 'team',
3862
+ 'notebook',
3863
+ 'coffee',
3864
+ 'teamwork',
3865
+ 'conference'
3866
+ ],
3867
+ broad: [
3868
+ 'office workspace',
3869
+ 'business meeting',
3870
+ 'workspace desk laptop'
3871
+ ]
3872
+ };
3873
+ const WORKSPACE = {
3874
+ kin: [
3875
+ 'workspace',
3876
+ 'desk',
3877
+ 'laptop',
3878
+ 'notebook',
3879
+ 'coffee',
3880
+ 'office',
3881
+ 'book',
3882
+ 'books',
3883
+ 'workplace'
3884
+ ],
3885
+ broad: [
3886
+ 'workspace desk laptop',
3887
+ 'notebook coffee desk'
3888
+ ]
3889
+ };
3890
+ /**
3891
+ * The last searches a picture tries, for each kind of site the guided start
3892
+ * offers (AGL-3923): plain, common photos of where that kind's work happens,
3893
+ * which every library holds many of, held to their own words. Never a face
3894
+ * as the lead, never a child, never a product nor a rival business: a
3895
+ * restaurant's are its tables and plates, never an office. A kind with no set
3896
+ * keeps a slot nothing fits without a photo (AGL-3831).
3897
+ */ const KIND_GENERIC = {
3898
+ business: OFFICE,
3899
+ trades: {
3900
+ kin: [
3901
+ 'tools',
3902
+ 'tool',
3903
+ 'toolbox',
3904
+ 'workbench',
3905
+ 'workshop',
3906
+ 'construction',
3907
+ 'repair',
3908
+ 'renovation',
3909
+ 'drill',
3910
+ 'hammer',
3911
+ 'wrench',
3912
+ 'roof',
3913
+ 'house',
3914
+ 'carpentry'
3915
+ ],
3916
+ broad: [
3917
+ 'tools workbench',
3918
+ 'home renovation',
3919
+ 'construction tools'
3920
+ ]
3921
+ },
3922
+ professional: {
3923
+ kin: [
3924
+ 'office',
3925
+ 'desk',
3926
+ 'documents',
3927
+ 'document',
3928
+ 'meeting',
3929
+ 'conference',
3930
+ 'laptop',
3931
+ 'workplace',
3932
+ 'business',
3933
+ 'notebook',
3934
+ 'paperwork',
3935
+ 'consulting'
3936
+ ],
3937
+ broad: [
3938
+ 'office desk documents',
3939
+ 'business meeting',
3940
+ 'conference room'
3941
+ ]
3942
+ },
3943
+ wellness: {
3944
+ kin: [
3945
+ 'spa',
3946
+ 'massage',
3947
+ 'wellness',
3948
+ 'relaxation',
3949
+ 'relax',
3950
+ 'towel',
3951
+ 'towels',
3952
+ 'candle',
3953
+ 'stones',
3954
+ 'therapy',
3955
+ 'treatment',
3956
+ 'aromatherapy'
3957
+ ],
3958
+ broad: [
3959
+ 'spa stones towel',
3960
+ 'massage room',
3961
+ 'wellness spa'
3962
+ ]
3963
+ },
3964
+ restaurant: {
3965
+ kin: [
3966
+ 'restaurant',
3967
+ 'table',
3968
+ 'dining',
3969
+ 'food',
3970
+ 'plate',
3971
+ 'dish',
3972
+ 'meal',
3973
+ 'kitchen',
3974
+ 'cafe',
3975
+ 'dinner',
3976
+ 'lunch',
3977
+ 'cuisine'
3978
+ ],
3979
+ broad: [
3980
+ 'restaurant interior',
3981
+ 'restaurant table food',
3982
+ 'food plate'
3983
+ ]
3984
+ },
3985
+ store: {
3986
+ kin: [
3987
+ 'shop',
3988
+ 'store',
3989
+ 'retail',
3990
+ 'shelf',
3991
+ 'shelves',
3992
+ 'display',
3993
+ 'interior',
3994
+ 'boutique',
3995
+ 'storefront',
3996
+ 'counter'
3997
+ ],
3998
+ broad: [
3999
+ 'shop interior',
4000
+ 'retail store'
4001
+ ]
4002
+ },
4003
+ portfolio: {
4004
+ kin: [
4005
+ 'studio',
4006
+ 'workspace',
4007
+ 'desk',
4008
+ 'art',
4009
+ 'design',
4010
+ 'creative',
4011
+ 'sketch',
4012
+ 'drawing',
4013
+ 'paint',
4014
+ 'painting',
4015
+ 'brush',
4016
+ 'brushes',
4017
+ 'canvas',
4018
+ 'pencil',
4019
+ 'pencils',
4020
+ 'paper'
4021
+ ],
4022
+ broad: [
4023
+ 'art supplies',
4024
+ 'creative workspace',
4025
+ 'art studio'
4026
+ ]
4027
+ },
4028
+ studio: {
4029
+ kin: [
4030
+ 'design',
4031
+ 'studio',
4032
+ 'workspace',
4033
+ 'desk',
4034
+ 'laptop',
4035
+ 'sketch',
4036
+ 'creative',
4037
+ 'office',
4038
+ 'drawing',
4039
+ 'pencil'
4040
+ ],
4041
+ broad: [
4042
+ 'design studio workspace',
4043
+ 'creative workspace',
4044
+ 'workspace desk laptop'
4045
+ ]
4046
+ },
4047
+ photography: {
4048
+ kin: [
4049
+ 'camera',
4050
+ 'cameras',
4051
+ 'photography',
4052
+ 'lens',
4053
+ 'photographer',
4054
+ 'studio',
4055
+ 'film',
4056
+ 'tripod'
4057
+ ],
4058
+ broad: [
4059
+ 'camera lens',
4060
+ 'photography studio',
4061
+ 'vintage camera'
4062
+ ]
4063
+ },
4064
+ blog: {
4065
+ kin: [
4066
+ 'notebook',
4067
+ 'desk',
4068
+ 'writing',
4069
+ 'laptop',
4070
+ 'coffee',
4071
+ 'journal',
4072
+ 'book',
4073
+ 'books',
4074
+ 'workspace',
4075
+ 'pen',
4076
+ 'typewriter'
4077
+ ],
4078
+ broad: [
4079
+ 'writing desk notebook',
4080
+ 'laptop coffee desk'
4081
+ ]
4082
+ },
4083
+ events: {
4084
+ kin: [
4085
+ 'event',
4086
+ 'venue',
4087
+ 'wedding',
4088
+ 'reception',
4089
+ 'party',
4090
+ 'celebration',
4091
+ 'table',
4092
+ 'decoration',
4093
+ 'banquet',
4094
+ 'hall',
4095
+ 'flowers'
4096
+ ],
4097
+ broad: [
4098
+ 'event venue decoration',
4099
+ 'banquet table',
4100
+ 'party decoration'
4101
+ ]
4102
+ },
4103
+ fitness: {
4104
+ kin: [
4105
+ 'gym',
4106
+ 'fitness',
4107
+ 'weights',
4108
+ 'dumbbell',
4109
+ 'dumbbells',
4110
+ 'barbell',
4111
+ 'kettlebell',
4112
+ 'workout',
4113
+ 'exercise',
4114
+ 'training'
4115
+ ],
4116
+ broad: [
4117
+ 'gym equipment',
4118
+ 'dumbbells gym',
4119
+ 'fitness gym'
4120
+ ]
4121
+ },
4122
+ yoga: {
4123
+ kin: [
4124
+ 'yoga',
4125
+ 'mat',
4126
+ 'meditation',
4127
+ 'studio',
4128
+ 'relaxation',
4129
+ 'candle',
4130
+ 'zen',
4131
+ 'mindfulness'
4132
+ ],
4133
+ broad: [
4134
+ 'yoga mat',
4135
+ 'yoga studio',
4136
+ 'meditation candle'
4137
+ ]
4138
+ },
4139
+ beauty: {
4140
+ kin: [
4141
+ 'salon',
4142
+ 'beauty',
4143
+ 'hair',
4144
+ 'makeup',
4145
+ 'cosmetics',
4146
+ 'nails',
4147
+ 'manicure',
4148
+ 'spa',
4149
+ 'mirror',
4150
+ 'brush',
4151
+ 'brushes'
4152
+ ],
4153
+ broad: [
4154
+ 'beauty salon interior',
4155
+ 'makeup brushes cosmetics',
4156
+ 'hair salon chair'
4157
+ ]
4158
+ },
4159
+ realestate: {
4160
+ kin: [
4161
+ 'house',
4162
+ 'home',
4163
+ 'interior',
4164
+ 'living',
4165
+ 'room',
4166
+ 'property',
4167
+ 'architecture',
4168
+ 'apartment',
4169
+ 'kitchen',
4170
+ 'estate'
4171
+ ],
4172
+ broad: [
4173
+ 'house exterior',
4174
+ 'home interior living room',
4175
+ 'modern kitchen interior'
4176
+ ]
4177
+ },
4178
+ education: {
4179
+ kin: [
4180
+ 'classroom',
4181
+ 'books',
4182
+ 'book',
4183
+ 'library',
4184
+ 'school',
4185
+ 'desk',
4186
+ 'education',
4187
+ 'learning',
4188
+ 'study',
4189
+ 'studying',
4190
+ 'notebook',
4191
+ 'chalkboard',
4192
+ 'blackboard',
4193
+ 'lecture'
4194
+ ],
4195
+ broad: [
4196
+ 'library books',
4197
+ 'classroom',
4198
+ 'study desk books'
4199
+ ]
4200
+ },
4201
+ nonprofit: {
4202
+ kin: [
4203
+ 'volunteer',
4204
+ 'volunteers',
4205
+ 'volunteering',
4206
+ 'charity',
4207
+ 'donation',
4208
+ 'donations',
4209
+ 'community',
4210
+ 'helping',
4211
+ 'help',
4212
+ 'hands'
4213
+ ],
4214
+ broad: [
4215
+ 'volunteers charity',
4216
+ 'donation box',
4217
+ 'community volunteering'
4218
+ ]
4219
+ },
4220
+ music: {
4221
+ kin: [
4222
+ 'music',
4223
+ 'guitar',
4224
+ 'piano',
4225
+ 'instrument',
4226
+ 'instruments',
4227
+ 'concert',
4228
+ 'stage',
4229
+ 'microphone',
4230
+ 'drums',
4231
+ 'studio',
4232
+ 'vinyl'
4233
+ ],
4234
+ broad: [
4235
+ 'guitar instrument',
4236
+ 'concert stage lights',
4237
+ 'music studio microphone'
4238
+ ]
4239
+ },
4240
+ personal: WORKSPACE,
4241
+ landing: {
4242
+ kin: [
4243
+ 'office',
4244
+ 'workspace',
4245
+ 'desk',
4246
+ 'laptop',
4247
+ 'computer',
4248
+ 'meeting',
4249
+ 'business',
4250
+ 'work',
4251
+ 'workplace',
4252
+ 'team',
4253
+ 'technology',
4254
+ 'startup'
4255
+ ],
4256
+ broad: [
4257
+ 'modern office',
4258
+ 'workspace desk laptop',
4259
+ 'business meeting'
4260
+ ]
4261
+ },
4262
+ 'coming-soon': WORKSPACE
4263
+ };
4264
+ /** The kind of site whose common photos each starter falls back to. */ const STARTER_GENERIC_KIND = {
4265
+ business: 'business',
4266
+ 'physical-shop': 'store',
4267
+ portfolio: 'portfolio',
4268
+ landing: 'landing'
4269
+ };
4270
+ /** Stems no common photo leads with: a face, a portrait (AGL-3923). */ const FACE_STEMS = [
4271
+ 'face',
4272
+ 'portrait',
4273
+ 'selfie',
4274
+ 'headshot'
4275
+ ].map(aiStockStem);
4276
+ /** The starter a kind of site follows ({@link STARTER_KINDS}); a kind not listed follows the business starter. */ function starterOfKind(kind) {
4277
+ var _ref;
4278
+ var _aiSiteKind;
4279
+ const starter = (_ref = (_aiSiteKind = aiSiteKind(kind)) == null ? void 0 : _aiSiteKind.starter) != null ? _ref : 'business';
4280
+ return STARTER_KINDS[starter] ? starter : 'business';
4281
+ }
4282
+ /** A kind of site's common photos as a world to search and judge by ({@link KIND_GENERIC}); `null` for a kind with no set. */ export function aiStockKindGenericTerms(kind) {
4283
+ const set = KIND_GENERIC[kind];
4284
+ if (!set) return null;
4285
+ const kin = [
4286
+ ...new Set(set.kin.map(aiStockStem))
4287
+ ];
4288
+ return {
4289
+ business: '',
4290
+ craft: '',
4291
+ domain: kin,
4292
+ broad: [
4293
+ ...set.broad
4294
+ ],
4295
+ making: [],
4296
+ named: [],
4297
+ rivals: [],
4298
+ core: kin,
4299
+ shun: [],
4300
+ shunLead: []
4301
+ };
4302
+ }
4303
+ /**
4304
+ * Whether a site's world is only its own words (AGL-3923): no craft this
4305
+ * module lists and no kind of site named it, so its broad searches are its
4306
+ * business and its craft read back ("freelencing"), which a library may not
4307
+ * know at all.
4308
+ */ export function aiStockWorldIsUnlisted(terms) {
4309
+ const own = [
4310
+ ...new Set([
4311
+ terms.business,
4312
+ terms.craft
4313
+ ].map((word)=>word.trim()).filter(Boolean))
4314
+ ];
4315
+ return terms.broad.length > 0 && terms.broad.length === own.length && terms.broad.every((query, index)=>query === own[index]);
4316
+ }
4317
+ /**
4318
+ * Whether a site's world agrees with the kind of site the person picked
4319
+ * (AGL-3923): it was read from the kind, or names one of the kind's own
4320
+ * words. A gym whose owner picked Business & services does not, and is never
4321
+ * given an office.
4322
+ */ function worldOfKind(terms, kind) {
4323
+ var _ref;
4324
+ var _aiStockKindGenericTerms;
4325
+ const own = aiStockKindTerms(kind);
4326
+ if (!own) return false;
4327
+ if (own.craft === terms.craft && own.broad.join('|') === terms.broad.join('|')) return true;
4328
+ const words = new Set([
4329
+ ...own.domain,
4330
+ ...(_ref = (_aiStockKindGenericTerms = aiStockKindGenericTerms(kind)) == null ? void 0 : _aiStockKindGenericTerms.domain) != null ? _ref : []
4331
+ ]);
4332
+ return terms.domain.some((stem)=>words.has(stem));
4333
+ }
4334
+ /**
4335
+ * What a picture is searched by once nothing narrower found it a photo
4336
+ * (AGL-3923), widest last.
4337
+ *
4338
+ * - A site whose world is its own words alone ({@link aiStockWorldIsUnlisted})
4339
+ * widens to the kind of site the person picked, the kind its starter is the
4340
+ * shape of, and last the kind's own common photos ({@link KIND_GENERIC}),
4341
+ * else its starter's.
4342
+ * - Any other site widens only to its kind's own common photos, and only
4343
+ * where its world agrees with that kind ({@link worldOfKind}): a portfolio
4344
+ * read from its kind takes art supplies before no photo at all, and a
4345
+ * barbecue caterer whose kind says nothing of food never takes an office
4346
+ * (AGL-3831). A kind with no set keeps a slot nothing fits without a photo.
4347
+ *
4348
+ * A rung that would search what an earlier one did is left out.
4349
+ *
4350
+ * The beta.265 Kasvora Marketing Solutions start (job BK3r4I8bF1) typed its
4351
+ * business as "Freelencing site offer services": its world was the one word
4352
+ * "freelencing", which no library tags, so its cover hero searched it, found
4353
+ * nothing, and shipped a flat dark band with no photo.
4354
+ */ export function aiStockWiderTerms(terms, kind) {
4355
+ var _STARTER_KINDS_starter, _aiStockKindGenericTerms, _STARTER_GENERIC_KIND_starter;
4356
+ const rungs = [];
4357
+ const seen = new Set([
4358
+ JSON.stringify([
4359
+ terms.craft,
4360
+ [
4361
+ ...terms.broad
4362
+ ]
4363
+ ])
4364
+ ]);
4365
+ const add = (rung, found)=>{
4366
+ if (!found || !found.broad.length) return;
4367
+ const key = JSON.stringify([
4368
+ found.craft,
4369
+ found.broad
4370
+ ]);
4371
+ if (seen.has(key)) return;
4372
+ seen.add(key);
4373
+ rungs.push({
4374
+ rung,
4375
+ terms: found
4376
+ });
4377
+ };
4378
+ if (!aiStockWorldIsUnlisted(terms)) {
4379
+ if (kind && worldOfKind(terms, kind)) add('generic', aiStockKindGenericTerms(kind));
4380
+ return rungs;
4381
+ }
4382
+ const starter = starterOfKind(kind);
4383
+ if (kind) add('kind', aiStockKindTerms(kind));
4384
+ add('starter', aiStockKindTerms((_STARTER_KINDS_starter = STARTER_KINDS[starter]) != null ? _STARTER_KINDS_starter : 'business'));
4385
+ add('generic', (_aiStockKindGenericTerms = aiStockKindGenericTerms(kind)) != null ? _aiStockKindGenericTerms : aiStockKindGenericTerms((_STARTER_GENERIC_KIND_starter = STARTER_GENERIC_KIND[starter]) != null ? _STARTER_GENERIC_KIND_starter : 'business'));
4386
+ return rungs;
3803
4387
  }
3804
4388
  /**
3805
4389
  * The first word of a text that names a craft {@link CRAFT_KIN} lists, as
@@ -3841,7 +4425,7 @@ const EXTENSIONS = {
3841
4425
  * The source for one page, or `null` when this deployment has no stock
3842
4426
  * photo library or no media door, which leaves every slot to the starters.
3843
4427
  */ export function aiLayoutStockPhotoSource(input, deps = {}) {
3844
- var _deps_provider, _deps_ingest;
4428
+ var _deps_provider, _deps_ingest, _input_kind;
3845
4429
  const provider = ((_deps_provider = deps.provider) != null ? _deps_provider : stockPhotoProvider)();
3846
4430
  const ingest = ((_deps_ingest = deps.ingest) != null ? _deps_ingest : pluginMediaIngest)();
3847
4431
  if (!provider || !ingest || !input.hostId || !input.uid) return null;
@@ -3853,6 +4437,9 @@ const EXTENSIONS = {
3853
4437
  // alt texts, in its own language, name no subject a search could use.
3854
4438
  const english = aiStockSourceSubject(input);
3855
4439
  const terms = aiStockSourceTerms(input);
4440
+ // Where a picture's own world finds nothing, the wider worlds it tries (AGL-3923).
4441
+ const wider = aiStockWiderTerms(terms, (_input_kind = input.kind) != null ? _input_kind : '');
4442
+ const unlisted = aiStockWorldIsUnlisted(terms);
3856
4443
  const proper = aiStockProperWords(input.business);
3857
4444
  return async (slots)=>{
3858
4445
  var _deps_budgetMs, _input_avoid;
@@ -3869,8 +4456,10 @@ const EXTENSIONS = {
3869
4456
  /** The photos this page's slots have claimed, first choices, by slot order. */ const shown = [];
3870
4457
  /** Asset srcs this page has placed. */ const placedSrcs = new Set();
3871
4458
  const answers = new Map();
4459
+ /** The wider world a widened search is judged by (AGL-3923); a search not listed is judged by the site's. */ const worldOf = new WeakMap();
3872
4460
  let searches = 0;
3873
4461
  let storing = true;
4462
+ /** Slots a photo was chosen for and could not be copied into the library (AGL-3923). */ const uncopied = new Set();
3874
4463
  /** Whether the library answered any search of this page: a slot left empty then found nothing fitting. */ let answered = false;
3875
4464
  const remember = (sourceKey, src)=>{
3876
4465
  placedSrcs.add(src);
@@ -3953,7 +4542,17 @@ const EXTENSIONS = {
3953
4542
  maxBytes: AI_LAYOUT_STOCK_MAX_BYTES,
3954
4543
  signal
3955
4544
  });
3956
- if (!bytes || signal.aborted || !storing) return null;
4545
+ if (!bytes || signal.aborted || !storing) {
4546
+ // A copy the library would not hand over is otherwise silent (AGL-3923).
4547
+ if (!bytes && !signal.aborted) {
4548
+ uncopied.add(slot);
4549
+ console.warn('ai layout pictures: a stock photo could not be downloaded', {
4550
+ provider: photo.provider,
4551
+ id: photo.id
4552
+ });
4553
+ }
4554
+ return null;
4555
+ }
3957
4556
  const credit = provider.credit(photo);
3958
4557
  const stored = await ingest.ingest({
3959
4558
  hostId: input.hostId,
@@ -3988,13 +4587,28 @@ const EXTENSIONS = {
3988
4587
  height: (_stored_height = stored.height) != null ? _stored_height : photo.height
3989
4588
  }, described);
3990
4589
  }
3991
- storing = false;
3992
4590
  const refusal = stored;
4591
+ // One photo's own bytes refused (AGL-3923): the beta.267 Brightvale
4592
+ // start's first pick, Pixabay 10157445, came labeled image/png and was
4593
+ // not PNG, and that one 415 stopped every copy on all four of its
4594
+ // pages, which then showed the starters' pottery bench and coffee
4595
+ // shop. Such a refusal is this photo's: the slot tries its next hit.
4596
+ const ofThisPhoto = AI_STOCK_PHOTO_OWN_REFUSALS.has(refusal.status);
4597
+ if (ofThisPhoto) {
4598
+ uncopied.add(slot);
4599
+ // Never tried again by this page's other slots, nor by the job's other pages.
4600
+ claimed.add(sourceKey);
4601
+ job == null ? void 0 : job.set(sourceKey, REFUSED_BY_THE_LIBRARY);
4602
+ } else storing = false;
3993
4603
  console.warn('ai layout pictures: the media library refused a stock photo', {
3994
4604
  status: refusal.status,
3995
- reason: refusal.reason
4605
+ reason: refusal.reason,
4606
+ provider: photo.provider,
4607
+ id: photo.id,
4608
+ holds: ofThisPhoto ? 'this photo' : 'the page'
3996
4609
  });
3997
4610
  } catch (error) {
4611
+ uncopied.add(slot);
3998
4612
  console.warn('ai layout pictures: a stock photo could not be kept', {
3999
4613
  error: String(error)
4000
4614
  });
@@ -4006,14 +4620,14 @@ const EXTENSIONS = {
4006
4620
  * hit names a child; a portrait of one of the business's people names an
4007
4621
  * adult, never leads with a child, and is preferred in its role (AGL-3856).
4008
4622
  */ const judgementFor = (slot, request, subject)=>{
4009
- var _request_refuse, _input_sectionNames_slot_sectionIndex, _request_refuseLead;
4623
+ var _request_refuse, _input_sectionNames_slot_sectionIndex, _worldOf_get, _request_refuseLead;
4010
4624
  var _request_needs, _role_scene, _role_refuse;
4011
4625
  const refuse = [
4012
4626
  ...(_request_refuse = request.refuse) != null ? _request_refuse : [],
4013
4627
  ...terms.adults ? CHILD_STEMS : []
4014
4628
  ];
4015
4629
  const role = slot.people ? aiStockPeopleRole(slot.alt, (_input_sectionNames_slot_sectionIndex = input.sectionNames[slot.sectionIndex]) != null ? _input_sectionNames_slot_sectionIndex : '') : null;
4016
- return _extends({}, judgementOf(slot, request, subject), refuse.length ? {
4630
+ return _extends({}, judgementOf(slot, request, subject, (_worldOf_get = worldOf.get(request)) != null ? _worldOf_get : terms), refuse.length ? {
4017
4631
  refuse: [
4018
4632
  ...new Set(refuse)
4019
4633
  ]
@@ -4033,12 +4647,16 @@ const EXTENSIONS = {
4033
4647
  roleRefuse: aiStockRoleRefusals(role, slot.alt)
4034
4648
  } : {}) : {});
4035
4649
  };
4036
- /** A search's own judgement, as its words set it. */ const judgementOf = (slot, request, subject)=>{
4650
+ /**
4651
+ * A search's own judgement, as its words set it, held to the world it was
4652
+ * asked for: the site's own, or a wider one where the site's found nothing
4653
+ * (AGL-3923). What lets a sensitive topic in is always the site's own words.
4654
+ */ const judgementOf = (slot, request, subject, world)=>{
4037
4655
  var _request_subject, _request_shun;
4038
4656
  var _request_allow, _request_shun1, _request_leadAny, _request_refuse, _request_needs, _request_refuseLead;
4039
4657
  return _extends({
4040
4658
  subject: (_request_subject = request.subject) != null ? _request_subject : request.broad ? '' : subject,
4041
- domain: terms.domain,
4659
+ domain: world.domain,
4042
4660
  strict: slot.role === 'gallery' && !request.broad || !!request.requires || !!request.strict,
4043
4661
  named: ((_request_allow = request.allow) == null ? void 0 : _request_allow.length) ? [
4044
4662
  ...new Set([
@@ -4046,16 +4664,16 @@ const EXTENSIONS = {
4046
4664
  ...request.allow
4047
4665
  ])
4048
4666
  ] : terms.named,
4049
- rivals: terms.rivals
4050
- }, terms.shun.length || ((_request_shun1 = request.shun) == null ? void 0 : _request_shun1.length) ? {
4667
+ rivals: world.rivals
4668
+ }, world.shun.length || ((_request_shun1 = request.shun) == null ? void 0 : _request_shun1.length) ? {
4051
4669
  shun: [
4052
4670
  ...new Set([
4053
- ...terms.shun,
4671
+ ...world.shun,
4054
4672
  ...(_request_shun = request.shun) != null ? _request_shun : []
4055
4673
  ])
4056
4674
  ]
4057
- } : {}, terms.shunLead.length ? {
4058
- shunLead: terms.shunLead
4675
+ } : {}, world.shunLead.length ? {
4676
+ shunLead: world.shunLead
4059
4677
  } : {}, request.product ? {
4060
4678
  product: true
4061
4679
  } : {}, ((_request_leadAny = request.leadAny) == null ? void 0 : _request_leadAny.length) ? {
@@ -4089,9 +4707,9 @@ const EXTENSIONS = {
4089
4707
  * The craft's own photos, the last thing a slot of a craft's site is
4090
4708
  * filled from (AGL-3660): its broad searches, any hit naming the craft
4091
4709
  * itself. Never a desk, a laptop or a living room for a soap shop.
4092
- */ const craftSearches = (slot)=>{
4710
+ */ const craftSearches = (slot, world = terms)=>{
4093
4711
  // A portfolio's record of the work is never filled by the craft's own photos (AGL-3856).
4094
- if (!terms.core.length || aiStockShowsTheWork(slot, terms)) return [];
4712
+ if (!world.core.length || aiStockShowsTheWork(slot, world)) return [];
4095
4713
  const orientation = aiStockOrientation(slot.aspect);
4096
4714
  const size = slot.role === 'hero' ? {
4097
4715
  minWidth: 1600
@@ -4100,12 +4718,12 @@ const EXTENSIONS = {
4100
4718
  } : {
4101
4719
  minWidth: 900
4102
4720
  };
4103
- return terms.broad.map((query)=>_extends({
4721
+ return world.broad.map((query)=>_extends({
4104
4722
  query: clip(query),
4105
4723
  orientation
4106
4724
  }, size, {
4107
4725
  broad: true,
4108
- category: terms.core
4726
+ category: world.core
4109
4727
  }, slot.role === 'hero' ? {
4110
4728
  first: true
4111
4729
  } : {}, slot.product ? {
@@ -4113,6 +4731,78 @@ const EXTENSIONS = {
4113
4731
  tier: 'weak'
4114
4732
  } : {}));
4115
4733
  };
4734
+ /**
4735
+ * A slot's searches once its own world found it nothing (AGL-3923), in
4736
+ * order: the kind of site's world, the starter's, the slot's own words
4737
+ * alone held to no world, and last the starter's common photos. Each is
4738
+ * judged by the world it was asked for. A product, a portrait of the
4739
+ * business's people and a record's cover keep their own fallbacks.
4740
+ */ const widerSearches = (slot, subject, sectionName)=>{
4741
+ var _wider_find;
4742
+ if (!wider.length || slot.product || slot.people || slot.record) return [];
4743
+ const part = aiStockSlotPart(slot, sectionName);
4744
+ const asked = [];
4745
+ const ask = (world, requests)=>{
4746
+ for (const request of requests){
4747
+ worldOf.set(request, world);
4748
+ asked.push(request);
4749
+ }
4750
+ };
4751
+ for (const { rung, terms: world } of wider){
4752
+ if (rung === 'generic') continue;
4753
+ ask(world, [
4754
+ ...aiStockSearchesFor(slot, world, subject, part, sectionName),
4755
+ ...craftSearches(slot, world)
4756
+ ]);
4757
+ }
4758
+ const orientation = aiStockOrientation(slot.aspect);
4759
+ const size = slot.role === 'hero' ? {
4760
+ minWidth: 1600
4761
+ } : orientation === 'vertical' ? {
4762
+ minHeight: 900
4763
+ } : {
4764
+ minWidth: 900
4765
+ };
4766
+ // The slot's own words, only where the site's world is its own words
4767
+ // alone, held to the kind's common world (AGL-3923), never to no world:
4768
+ // the beta.267 Brightvale closing band's "ready talk marketing" was
4769
+ // answered with a tin-can telephone, and its pets-and-owners answers
4770
+ // name no office either.
4771
+ const common = (_wider_find = wider.find((rung)=>rung.rung === 'generic')) == null ? void 0 : _wider_find.terms;
4772
+ if (subject && unlisted && common) {
4773
+ const object = aiStockObjectWord(subject);
4774
+ const own = [
4775
+ ...new Set([
4776
+ subject,
4777
+ object
4778
+ ].map(clip).filter(Boolean))
4779
+ ];
4780
+ ask(common, own.map((query)=>_extends({
4781
+ query,
4782
+ orientation
4783
+ }, size, {
4784
+ subject,
4785
+ strict: true
4786
+ })));
4787
+ }
4788
+ // The kind's common photos: OF its world among their first tags, never
4789
+ // led by a face, never of a child (AGL-3923).
4790
+ for (const { rung, terms: world } of wider)if (rung === 'generic') ask(world, world.broad.map((query)=>_extends({
4791
+ query: clip(query),
4792
+ orientation
4793
+ }, size, {
4794
+ broad: true,
4795
+ category: world.core
4796
+ }, slot.role === 'hero' ? {
4797
+ first: true
4798
+ } : {
4799
+ leadWorld: true
4800
+ }, {
4801
+ refuse: CHILD_STEMS,
4802
+ refuseLead: FACE_STEMS
4803
+ })));
4804
+ return asked;
4805
+ };
4116
4806
  // 1. Choose, slot by slot, so the page's choices never collide: each
4117
4807
  // slot claims its best accepted hit before the next slot looks.
4118
4808
  const choices = [];
@@ -4143,6 +4833,11 @@ const EXTENSIONS = {
4143
4833
  if (elsewhere(key)) blocked.add(key);
4144
4834
  }
4145
4835
  }
4836
+ // A photo the library refused for its own bytes is never a repeat either (AGL-3923).
4837
+ for (const hit of found){
4838
+ const key = stockPhotoSourceKey(hit);
4839
+ if ((job == null ? void 0 : job.get(key)) === REFUSED_BY_THE_LIBRARY) blocked.add(key);
4840
+ }
4146
4841
  const judgement = judgementFor(slot, request, subject);
4147
4842
  const ranked = aiStockRank(found, blocked, `${input.seed}:${index}:${request.query}`, judgement);
4148
4843
  // A team's portraits are of different people where the hits allow (AGL-3856).
@@ -4177,6 +4872,9 @@ const EXTENSIONS = {
4177
4872
  const repeats = Boolean(slot.product) || slot.role === 'hero';
4178
4873
  const sure = slot.product ? requests.filter((request)=>request.tier !== 'weak') : requests;
4179
4874
  const unsure = slot.product ? requests.filter((request)=>request.tier === 'weak') : [];
4875
+ // 3. Nothing of its own world at all: the wider worlds (AGL-3923), new
4876
+ // and then, for a hero, one the job already shows.
4877
+ const widened = widerSearches(slot, subject, sectionName);
4180
4878
  const phases = [
4181
4879
  [
4182
4880
  sure,
@@ -4197,6 +4895,18 @@ const EXTENSIONS = {
4197
4895
  unsure,
4198
4896
  true
4199
4897
  ]
4898
+ ] : [],
4899
+ ...widened.length ? [
4900
+ [
4901
+ widened,
4902
+ false
4903
+ ]
4904
+ ] : [],
4905
+ ...widened.length && repeats ? [
4906
+ [
4907
+ widened,
4908
+ true
4909
+ ]
4200
4910
  ] : []
4201
4911
  ];
4202
4912
  for (const [tried, repeat] of phases){
@@ -4265,7 +4975,10 @@ const EXTENSIONS = {
4265
4975
  // The library answered and nothing in it fits: no photo rather than a
4266
4976
  // starter that shows another business (AGL-3831). A library that never
4267
4977
  // answered, or a site that refused to store, leaves the starters.
4268
- return answered && storing ? photos.map((photo)=>photo != null ? photo : AI_LAYOUT_NO_PHOTO) : photos;
4978
+ // A slot whose fitting photo could not be copied was failed by the
4979
+ // library, as one that never answered is, and takes a starter rather
4980
+ // than lose its picture (AGL-3923).
4981
+ return answered && storing ? photos.map((photo, index)=>photo != null ? photo : uncopied.has(slots[index]) ? null : AI_LAYOUT_NO_PHOTO) : photos;
4269
4982
  };
4270
4983
  }
4271
4984