@aglyn/plugins-ai 1.0.0-beta.260 → 1.0.0-beta.262
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/package.json +17 -17
- package/src/lib/components/ai-brief-dialog.component.d.ts +21 -58
- package/src/lib/components/ai-brief-dialog.component.js +8 -132
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +2 -35
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +2 -8
- package/src/lib/components/ai-job-plan.component.js +13 -74
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.js +43 -34
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
- package/src/lib/components/ai-template-collection-field.component.js +13 -10
- package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
- package/src/lib/components/ai-upsell-dialog.component.js +9 -66
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -8
- package/src/lib/components/use-ai-job-run.js +4 -15
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/insights/ai-figure-readers.js +15 -5
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
- package/src/lib/insights/ai-figure-summaries.js +79 -0
- package/src/lib/insights/ai-figure-summaries.js.map +1 -0
- package/src/lib/insights/ai-insight-readers.d.ts +2 -1
- package/src/lib/insights/ai-insight-readers.js +11 -26
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-admission.d.ts +6 -0
- package/src/lib/jobs/ai-job-admission.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +10 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +43 -11
- package/src/lib/jobs/ai-layout-stock-photos.js +329 -32
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-stock-photo-picker.js +13 -7
- package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.d.ts +35 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.js +404 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-motion.d.ts +9 -3
- package/src/lib/layout-language/ai-layout-motion.js +9 -3
- package/src/lib/layout-language/ai-layout-motion.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +10 -1
- package/src/lib/layout-language/ai-layout-pictures.js +12 -6
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-brief-copy.d.ts +85 -0
- package/src/lib/model/ai-brief-copy.js +152 -0
- package/src/lib/model/ai-brief-copy.js.map +1 -0
- package/src/lib/model/ai-create-view.d.ts +200 -0
- package/src/lib/model/ai-create-view.js +263 -0
- package/src/lib/model/ai-create-view.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +1 -1
- package/src/lib/model/ai-insight.js +4 -39
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +24 -0
- package/src/lib/model/ai-job-activity.js +52 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
- package/src/lib/model/ai-job-plan-lines.js +70 -0
- package/src/lib/model/ai-job-plan-lines.js.map +1 -0
- package/src/lib/model/ai-template-collections.d.ts +28 -0
- package/src/lib/model/ai-template-collections.js +28 -0
- package/src/lib/model/ai-template-collections.js.map +1 -0
- package/src/lib/model/ai-upsell-copy.d.ts +41 -0
- package/src/lib/model/ai-upsell-copy.js +85 -0
- package/src/lib/model/ai-upsell-copy.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +40 -0
- package/src/lib/runtime/ai-doctrine-validators.js +148 -3
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +6 -2
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +32 -0
- package/src/lib/server/ai-create-route.js +295 -0
- package/src/lib/server/ai-create-route.js.map +1 -0
- package/src/lib/server/ai-jobs-route.js +19 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
|
@@ -117,6 +117,19 @@ import type { AiBuildPlan } from '../model/ai-build-plan';
|
|
|
117
117
|
* its own. No product shows a room or a scene, a fragrance bottle, or what
|
|
118
118
|
* its kin shuns (a pump for a bar) unless it names it; "bath" in a bath
|
|
119
119
|
* gift set is what the set is for; a size is never the product.
|
|
120
|
+
* 8. **What the photo is OF** (the beta.259 starts, AGL-3856: a typewriter
|
|
121
|
+
* printing "terms of service" as a family law firm's hero, a yoga pose on
|
|
122
|
+
* a beach and a heart-shaped potato as a strength gym's events, a horse's
|
|
123
|
+
* head of a "western film" as a photographer's "Film" card). A listed
|
|
124
|
+
* craft may name what its photos are never OF among their first tags
|
|
125
|
+
* (`shunLead`: a document for a law practice, yoga for a strength gym) and
|
|
126
|
+
* its rival worlds (a movie's for a photographer's film); a comma after a
|
|
127
|
+
* place goes on naming the place ("Columbus, Ohio"), never the world; an
|
|
128
|
+
* event's or a lesson's word ("basics", "class", "session") is never its
|
|
129
|
+
* object; a word the table does not list beside a craft it does
|
|
130
|
+
* ("wedding" of "a wedding and portrait photographer") is not the craft;
|
|
131
|
+
* and a hero whose second tag names the world too leads one whose first
|
|
132
|
+
* alone does.
|
|
120
133
|
*
|
|
121
134
|
* A slot the library could not fill because it never answered — down, or
|
|
122
135
|
* out of time before its first answer — or because the site's library
|
|
@@ -203,6 +216,8 @@ export interface AiStockSiteTerms {
|
|
|
203
216
|
core: string[];
|
|
204
217
|
/** Words no photo of the site's craft carries unless the slot or the brief names them ({@link CRAFT_KIN}). */
|
|
205
218
|
shun: string[];
|
|
219
|
+
/** Words no photo of the site's craft is OF, among its first tags, unless the slot or the brief names them ({@link CRAFT_KIN}). */
|
|
220
|
+
shunLead: string[];
|
|
206
221
|
}
|
|
207
222
|
/** The site's terms, read from its business type. */
|
|
208
223
|
export declare function aiStockSiteTerms(businessType: string): AiStockSiteTerms;
|
|
@@ -370,6 +385,8 @@ export interface AiStockJudgement {
|
|
|
370
385
|
product?: boolean;
|
|
371
386
|
/** Words a hit never carries unless the subject or the brief names them ({@link AiStockSiteTerms.shun}). */
|
|
372
387
|
shun?: readonly string[];
|
|
388
|
+
/** Words a hit's first tags never carry unless the subject or the brief names them ({@link AiStockSiteTerms.shunLead}). */
|
|
389
|
+
shunLead?: readonly string[];
|
|
373
390
|
}
|
|
374
391
|
/**
|
|
375
392
|
* Whether a hit carries a topic its site never named ({@link SENSITIVE_TOPICS}):
|
|
@@ -406,7 +423,15 @@ export declare function aiStockPlacedSrcs(nodes: NodesMap | null | undefined): s
|
|
|
406
423
|
/** Forgets every job's placed photos; for specs. */
|
|
407
424
|
export declare function aiStockForgetJobPhotos(): void;
|
|
408
425
|
/**
|
|
409
|
-
*
|
|
426
|
+
* A photo described from its own tags (AGL-3856): its first few that name a
|
|
427
|
+
* thing, a phrase another one holds dropped ("wedding" beside "wedding
|
|
428
|
+
* dress"), as an English phrase — "A wedding dress and a bride", "A hammer,
|
|
429
|
+
* books and law" — never the comma-separated list the library keeps. `null`
|
|
430
|
+
* where none names a thing.
|
|
431
|
+
*/
|
|
432
|
+
export declare function aiStockTagsAlt(tags: readonly string[], proper?: ReadonlySet<string>): string | null;
|
|
433
|
+
/**
|
|
434
|
+
* THE WORDS A PLACED STOCK PHOTO IS DESCRIBED BY (AGL-3805, AGL-3856).
|
|
410
435
|
*
|
|
411
436
|
* A slot's alt text is the model's, written for the picture the page asked
|
|
412
437
|
* for before any photo was found, and a slot is filled by whatever the
|
|
@@ -420,21 +445,28 @@ export declare function aiStockForgetJobPhotos(): void;
|
|
|
420
445
|
* dulce", a plate of macarons the shop's display case.
|
|
421
446
|
*
|
|
422
447
|
* So the photo placed answers for its own words. `null` keeps the slot's: on
|
|
423
|
-
* an English site whose words name
|
|
424
|
-
* subject (`aiStockSubjectWords
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
448
|
+
* an English site whose words name the thing the photo is OF — their
|
|
449
|
+
* subject's object (`aiStockSubjectWords`, a trailing "speaking calmly"
|
|
450
|
+
* dropped) among the hit's first two tags. Any word of the subject among its
|
|
451
|
+
* first three was not enough: the beta.259 Hartwell Reyes Family Law hero kept
|
|
452
|
+
* "Two attorneys speaking calmly with a client in a bright Columbus office"
|
|
453
|
+
* for a typewriter whose third tag was "attorney" (AGL-3856). A stand-in's
|
|
454
|
+
* words ({@link AiLayoutPictureSlot.standIn}) are a search's, never a
|
|
455
|
+
* description, and are never kept. Otherwise the photo is described by the
|
|
428
456
|
* library's own words: its description where it writes one (a Pexels hit's
|
|
429
|
-
* `alt`), else its first tags
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
457
|
+
* `alt`), else its first tags as an English phrase ({@link aiStockTagsAlt}),
|
|
458
|
+
* never the library's tag list. The libraries write in English, and no model
|
|
459
|
+
* translates them, so a site in another language takes the platform's own
|
|
460
|
+
* words for an illustrative photo in its language (`aiLayoutLabel`), true of
|
|
461
|
+
* any photo, as does a hit whose words name no thing.
|
|
433
462
|
*/
|
|
434
|
-
export declare function aiStockPhotoAlt(photo: Pick<StockPhoto, 'tags' | 'alt' | 'pageUrl'>, slot: Pick<AiLayoutPictureSlot, 'alt' | 'product'>, input?: {
|
|
463
|
+
export declare function aiStockPhotoAlt(photo: Pick<StockPhoto, 'tags' | 'alt' | 'pageUrl'>, slot: Pick<AiLayoutPictureSlot, 'alt' | 'product' | 'standIn'>, input?: {
|
|
435
464
|
language?: string;
|
|
436
465
|
sectionName?: string;
|
|
466
|
+
proper?: ReadonlySet<string>;
|
|
437
467
|
}): string | null;
|
|
468
|
+
/** The names a business type writes with a capital, lowercased: "columbus", "ohio" (AGL-3856). */
|
|
469
|
+
export declare function aiStockProperWords(businessType: string): Set<string>;
|
|
438
470
|
export interface AiLayoutStockPhotoSourceInput {
|
|
439
471
|
hostId: string;
|
|
440
472
|
/** The member the photos are stored as: the job's creator. */
|
|
@@ -119,6 +119,19 @@ import { aiStockBusinessNoun } from "./ai-stock-business-nouns.js";
|
|
|
119
119
|
* its own. No product shows a room or a scene, a fragrance bottle, or what
|
|
120
120
|
* its kin shuns (a pump for a bar) unless it names it; "bath" in a bath
|
|
121
121
|
* gift set is what the set is for; a size is never the product.
|
|
122
|
+
* 8. **What the photo is OF** (the beta.259 starts, AGL-3856: a typewriter
|
|
123
|
+
* printing "terms of service" as a family law firm's hero, a yoga pose on
|
|
124
|
+
* a beach and a heart-shaped potato as a strength gym's events, a horse's
|
|
125
|
+
* head of a "western film" as a photographer's "Film" card). A listed
|
|
126
|
+
* craft may name what its photos are never OF among their first tags
|
|
127
|
+
* (`shunLead`: a document for a law practice, yoga for a strength gym) and
|
|
128
|
+
* its rival worlds (a movie's for a photographer's film); a comma after a
|
|
129
|
+
* place goes on naming the place ("Columbus, Ohio"), never the world; an
|
|
130
|
+
* event's or a lesson's word ("basics", "class", "session") is never its
|
|
131
|
+
* object; a word the table does not list beside a craft it does
|
|
132
|
+
* ("wedding" of "a wedding and portrait photographer") is not the craft;
|
|
133
|
+
* and a hero whose second tag names the world too leads one whose first
|
|
134
|
+
* alone does.
|
|
122
135
|
*
|
|
123
136
|
* A slot the library could not fill because it never answered — down, or
|
|
124
137
|
* out of time before its first answer — or because the site's library
|
|
@@ -155,13 +168,15 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
155
168
|
/** The nouns that name a place of business, not its craft: "studio" in "ceramics studio". */ const VENUES = words('studio studios shop shops store stores boutique company co workshop gallery atelier agency brand practice ' + 'salon house collective lab market services service firm clinic center centre parlor parlour bar ' + 'class classes lessons school academy');
|
|
156
169
|
/** The person who does the craft, not the craft: "artist" in "ceramic artist". */ const ROLES = words('artist artists maker makers artisan artisans owner owners founder founders creator creators');
|
|
157
170
|
/** Words naming what kind of SITE it is, never what it shows: "portfolio" in "a portfolio for a ceramic artist". */ const SITE_KINDS = words('portfolio portfolios website websites site sites blog blogs online web webshop ecommerce e-commerce ' + 'landing personal homepage storefront');
|
|
158
|
-
/** Verbs a business type uses to say what it does; what follows them is its domain, never its business. */ const DOING = /(?<![\w-])(?:makes?|making|made|sells?|selling|sold|offers?|offering|creates?|creating|specializ(?:es|ing)|specialis(?:es|ing)|provides?|providing|featuring|showcasing)(?![\w-])/g;
|
|
171
|
+
/** Verbs a business type uses to say what it does; what follows them is its domain, never its business. */ const DOING = /(?<![\w-])(?:makes?|making|made|sells?|selling|sold|offers?|offering|creates?|creating|specializ(?:es|ing)|specialis(?:es|ing)|provides?|providing|featuring|showcasing|shoots?|shooting)(?![\w-])/g;
|
|
159
172
|
/** Where a business type's own clause ends. */ const BUSINESS_BREAK = /[,;:.()]|\s[-–—]\s|\b(?:in|for|near|serving|based|located|that|which|who|with|from|since|and|&)\b/;
|
|
160
173
|
/** A clause after one of these says where or for whom, never what: "in Austin", "for busy parents". */ const PLACE_OR_AUDIENCE = /^(?:in|near|serving|based|located|across|around|throughout|for)$/;
|
|
161
174
|
/**
|
|
162
175
|
* Words naming a set or a wrapper of a thing, never the thing: the OBJECT
|
|
163
176
|
* of "nesting bowl set" is the bowl, of "candle gift set" the candle.
|
|
164
|
-
*/ const WRAPPERS = words('set sets kit kits gift gifts bundle bundles pack packs box boxes trio duo pair collection sampler assortment ' + 'series range line group lot bunch')
|
|
177
|
+
*/ const WRAPPERS = words('set sets kit kits gift gifts bundle bundles pack packs box boxes trio duo pair collection sampler assortment ' + 'series range line group lot bunch ' + // What an event or a lesson is, never what it shows (AGL-3856): "Intro to
|
|
178
|
+
// barbell basics" is of a barbell, "Small-group strength class" of strength.
|
|
179
|
+
'class classes session sessions lesson lessons course courses workshop workshops seminar seminars ' + 'meetup meetups program programs programme programmes basics intro introduction fundamentals');
|
|
165
180
|
/**
|
|
166
181
|
* A hit tagged with any of these is never placed: a mockup's blank panel is
|
|
167
182
|
* an empty frame on the page (the Juniper Clay hero), a template or a
|
|
@@ -1076,6 +1091,186 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1076
1091
|
making: [
|
|
1077
1092
|
'leather crafting'
|
|
1078
1093
|
]
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
// A law practice is its people and its office: never a document, a
|
|
1097
|
+
// statute or a statue of justice (the beta.259 Hartwell Reyes hero).
|
|
1098
|
+
match: [
|
|
1099
|
+
'law',
|
|
1100
|
+
'legal',
|
|
1101
|
+
'lawyer',
|
|
1102
|
+
'attorney',
|
|
1103
|
+
'solicitor',
|
|
1104
|
+
'barrister',
|
|
1105
|
+
'paralegal',
|
|
1106
|
+
'notary'
|
|
1107
|
+
],
|
|
1108
|
+
kin: [
|
|
1109
|
+
'law',
|
|
1110
|
+
'legal',
|
|
1111
|
+
'lawyer',
|
|
1112
|
+
'attorney',
|
|
1113
|
+
'counsel',
|
|
1114
|
+
'office',
|
|
1115
|
+
'client',
|
|
1116
|
+
'consultation',
|
|
1117
|
+
'meeting',
|
|
1118
|
+
'advice',
|
|
1119
|
+
'mediation'
|
|
1120
|
+
],
|
|
1121
|
+
broad: [
|
|
1122
|
+
'lawyer office',
|
|
1123
|
+
'lawyer client meeting'
|
|
1124
|
+
],
|
|
1125
|
+
making: [
|
|
1126
|
+
'lawyer meeting client',
|
|
1127
|
+
'lawyer office'
|
|
1128
|
+
],
|
|
1129
|
+
// Policing is not a family firm's; a statue of justice is a symbol, not its people.
|
|
1130
|
+
shun: [
|
|
1131
|
+
'police',
|
|
1132
|
+
'policeman',
|
|
1133
|
+
'cop',
|
|
1134
|
+
'handcuffs',
|
|
1135
|
+
'prisoner',
|
|
1136
|
+
'arrest',
|
|
1137
|
+
'crime',
|
|
1138
|
+
'criminal',
|
|
1139
|
+
'justitia',
|
|
1140
|
+
'themis',
|
|
1141
|
+
'goddess'
|
|
1142
|
+
],
|
|
1143
|
+
shunLead: [
|
|
1144
|
+
'term',
|
|
1145
|
+
'condition',
|
|
1146
|
+
'paragraph',
|
|
1147
|
+
'codex',
|
|
1148
|
+
'regulation',
|
|
1149
|
+
'statute',
|
|
1150
|
+
'document',
|
|
1151
|
+
'contract',
|
|
1152
|
+
'typewriter',
|
|
1153
|
+
'constitution',
|
|
1154
|
+
'certificate'
|
|
1155
|
+
]
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
// A strength gym's photos are of lifting and training; a yoga pose on a
|
|
1159
|
+
// beach is tagged "strength" too (the beta.259 Ironline Strength Studio
|
|
1160
|
+
// events).
|
|
1161
|
+
match: [
|
|
1162
|
+
'gym',
|
|
1163
|
+
'fitness',
|
|
1164
|
+
'strength',
|
|
1165
|
+
'barbell',
|
|
1166
|
+
'crossfit',
|
|
1167
|
+
'weightlifting',
|
|
1168
|
+
'powerlifting',
|
|
1169
|
+
'bodybuilding',
|
|
1170
|
+
'kettlebell'
|
|
1171
|
+
],
|
|
1172
|
+
kin: [
|
|
1173
|
+
'gym',
|
|
1174
|
+
'fitness',
|
|
1175
|
+
'workout',
|
|
1176
|
+
'training',
|
|
1177
|
+
'exercise',
|
|
1178
|
+
'barbell',
|
|
1179
|
+
'dumbbell',
|
|
1180
|
+
'weight',
|
|
1181
|
+
'weightlifting',
|
|
1182
|
+
'deadlift',
|
|
1183
|
+
'squat',
|
|
1184
|
+
'kettlebell',
|
|
1185
|
+
'athlete',
|
|
1186
|
+
'strength',
|
|
1187
|
+
'muscle'
|
|
1188
|
+
],
|
|
1189
|
+
broad: [
|
|
1190
|
+
'gym',
|
|
1191
|
+
'weight training'
|
|
1192
|
+
],
|
|
1193
|
+
making: [
|
|
1194
|
+
'coach training athlete',
|
|
1195
|
+
'barbell training'
|
|
1196
|
+
],
|
|
1197
|
+
shunLead: [
|
|
1198
|
+
'yoga',
|
|
1199
|
+
'pilates',
|
|
1200
|
+
'meditation',
|
|
1201
|
+
'ballet',
|
|
1202
|
+
'ballerina',
|
|
1203
|
+
'dance',
|
|
1204
|
+
'dancer',
|
|
1205
|
+
'karate',
|
|
1206
|
+
'kickboxing',
|
|
1207
|
+
'boxing',
|
|
1208
|
+
'boxer',
|
|
1209
|
+
'beach',
|
|
1210
|
+
'circus',
|
|
1211
|
+
// Another sport's ball, never "medicine ball": a basketball court covered a deadlift night (AGL-3864).
|
|
1212
|
+
'basketball',
|
|
1213
|
+
'football',
|
|
1214
|
+
'soccer',
|
|
1215
|
+
'baseball',
|
|
1216
|
+
'volleyball',
|
|
1217
|
+
'tennis',
|
|
1218
|
+
'golf',
|
|
1219
|
+
'rugby',
|
|
1220
|
+
'hockey'
|
|
1221
|
+
]
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
// A photographer's own photos are of a camera or a shoot; "film" is a
|
|
1225
|
+
// movie's word too, and "western film" a horse (the beta.259 Juniper and
|
|
1226
|
+
// Rye Photography "Film" card).
|
|
1227
|
+
match: [
|
|
1228
|
+
'photographer',
|
|
1229
|
+
'photography',
|
|
1230
|
+
'photo',
|
|
1231
|
+
'photograph'
|
|
1232
|
+
],
|
|
1233
|
+
kin: [
|
|
1234
|
+
'photographer',
|
|
1235
|
+
'photography',
|
|
1236
|
+
'photo',
|
|
1237
|
+
'photograph',
|
|
1238
|
+
'photoshoot',
|
|
1239
|
+
'camera',
|
|
1240
|
+
'lens'
|
|
1241
|
+
],
|
|
1242
|
+
broad: [
|
|
1243
|
+
'photographer camera',
|
|
1244
|
+
'photography'
|
|
1245
|
+
],
|
|
1246
|
+
making: [
|
|
1247
|
+
'photographer at work',
|
|
1248
|
+
'photographer camera'
|
|
1249
|
+
],
|
|
1250
|
+
rivals: {
|
|
1251
|
+
words: [
|
|
1252
|
+
'movie',
|
|
1253
|
+
'cinema',
|
|
1254
|
+
'cinematography',
|
|
1255
|
+
'filmmaking',
|
|
1256
|
+
'filming',
|
|
1257
|
+
'videography',
|
|
1258
|
+
'video',
|
|
1259
|
+
'actor',
|
|
1260
|
+
'projector',
|
|
1261
|
+
'hollywood',
|
|
1262
|
+
'western'
|
|
1263
|
+
],
|
|
1264
|
+
core: [
|
|
1265
|
+
'camera',
|
|
1266
|
+
'photograph',
|
|
1267
|
+
'photography',
|
|
1268
|
+
'photographer',
|
|
1269
|
+
'photo',
|
|
1270
|
+
'photoshoot',
|
|
1271
|
+
'analog'
|
|
1272
|
+
]
|
|
1273
|
+
}
|
|
1079
1274
|
}
|
|
1080
1275
|
];
|
|
1081
1276
|
/** Lowercase words, letters only, without stopwords. */ function contentWords(text) {
|
|
@@ -1150,13 +1345,20 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1150
1345
|
/** The last word of the first clause that names the world: "soap" of "for handmade cold-process soap". */ let firstWorld = '';
|
|
1151
1346
|
let named = false;
|
|
1152
1347
|
let making = false;
|
|
1348
|
+
let place = false;
|
|
1153
1349
|
for (const { opener, words: clause } of businessClauses(businessType)){
|
|
1154
1350
|
if (opener === '|') making = true;
|
|
1155
1351
|
// Where it is, and who it is for, are not its world — once something has named it.
|
|
1156
1352
|
if (named && PLACE_OR_AUDIENCE.test(opener)) {
|
|
1157
1353
|
making = false;
|
|
1354
|
+
place = true;
|
|
1158
1355
|
continue;
|
|
1159
1356
|
}
|
|
1357
|
+
// A comma goes on naming the place (AGL-3856): "Ohio" of "in Columbus,
|
|
1358
|
+
// Ohio" and "North Carolina" of "in Asheville, North Carolina" are not
|
|
1359
|
+
// the business's world.
|
|
1360
|
+
if (place && opener === ',') continue;
|
|
1361
|
+
place = false;
|
|
1160
1362
|
const kept = businessOf(clause).filter((word)=>!VENUES.has(word) && !ROLES.has(word));
|
|
1161
1363
|
if (kept.length) named = true;
|
|
1162
1364
|
// What it makes names its world by what the things are made of, never
|
|
@@ -1183,6 +1385,12 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1183
1385
|
...made
|
|
1184
1386
|
][0] || '';
|
|
1185
1387
|
if (craft && !stemList(business).includes(craft)) business = `${craft} ${business}`.trim();
|
|
1388
|
+
} else if (groups[0] && !groups.some((group)=>group.match.some((word)=>aiStockStem(word) === craft))) {
|
|
1389
|
+
var _groups__match_map_find;
|
|
1390
|
+
// A word the table does not list, beside a craft it does, says what kind
|
|
1391
|
+
// of that craft it is (AGL-3856): "wedding" of "a wedding and portrait
|
|
1392
|
+
// photographer" is a photographer's subject, never a craft to search by.
|
|
1393
|
+
craft = (_groups__match_map_find = groups[0].match.map(aiStockStem).find((stem)=>domain.has(stem) || made.has(stem))) != null ? _groups__match_map_find : craft;
|
|
1186
1394
|
}
|
|
1187
1395
|
if (craft) domain.add(craft);
|
|
1188
1396
|
const broad = [];
|
|
@@ -1192,9 +1400,11 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1192
1400
|
craft
|
|
1193
1401
|
] : []);
|
|
1194
1402
|
const shun = new Set();
|
|
1403
|
+
const shunLead = new Set();
|
|
1195
1404
|
for (const group of groups){
|
|
1196
|
-
var _group_shun;
|
|
1405
|
+
var _group_shun, _group_shunLead;
|
|
1197
1406
|
for (const word of (_group_shun = group.shun) != null ? _group_shun : [])shun.add(aiStockStem(word));
|
|
1407
|
+
for (const word of (_group_shunLead = group.shunLead) != null ? _group_shunLead : [])shunLead.add(aiStockStem(word));
|
|
1198
1408
|
for (const word of group.match)core.add(aiStockStem(word));
|
|
1199
1409
|
for (const word of group.kin)domain.add(aiStockStem(word));
|
|
1200
1410
|
broad.push(...group.broad);
|
|
@@ -1226,6 +1436,9 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
1226
1436
|
],
|
|
1227
1437
|
shun: [
|
|
1228
1438
|
...shun
|
|
1439
|
+
],
|
|
1440
|
+
shunLead: [
|
|
1441
|
+
...shunLead
|
|
1229
1442
|
]
|
|
1230
1443
|
};
|
|
1231
1444
|
}
|
|
@@ -1683,7 +1896,7 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1683
1896
|
* the subject's two-word phrase 3, each of its other words 1, and the
|
|
1684
1897
|
* site's world 1, plus a quarter when it is among the first tags.
|
|
1685
1898
|
*/ export function aiStockRelevance(photo, judgement) {
|
|
1686
|
-
var _judgement_rivals, _judgement_subject, _judgement_named, _judgement_shun, _judgement_leadAny, _judgement_kin, _judgement_requires, _judgement_category, _judgement_object, _judgement_domain;
|
|
1899
|
+
var _judgement_rivals, _judgement_subject, _judgement_named, _judgement_shun, _judgement_shunLead, _judgement_leadAny, _judgement_kin, _judgement_requires, _judgement_category, _judgement_object, _judgement_domain;
|
|
1687
1900
|
const hit = hitWords(photo);
|
|
1688
1901
|
if (hit.rejected) return 0;
|
|
1689
1902
|
if (aiStockSensitive(photo, judgement.named)) return 0;
|
|
@@ -1697,6 +1910,8 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1697
1910
|
]);
|
|
1698
1911
|
// A dispenser is not a soapmaker's, nor a pump a bar's (AGL-3660).
|
|
1699
1912
|
if (((_judgement_shun = judgement.shun) != null ? _judgement_shun : []).some((word)=>hit.all.has(word) && !owned.has(word))) return 0;
|
|
1913
|
+
// What the photo is OF: a typewriter's terms of service are not a law firm's.
|
|
1914
|
+
if (((_judgement_shunLead = judgement.shunLead) != null ? _judgement_shunLead : []).some((word)=>hit.lead.has(word) && !owned.has(word))) return 0;
|
|
1700
1915
|
if (judgement.product) {
|
|
1701
1916
|
// A sauna, a bathroom shelf: a room is never the product.
|
|
1702
1917
|
for (const word of hit.lead)if (PRODUCT_SCENES.has(word) && !owned.has(word)) return 0;
|
|
@@ -1773,6 +1988,13 @@ const clip = (query)=>query.replace(/\s+/g, ' ').trim().slice(0, STOCK_PHOTO_QUE
|
|
|
1773
1988
|
if (phrased) score += 3;
|
|
1774
1989
|
for (const word of new Set(subject))if (word !== object && hit.all.has(word)) score += 1;
|
|
1775
1990
|
if (world) score += worldLead ? 1.25 : 1;
|
|
1991
|
+
// A hero is most of all what its first tags say: "law office, lawyer,
|
|
1992
|
+
// attorney" over "lawyers, personal injury, accident" (AGL-3856): a second
|
|
1993
|
+
// of them naming its world says it is of that world, not of something else.
|
|
1994
|
+
if (judgement.first && worldStems.size) {
|
|
1995
|
+
const said = hit.leadPhrases.slice(0, -1).filter((phrase)=>phrase.split(' ').some((stem)=>worldStems.has(stem)));
|
|
1996
|
+
if (said.length >= 2) score += 0.25;
|
|
1997
|
+
}
|
|
1776
1998
|
return Math.max(score, 0.5);
|
|
1777
1999
|
}
|
|
1778
2000
|
/**
|
|
@@ -1845,7 +2067,71 @@ function jobPhotos(key) {
|
|
|
1845
2067
|
}
|
|
1846
2068
|
/** The longest description of a photo written from the library's own words. */ const AI_STOCK_ALT_MAX = 160;
|
|
1847
2069
|
/**
|
|
1848
|
-
*
|
|
2070
|
+
* Words a tag uses for how a photo looks or feels, never a thing it shows
|
|
2071
|
+
* (AGL-3856): "extravagant" of "wedding dress, bride, extravagant".
|
|
2072
|
+
*/ 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 healthy fit ' + 'strong powerful homemade tasty yummy sweet sweetness nice dark bw monochrome macro sporty active married bridal ' + 'female male');
|
|
2073
|
+
/** What a tag names that takes no article: "law" of "hammer, books, law". */ const MASS_NOUNS = words('food coffee tea bread wood water law fitness wax soap clay nature money fabric yarn wool furniture jewelry jewellery ' + 'pottery glass paper music art sport health business meat grass snow sand gold silver leather cotton hair makeup ' + 'skincare justice equipment technology work architecture decor fashion wildlife marriage photography exercise ' + 'scenery weather traffic education advocacy wine beer chocolate rice sugar flour milk cheese honey oil ice fire ' + 'light sunlight dough sourdough pastry dessert strength crossfit people');
|
|
2074
|
+
/** Words ending in "-ing" that are things, and take an article: "a wedding", "a ring". */ const COUNTED_ING = words('wedding ring earring building painting drawing ceiling string swing king sibling wing');
|
|
2075
|
+
/**
|
|
2076
|
+
* A tag as a phrase of an English description (AGL-3856): "a wedding dress",
|
|
2077
|
+
* "an attorney", "books", "law", "Columbus"; `null` for a tag that names no
|
|
2078
|
+
* thing ("extravagant", "beautiful") or is too long to be one.
|
|
2079
|
+
*/ function describedTag(tag, proper) {
|
|
2080
|
+
const describing = (word)=>FILLER.has(word) || DESCRIBING.has(word) || /(?:ous|ful)$/.test(word) || word.length > 4 && /[^e]ed$/.test(word);
|
|
2081
|
+
const all = tag.toLowerCase().replace(/[^a-zÀ-ɏ\s'-]/g, ' ').split(/\s+/).filter(Boolean);
|
|
2082
|
+
// "Beautiful flowers" shows flowers; "roasted" shows nothing.
|
|
2083
|
+
const list = all.filter((word)=>!describing(word));
|
|
2084
|
+
if (!list.length || all.length > 3) return null;
|
|
2085
|
+
// A phrase of no thing ("up close") or a doing ("take pictures", "to lift", "work out") is not what it shows.
|
|
2086
|
+
if (list.every((word)=>STOPWORDS.has(word) && word !== 'people' && word !== 'person')) return null;
|
|
2087
|
+
if (/^(?:to|take|taking|make|making|get|do|go|lie)$/.test(list[0])) return null;
|
|
2088
|
+
if (/^(?:out|up|off|down|away|on|in)$/.test(list[list.length - 1])) return null;
|
|
2089
|
+
if (list.every((word)=>proper.has(word))) return list.map((word)=>`${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' ');
|
|
2090
|
+
const phrase = list.join(' ');
|
|
2091
|
+
const last = list[list.length - 1];
|
|
2092
|
+
const plural = aiStockStem(last) !== last && !/^(?:lens|canvas|atlas|gas|bus)$/.test(last);
|
|
2093
|
+
const uncounted = MASS_NOUNS.has(last) || last.endsWith('ing') && !COUNTED_ING.has(last);
|
|
2094
|
+
if (plural || uncounted || STOPWORDS.has(list[0]) && list[0] !== 'person') return phrase;
|
|
2095
|
+
return `${/^(?:[aeio]|u(?!ni|s[eu]))/.test(phrase) ? 'an' : 'a'} ${phrase}`;
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* A photo described from its own tags (AGL-3856): its first few that name a
|
|
2099
|
+
* thing, a phrase another one holds dropped ("wedding" beside "wedding
|
|
2100
|
+
* dress"), as an English phrase — "A wedding dress and a bride", "A hammer,
|
|
2101
|
+
* books and law" — never the comma-separated list the library keeps. `null`
|
|
2102
|
+
* where none names a thing.
|
|
2103
|
+
*/ export function aiStockTagsAlt(tags, proper = new Set()) {
|
|
2104
|
+
const kept = [];
|
|
2105
|
+
for (const tag of tags.slice(0, 6)){
|
|
2106
|
+
if (kept.length >= 3) break;
|
|
2107
|
+
const said = describedTag(tag, proper);
|
|
2108
|
+
if (!said) continue;
|
|
2109
|
+
const stems = stemList(said.replace(/^an?\s+/, ''));
|
|
2110
|
+
if (kept.some((entry)=>stems.every((stem)=>entry.stems.includes(stem)))) continue;
|
|
2111
|
+
// A longer phrase takes the place of one it holds: "wedding dress" for "wedding".
|
|
2112
|
+
const within = kept.findIndex((entry)=>entry.stems.every((stem)=>stems.includes(stem)));
|
|
2113
|
+
if (within >= 0) kept.splice(within, 1, {
|
|
2114
|
+
stems,
|
|
2115
|
+
said
|
|
2116
|
+
});
|
|
2117
|
+
else kept.push({
|
|
2118
|
+
stems,
|
|
2119
|
+
said
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
if (!kept.length) return null;
|
|
2123
|
+
const said = kept.map((entry)=>entry.said);
|
|
2124
|
+
const joined = said.length > 1 ? `${said.slice(0, -1).join(', ')} and ${said[said.length - 1]}` : said[0];
|
|
2125
|
+
const out = `${joined.charAt(0).toUpperCase()}${joined.slice(1)}`;
|
|
2126
|
+
return out.length > AI_STOCK_ALT_MAX ? out.slice(0, AI_STOCK_ALT_MAX).replace(/[\s,]+\S*$/, '') : out;
|
|
2127
|
+
}
|
|
2128
|
+
/** The thing a description names: its subject's object, a trailing "speaking calmly" or "dusted" dropped. */ function describedObject(text, sectionName) {
|
|
2129
|
+
const list = aiStockSubjectWords(text, sectionName).split(/\s+/).filter(Boolean);
|
|
2130
|
+
while(list.length > 1 && /(?:ing|ed|ly)$/.test(list[list.length - 1]))list.pop();
|
|
2131
|
+
return aiStockObjectWord(list.join(' '));
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* THE WORDS A PLACED STOCK PHOTO IS DESCRIBED BY (AGL-3805, AGL-3856).
|
|
1849
2135
|
*
|
|
1850
2136
|
* A slot's alt text is the model's, written for the picture the page asked
|
|
1851
2137
|
* for before any photo was found, and a slot is filled by whatever the
|
|
@@ -1859,37 +2145,44 @@ function jobPhotos(key) {
|
|
|
1859
2145
|
* dulce", a plate of macarons the shop's display case.
|
|
1860
2146
|
*
|
|
1861
2147
|
* So the photo placed answers for its own words. `null` keeps the slot's: on
|
|
1862
|
-
* an English site whose words name
|
|
1863
|
-
* subject (`aiStockSubjectWords
|
|
1864
|
-
*
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
2148
|
+
* an English site whose words name the thing the photo is OF — their
|
|
2149
|
+
* subject's object (`aiStockSubjectWords`, a trailing "speaking calmly"
|
|
2150
|
+
* dropped) among the hit's first two tags. Any word of the subject among its
|
|
2151
|
+
* first three was not enough: the beta.259 Hartwell Reyes Family Law hero kept
|
|
2152
|
+
* "Two attorneys speaking calmly with a client in a bright Columbus office"
|
|
2153
|
+
* for a typewriter whose third tag was "attorney" (AGL-3856). A stand-in's
|
|
2154
|
+
* words ({@link AiLayoutPictureSlot.standIn}) are a search's, never a
|
|
2155
|
+
* description, and are never kept. Otherwise the photo is described by the
|
|
1867
2156
|
* library's own words: its description where it writes one (a Pexels hit's
|
|
1868
|
-
* `alt`), else its first tags
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
2157
|
+
* `alt`), else its first tags as an English phrase ({@link aiStockTagsAlt}),
|
|
2158
|
+
* never the library's tag list. The libraries write in English, and no model
|
|
2159
|
+
* translates them, so a site in another language takes the platform's own
|
|
2160
|
+
* words for an illustrative photo in its language (`aiLayoutLabel`), true of
|
|
2161
|
+
* any photo, as does a hit whose words name no thing.
|
|
1872
2162
|
*/ export function aiStockPhotoAlt(photo, slot, input = {}) {
|
|
1873
|
-
var
|
|
1874
|
-
var _slot_product;
|
|
2163
|
+
var _photo_alt, _aiStockTagsAlt, _photo_tags;
|
|
1875
2164
|
if (aiLayoutSpeaksOtherThanEnglish(input.language)) return aiLayoutLabel('illustrativePhoto', input.language);
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
2165
|
+
if (!slot.standIn) {
|
|
2166
|
+
var _photo_tags1, _ref;
|
|
2167
|
+
var _slot_product;
|
|
2168
|
+
const first = new Set(stemList(((_photo_tags1 = photo.tags) != null ? _photo_tags1 : []).slice(0, 2).join(' ')));
|
|
2169
|
+
const objects = [
|
|
2170
|
+
slot.alt,
|
|
2171
|
+
...(_ref = (_slot_product = slot.product) == null ? void 0 : _slot_product.subjects) != null ? _ref : []
|
|
2172
|
+
].map((text)=>{
|
|
2173
|
+
var _input_sectionName;
|
|
2174
|
+
return describedObject(text, (_input_sectionName = input.sectionName) != null ? _input_sectionName : '');
|
|
2175
|
+
});
|
|
2176
|
+
if (objects.some((object)=>object && first.has(object))) return null;
|
|
2177
|
+
}
|
|
1885
2178
|
const own = ((_photo_alt = photo.alt) != null ? _photo_alt : '').replace(/\s+/g, ' ').trim();
|
|
1886
2179
|
if (own) return own.length > AI_STOCK_ALT_MAX ? `${own.slice(0, AI_STOCK_ALT_MAX).replace(/\s+\S*$/, '')}…` : own;
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
return
|
|
2180
|
+
return (_aiStockTagsAlt = aiStockTagsAlt((_photo_tags = photo.tags) != null ? _photo_tags : [], input.proper)) != null ? _aiStockTagsAlt : aiLayoutLabel('illustrativePhoto', input.language);
|
|
2181
|
+
}
|
|
2182
|
+
/** The names a business type writes with a capital, lowercased: "columbus", "ohio" (AGL-3856). */ export function aiStockProperWords(businessType) {
|
|
2183
|
+
const found = new Set();
|
|
2184
|
+
for (const [index, word] of businessType.split(/[^A-Za-zÀ-ɏ'-]+/).filter(Boolean).entries())if (index > 0 && /^[A-ZÀ-Þ]/.test(word)) found.add(word.toLowerCase());
|
|
2185
|
+
return found;
|
|
1893
2186
|
}
|
|
1894
2187
|
const EXTENSIONS = {
|
|
1895
2188
|
'image/jpeg': 'jpg',
|
|
@@ -1980,6 +2273,7 @@ const EXTENSIONS = {
|
|
|
1980
2273
|
// alt texts, in its own language, name no subject a search could use.
|
|
1981
2274
|
const english = aiStockSourceSubject(input);
|
|
1982
2275
|
const terms = aiStockSourceTerms(input);
|
|
2276
|
+
const proper = aiStockProperWords(input.business);
|
|
1983
2277
|
return async (slots)=>{
|
|
1984
2278
|
var _deps_budgetMs, _input_avoid;
|
|
1985
2279
|
const timeout = AbortSignal.timeout((_deps_budgetMs = deps.budgetMs) != null ? _deps_budgetMs : AI_LAYOUT_STOCK_PHOTOS_BUDGET_MS);
|
|
@@ -2038,7 +2332,8 @@ const EXTENSIONS = {
|
|
|
2038
2332
|
return aiStockPhotoAlt(photo, slot, _extends({}, input.language ? {
|
|
2039
2333
|
language: input.language
|
|
2040
2334
|
} : {}, {
|
|
2041
|
-
sectionName: (_input_sectionNames_slot_sectionIndex = input.sectionNames[slot.sectionIndex]) != null ? _input_sectionNames_slot_sectionIndex : ''
|
|
2335
|
+
sectionName: (_input_sectionNames_slot_sectionIndex = input.sectionNames[slot.sectionIndex]) != null ? _input_sectionNames_slot_sectionIndex : '',
|
|
2336
|
+
proper
|
|
2042
2337
|
}));
|
|
2043
2338
|
};
|
|
2044
2339
|
/** The photo placed in the site's library, a copy it holds or a new one; `null` when it cannot be placed. */ const keep = async (photo, slot, query, repeat = false)=>{
|
|
@@ -2135,6 +2430,8 @@ const EXTENSIONS = {
|
|
|
2135
2430
|
...(_request_shun = request.shun) != null ? _request_shun : []
|
|
2136
2431
|
])
|
|
2137
2432
|
]
|
|
2433
|
+
} : {}, terms.shunLead.length ? {
|
|
2434
|
+
shunLead: terms.shunLead
|
|
2138
2435
|
} : {}, request.product ? {
|
|
2139
2436
|
product: true
|
|
2140
2437
|
} : {}, ((_request_leadAny = request.leadAny) == null ? void 0 : _request_leadAny.length) ? {
|