@aglyn/plugins-ai 1.0.0-beta.247 → 1.0.0-beta.250

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.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { StockPhoto } from '@aglyn/aglyn/plugin-manager/stock-photo-provider';
18
+ /** `pan dulce conchas`: the Espiga Dorada hero and four other slots, answered 2026-10-13T20:16:23.066Z. */
19
+ export declare const PAN_DULCE_CONCHAS: StockPhoto[];
20
+ /** `bakery`: the Espiga Dorada slots that searched the subject, answered 2026-10-13T20:16:23.366Z. */
21
+ export declare const BAKERY: StockPhoto[];
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * PIXABAY'S RECORDED ANSWERS FOR THE beta.247 PANADERÍA RETEST (AGL-3769):
18
+ * what production's `stockPhotoSearches` cache held for the two searches
19
+ * Panaderia La Espiga Dorada's guided start (job Eynu8Iag2Z, 2026-10-13)
20
+ * sent, read back from the cache, never searched again. Ids, sizes, page
21
+ * slugs and tags are Pixabay's own, in its order; the contributor is left out.
22
+ *
23
+ * Its plan's photo words were "bakery pan dulce conchas": the subject first,
24
+ * as the tool asks, then two Spanish names spelled in English letters. Read
25
+ * as an English noun phrase, head last, the site's business became "pan dulce
26
+ * conchas" and its craft "concha", so the hero searched that and took
27
+ * 4965870, purple ceanothus flowers whose first tag is "ceanothus concha";
28
+ * the story section took 109343, "the concha bay" at San Sebastián, and
29
+ * another ceanothus (4955734) filled a cake slot. "bakery", the subject the
30
+ * words led with, was searched only as the bakery kin's broad search.
31
+ */ const hit = (id, width, height, slug, tags)=>({
32
+ provider: 'pixabay',
33
+ id: String(id),
34
+ width,
35
+ height,
36
+ pageUrl: `https://pixabay.com/photos/${slug}/`,
37
+ photographer: 'A Pixabay contributor',
38
+ tags: tags.split(', '),
39
+ downloadUrl: `https://pixabay.com/get/g${id}_1280.jpg`
40
+ });
41
+ /** `pan dulce conchas`: the Espiga Dorada hero and four other slots, answered 2026-10-13T20:16:23.066Z. */ export const PAN_DULCE_CONCHAS = [
42
+ hit(2011373, 1280, 878, 'deadvlei-namibia-africa-desert-2011373', 'deadvlei, namibia, africa, desert, dryness, tree, dead vlei, sand, dried up, nature, burned, namib'),
43
+ hit(3238173, 1280, 930, 'fried-egg-pan-meal-grill-egg-yolk-3238173', 'fried egg, pan, meal, grill, egg yolk, food, fried eggs, frying pan, protein, cook, yolk, eggs, kitchen, egg, fried, yummy, tasty, eggs, eggs, eggs'),
44
+ hit(3147645, 1280, 719, 'dulce-sweet-fruit-cake-pastry-3147645', 'dulce, sweet, fruit, cake, pastry, sugar, italy'),
45
+ hit(4506703, 1280, 720, 'dumpling-gyoza-frying-pan-skillet-4506703', 'dumpling, gyoza, frying pan, skillet, pan, gyoza, gyoza, gyoza, gyoza, gyoza'),
46
+ hit(9565430, 1280, 853, 'acacia-nature-tree-africa-etosha-9565430', 'acacia, nature, tree, africa, etosha, salt pan, scenery, panorama'),
47
+ hit(1979272, 1280, 853, 'happy-valentines-day-woman-man-1979272', 'happy valentines day, woman, man, kitchen, food preparation, couple, relationship, together, cooking, pair, cutting, chopping, everyday life, blonde, cutting board, vegetables, happy couple, stove, cook, knife'),
48
+ hit(5921491, 1280, 854, 'meal-food-tofu-rice-dish-cooked-5921491', 'meal, food, tofu, rice, dish, cooked, fried, tasty, delicious, cuisine, pan, bowls, kaiseki, flat lay, meal, food, tofu, tofu, rice, rice'),
49
+ hit(64310, 1280, 850, 'tree-desert-namibia-clay-pan-64310', 'tree, desert, namibia, nature, clay pan, drought, africa, sand, sky, dunes, outdoors, discovery'),
50
+ hit(8027678, 1280, 853, 'vegetable-pan-grilled-vegetables-8027678', 'vegetable pan, grilled vegetables, cooking, healthy, zucchini, broccoli, paprika, grilling, nourishment, cooking, cooking, cooking, healthy, healthy, healthy, healthy, healthy, zucchini'),
51
+ hit(7887696, 1280, 853, 'weight-metal-old-rusted-pan-weigh-7887696', 'weight, metal, old, rusted, pan, weigh, weight, weight, weight, weight, weight'),
52
+ hit(3060093, 1280, 854, 'salt-harvesting-vietnam-water-salt-3060093', 'salt harvesting, vietnam, water, salt, work, people, nature, reflection, hon khoi, salt field, salina, tropical, agriculture, asia, harvest, asian, hats, traditional'),
53
+ hit(9384171, 1280, 853, 'food-japanese-meal-class-b-gourmet-9384171', 'food, japanese meal, class b gourmet, takoyaki, cooking, shop'),
54
+ hit(8295778, 1280, 853, 'sweet-potatoes-potatoes-cook-meal-8295778', 'sweet potatoes, potatoes, cook, meal, food, grill, pan, food, food, food, food, food'),
55
+ hit(3619206, 1280, 847, 'central-america-guatemala-rio-dulce-3619206', 'central america, guatemala, rio dulce, water, heaven, nature, clouds, landscape'),
56
+ hit(6286592, 1280, 853, 'meatballs-cook-frying-pan-6286592', 'meatballs, cook, frying pan, minced meat, meat, food, meal, delicious, fry, sear, pan, closeup, meatballs, meatballs, meatballs, meatballs, meatballs'),
57
+ hit(4965870, 1280, 853, 'ceanothus-concha-california-4965870', 'ceanothus concha, nature, california, native plants, spring, flower background, flowers, flower wallpaper, beautiful flowers, purple'),
58
+ hit(4216459, 1280, 853, 'spain-salamanca-architecture-old-4216459', 'spain, salamanca, architecture, old, history, europe, tourism, church, casa conchas, salamanca, salamanca, salamanca, salamanca, salamanca'),
59
+ hit(3619207, 1280, 848, 'central-america-guatemala-rio-dulce-3619207', 'central america, guatemala, rio dulce, water, nature, heaven, clouds, landscape'),
60
+ hit(1835369, 1280, 726, 'cooking-wok-chinese-asian-food-1835369', 'cooking, wok, chinese, asian, food, frying pan, meal, pan, vegetables, stir fry, frying, cook, cooking food, cooking vegetables, cooking, cooking, cooking, cooking, cooking, wok'),
61
+ hit(4813261, 1280, 853, 'meat-duck-duck-breast-raw-fry-4813261', 'meat, duck, duck breast, raw, fry, court, tasty, fried, sear, recipe, roast duck, food, poultry, food photo, kitchen, cook, meat, meat, meat, duck'),
62
+ hit(4644941, 1280, 853, 'fat-frying-pan-4644941', 'fat, frying, pan'),
63
+ hit(3246189, 1280, 853, 'food-pan-cooking-frying-pan-meal-3246189', 'food, pan, cooking, frying pan, meal, camping, breakfast, fire pit, morning, cook, outdoors, pit, fire, campfire, grill, scrambled, smoke, spatula, outside, skillet'),
64
+ hit(6380762, 1280, 853, 'panpipe-musician-guatemalan-6380762', 'panpipe, musician, guatemalan, traditional costume, culture, man, pan flute, instrument, music, musical instrument, portrait, musician, musician, culture, pan flute, music, music, music, music, music'),
65
+ hit(2119027, 1280, 853, 'country-life-pan-kitchen-2119027', 'country life, pan, kitchen, preparation, frying pan, farmer, kitchen appliances, farm yard, country life, country life, pan, kitchen, kitchen, kitchen, kitchen, farmer, kitchen appliances, kitchen appliances, kitchen appliances, kitchen appliances'),
66
+ hit(2106825, 1280, 853, 'carrots-yellow-beets-vegetables-2106825', 'carrots, yellow beets, vegetables, carrot, food, vitamins, meal, fresh, bio, cook, yummy, vegetarian, vegetable plants, market fresh vegetables, orange, healthy, nourishment, vegan, ingredients, vegetable pan'),
67
+ hit(3125689, 1280, 853, 'cook-figure-fried-egg-food-3125689', 'cook, figure, fried egg, food, frying pan, fried eggs, pan, yummy, eggs, grill, egg yolk, yolk, kitchen, fun'),
68
+ hit(3619208, 1280, 847, 'central-america-guatemale-rio-dulce-3619208', 'central america, guatemale, rio dulce, water, heaven, clouds, nature, landscape'),
69
+ hit(3619205, 1280, 847, 'central-america-guatemala-rio-dulce-3619205', 'central america, guatemala, rio dulce, nature, water, heaven, clouds, landscape'),
70
+ hit(3941617, 1280, 853, 'meal-pan-chicken-tomato-3941617', 'meal, pan, chicken, tomato'),
71
+ hit(109343, 1280, 860, 'the-concha-bay-san-sebastian-lights-109343', 'the concha bay, san sebastian, lights, highlights, san sebastian, san sebastian, san sebastian, san sebastian, san sebastian'),
72
+ hit(1583152, 1280, 905, 'pan-vegetables-cook-1583152', 'pan, vegetables, cook, pan, pan, pan, pan, pan, cook'),
73
+ hit(6941781, 1280, 854, 'roulades-beef-bacon-pan-grill-6941781', 'roulades, beef, bacon, pan, grill, cooking, bacon, bacon, bacon, bacon, bacon'),
74
+ hit(4955734, 1280, 853, 'ceanothus-concha-bee-pollen-4955734', 'ceanothus concha, bee, pollen, pollinator, native plants, spring, nature, purple, blue bee'),
75
+ hit(5979722, 1280, 853, 'cooking-butter-pan-frying-pan-cook-5979722', 'cooking, butter, pan, frying pan, cook, food prepartaion, kitchen, dairy, pan, frying pan, frying pan, frying pan, frying pan, frying pan'),
76
+ hit(4644950, 1280, 853, 'frying-pan-fat-4644950', 'frying, pan, fat'),
77
+ hit(5909434, 1280, 853, 'breakfast-bacon-cast-iron-skillet-5909434', 'breakfast, bacon, cast iron, skillet, fying pan, frying, cooking, pan fry, meal, meat, delicious, tasty, pork, bacon strips, food, cooking food, bacon, bacon, skillet, skillet'),
78
+ hit(5003297, 1280, 853, 'butter-pan-cook-frying-pan-grill-5003297', 'butter, pan, cook, frying pan, grill, oil, fat, stove'),
79
+ hit(3834178, 1280, 733, 'cakes-cake-mould-springform-pan-3834178', 'cakes, cake mould, springform pan, baking pan, to bake, apples, apple cake, pastries, baked, kitchen, food, meal, yummy, cake mould, apple cake, apple cake, apple cake, apple cake, apple cake'),
80
+ hit(3125697, 1280, 793, 'fried-egg-pan-meal-grill-egg-yolk-3125697', 'fried egg, pan, meal, grill, egg yolk, food, fried eggs, frying pan, protein, cook, yolk, eggs, kitchen, egg, fried, yummy, tasty, fried egg, fried egg, fried egg'),
81
+ hit(1195362, 1280, 853, 'royal-fan-court-frying-pan-1195362', 'royal fan, court, frying pan, frying pan, frying pan, frying pan, frying pan, frying pan')
82
+ ];
83
+ /** `bakery`: the Espiga Dorada slots that searched the subject, answered 2026-10-13T20:16:23.366Z. */ export const BAKERY = [
84
+ hit(1867459, 1280, 853, 'breads-pastries-croissants-1867459', 'breads, pastries, croissants, puff pastries, danish, baked goods, puff pastry, bakery, breakfast, delicious, food, fresh pastries, pastry shop, bakeshop, restaurant, shop, tasty, variety, bread display, bakery'),
85
+ hit(6245204, 1280, 853, 'bread-basket-food-breakfast-snack-6245204', 'bread, basket, food, breakfast, snack, baked, delicious, bakery, closeup'),
86
+ hit(8535650, 1280, 853, 'bread-sourdough-flaxseed-seeds-8535650', 'bread, sourdough, flaxseed, seeds, healthy, bake, homemade, breakfast, bakery, flour, bread, bread, breakfast, bakery, bakery, bakery, bakery, bakery, flour'),
87
+ hit(3946227, 1280, 853, 'cake-homemade-dessert-roasted-3946227', 'cake, homemade, dessert, roasted, bakery, tasty, sweetness, party, cake, cake, cake, cake, cake'),
88
+ hit(5470532, 1280, 853, 'bread-bakery-baked-flour-braided-5470532', 'bread, bakery, baked, flour, braided, food, fresh, challah, traditional, bread, bread, bread, bread, bread, bakery, bakery, bakery, bakery, challah'),
89
+ hit(1284438, 1280, 719, 'bread-croissant-morning-puff-paste-1284438', 'bread, croissant, morning, puff paste, puff pastry, flaky pastry, bakery, breakfast, dessert, sweet, snack, homemade, delicious, cuisine, tasty, pie, bake, pastry, bun, nutrition'),
90
+ hit(8447394, 1280, 853, 'bun-sesame-baked-goods-baked-brown-8447394', 'bun, sesame, baked goods, baked, brown, crust, fresh, grain bread, food, bakery, healthy, crispy, sesame, baked goods, food, food, food, bakery, bakery, bakery'),
91
+ hit(1868396, 1280, 852, 'bakery-bread-baker-apron-baking-1868396', 'bakery, bread, baker, apron, baking, chef, cook, dough, flour, food, bakery, bakery, bakery, bakery, bread, bread, bread, bread, bread, baker'),
92
+ hit(8233676, 1280, 960, 'cake-berries-dessert-sweet-8233676', 'cake, berries, dessert, sweet, chocolate, chocolate cake, rum cake, bakery, birthday cake, raspberries, blueberries, fruit, round cake, figs, food, cake, cake, cake, cake, cake'),
93
+ hit(8503298, 1280, 853, 'bread-loaf-chef-cook-food-bakery-8503298', 'bread, loaf, chef, cook, food, bakery, bake, baking, bread, bread, bread, bread, bread, chef, bakery'),
94
+ hit(5116009, 1280, 854, 'cupcakes-chocolate-muffin-food-5116009', 'cupcakes, chocolate, muffin, food, sweet, baking, delicious, bakery, pastry, icing, frosting, cocoa, simple, white, cupcakes, cupcakes, cupcakes, cupcakes, cupcakes, chocolate'),
95
+ hit(1971552, 1280, 871, 'cake-piece-plate-dessert-pastry-1971552', 'cake, piece, plate, dessert, pastry, sweet, chocolate, cake slice, piece of cake, confectionery, bake, food, delicious, sweets, calories, eat, food photography, cake, cake, cake'),
96
+ hit(3216243, 1280, 853, 'cookie-biscuit-round-sweet-snack-3216243', 'cookie, biscuit, round, sweet, snack, carbohydrate, sugar, bakery, unhealthy, baked, treat, smile, jolly jammers tm, fattening, cookie, cookie, cookie, cookie, cookie, biscuit'),
97
+ hit(380178, 1280, 851, 'cupcakes-candles-birthday-380178', 'cupcakes, candles, candlelight, birthday candles, bithday celebration, bithday party, birthday cake, cake, party, happy birthday, celebrate, flame'),
98
+ hit(2918172, 1280, 875, 'christmas-cookies-xmas-christmas-2918172', 'christmas cookies, xmas, christmas, gingerbread, decoration, food, sweets, dessert, yummy, festive, celebrate, delicious, holiday, cookies, christmas cookies, christmas cookies, christmas, christmas, christmas, christmas'),
99
+ hit(1281053, 1280, 857, 'bread-baked-food-freshly-baked-1281053', 'bread, baked, food, freshly baked, artisan bread, dough, homemade, homemade bread, carbohydrates, eat, food photography, bread, bread, bread, bread, bread'),
100
+ hit(1868573, 1280, 853, 'doughnuts-desserts-pastries-treats-1868573', 'doughnuts, desserts, pastries, treats, sweets, pastry shop, dessert shop, assorted, assortment, display, shop, store, baked goods, bake shop, baked, bakery, buffet, chocolate, confectionery, delicious'),
101
+ hit(3641851, 1280, 936, 'plums-crumble-cake-pastry-3641851', 'plums, crumble cake, pastry, streuselkuchen, food, sweet, pie, cake, fruit cake, plum cake, sweet dish, fruits, baked, baked goods, fruity, delicious, tasty, closeup, pie, pie'),
102
+ hit(399286, 1280, 853, 'bread-food-baked-artisan-breads-399286', 'bread, food, baked, artisan breads, homemade bread, freshly baked, carbohydrates, dough, baked goods, fresh, tasty, eat, food photography, bread, bread, bread, bread, bread'),
103
+ hit(1283821, 1280, 764, 'cupcakes-fruits-dessert-food-1283821', 'cupcakes, fruits, dessert, food, pastry, cakes, berry, sweet, delicious, baked, bakery, cupcakes, dessert, dessert, dessert, dessert, food, pastry, cakes, cakes'),
104
+ hit(599521, 1280, 853, 'bake-butter-flour-pile-599521', 'bake, butter, flour, pile, food preparation, baking, dough, sweets, pastries, sugar, flour, flour, flour, flour, flour, baking, baking, baking, sugar, sugar'),
105
+ hit(1850628, 1280, 852, 'cupcakes-muffins-marshmallows-icing-1850628', 'cupcakes, muffins, marshmallows, icing, frosting, pastries, desserts, sweets, plate, treats, baked, homemade, homemade cupcakes, baked goods, baking, delicious, food, food photography, cupcakes, cupcakes'),
106
+ hit(1850011, 1280, 854, 'cake-chocolate-chocolate-cake-1850011', 'cake, chocolate, chocolate cake, sliced cake, cake slice, dessert, sweet, food, eat, food photography, delicious, indulgence, pastry, plate, sinful cake, slice, cake, cake, cake, cake'),
107
+ hit(7445434, 1280, 865, 'loaf-evening-bread-bread-crust-7445434', 'loaf, evening bread, bread, crust, breakfast, abendbrot, grain bread, sesame seeds, bread, bread, bread, bread, bread'),
108
+ hit(943245, 1280, 837, 'new-year-background-dough-cook-943245', 'new year background, dough, cook, recipe, italian, flour, kitchen, preparation, white, ingredient, bakery, homemade, preparing, cuisine, pastry, bread, baking, pizza, traditional'),
109
+ hit(3257019, 1280, 853, 'cakes-dessert-sugar-muffin-cupcake-3257019', 'cakes, dessert, sugar, muffin, cupcake, sweets, sprinkles, bakery, pink, meal, cakes, cakes, cakes, cakes, sugar, sugar, cupcake, cupcake, cupcake, bakery'),
110
+ hit(1868925, 1280, 948, 'bakery-breads-assorted-assortment-1868925', 'bakery, breads, assorted, assortment, bread shop, food, pastries, shop, store, freshly baked, bakery, bakery, bakery, bakery, bakery, shop'),
111
+ hit(821503, 1280, 853, 'bread-baking-fresh-home-made-food-821503', 'bread, baking, fresh, home made, food, bakery, healthy, flour, traditional, cuisine, homemade, loaf, eating, bread, bread, bread, bread, bread, baking'),
112
+ hit(2285209, 1280, 853, 'cupcakes-muffins-dessert-food-cake-2285209', 'cupcakes, muffins, dessert, food, cake, sweet, frosting, icing, cupcakes, cupcakes, cake, cake, cake, cake, cake'),
113
+ hit(9865309, 1280, 791, 'sourdough-bread-food-homemade-9865309', 'sourdough, bread, food, homemade, baking, fresh, baked, wheat, traditional, crust'),
114
+ hit(4840960, 1280, 853, 'baker-bread-bake-nutrition-bakery-4840960', 'baker, bread, bake, nutrition, bakery, fresh, homemade, food, craftsmen, flour, baked, delicious, baker, baker, baker, baker, bread, bread, bread, bakery'),
115
+ hit(9424463, 1280, 853, 'bun-food-fresh-bakery-9424463', 'bun, food, fresh, bakery, carbohydrates, to bake, dough, homemade, meal, yummy, nourishment, close up'),
116
+ hit(2436370, 1280, 853, 'loaf-bakery-preparation-fresh-bread-2436370', 'loaf, bakery, preparation, fresh bread, baker, meal, nourishment, baguette, dough, flour, craft, authentic, rye bread, wheat bread, to bake, biological, bakery, baguette, baguette, baguette'),
117
+ hit(2193537, 1280, 853, 'bread-loaves-crust-freshly-baked-2193537', 'bread, loaves, crust, freshly baked, bakery, bread crust, crispy, fresh, food, baked goods, homemade, baked bread, baked, flour, bread, bread, bread, bread, bread'),
118
+ hit(2178371, 1280, 853, 'macaroons-dessert-sweets-snacks-2178371', 'macaroons, dessert, sweets, snacks, colors, food, dessert, dessert, dessert, dessert, dessert, sweets, food'),
119
+ hit(8128550, 1280, 921, 'bread-loaves-food-bakery-craft-8128550', 'bread, loaves, food, bakery, craft, shelf, bread, bread, bread, bread, bread, bakery, bakery'),
120
+ hit(6562091, 1280, 853, 'croissant-bread-pastry-breakfast-6562091', 'croissant, bread, pastry, breakfast, raspberry, dish, berry, croissant, croissant, croissant, croissant, bread, breakfast, breakfast, breakfast, breakfast, breakfast'),
121
+ hit(279523, 1280, 763, 'cupcake-cake-sweets-sugar-carrot-279523', 'cupcake, cake, sweets, sugar, carrot, creme, candy, dough, dessert, brown, pastry, cupcake, cupcake, cake, cake, cake, cake, cake'),
122
+ hit(1869969, 1280, 851, 'book-diary-notes-notepad-paper-1869969', 'book, diary, notes, notepad, paper, still life, write, blank pages, open notebook, journal, notebook, empty pages, pages, book, book, book, book, book, diary, paper'),
123
+ hit(1868181, 1280, 853, 'desserts-pastries-shop-pastry-shop-1868181', 'desserts, pastries, shop, pastry shop, food, bakeshop, bakery, store, sweets, macaroons, pralines, chocolate candy, confections, bakery, bakery, bakery, bakery, bakery')
124
+ ];
125
+
126
+ //# sourceMappingURL=ai-stock-photo-agl3769-recordings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/ai/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { StockPhoto } from '@aglyn/aglyn/plugin-manager/stock-photo-provider'\n\n/**\n * PIXABAY'S RECORDED ANSWERS FOR THE beta.247 PANADERÍA RETEST (AGL-3769):\n * what production's `stockPhotoSearches` cache held for the two searches\n * Panaderia La Espiga Dorada's guided start (job Eynu8Iag2Z, 2026-10-13)\n * sent, read back from the cache, never searched again. Ids, sizes, page\n * slugs and tags are Pixabay's own, in its order; the contributor is left out.\n *\n * Its plan's photo words were \"bakery pan dulce conchas\": the subject first,\n * as the tool asks, then two Spanish names spelled in English letters. Read\n * as an English noun phrase, head last, the site's business became \"pan dulce\n * conchas\" and its craft \"concha\", so the hero searched that and took\n * 4965870, purple ceanothus flowers whose first tag is \"ceanothus concha\";\n * the story section took 109343, \"the concha bay\" at San Sebastián, and\n * another ceanothus (4955734) filled a cake slot. \"bakery\", the subject the\n * words led with, was searched only as the bakery kin's broad search.\n */\n\nconst hit = (\n id: number,\n width: number,\n height: number,\n slug: string,\n tags: string,\n): StockPhoto =>\n ({\n provider: 'pixabay',\n id: String(id),\n width,\n height,\n pageUrl: `https://pixabay.com/photos/${slug}/`,\n photographer: 'A Pixabay contributor',\n tags: tags.split(', '),\n downloadUrl: `https://pixabay.com/get/g${id}_1280.jpg`,\n }) as StockPhoto\n\n/** `pan dulce conchas`: the Espiga Dorada hero and four other slots, answered 2026-10-13T20:16:23.066Z. */\nexport const PAN_DULCE_CONCHAS: StockPhoto[] = [\n hit(\n 2011373,\n 1280,\n 878,\n 'deadvlei-namibia-africa-desert-2011373',\n 'deadvlei, namibia, africa, desert, dryness, tree, dead vlei, sand, dried up, nature, burned, namib',\n ),\n hit(\n 3238173,\n 1280,\n 930,\n 'fried-egg-pan-meal-grill-egg-yolk-3238173',\n 'fried egg, pan, meal, grill, egg yolk, food, fried eggs, frying pan, protein, cook, yolk, eggs, kitchen, egg, fried, yummy, tasty, eggs, eggs, eggs',\n ),\n hit(\n 3147645,\n 1280,\n 719,\n 'dulce-sweet-fruit-cake-pastry-3147645',\n 'dulce, sweet, fruit, cake, pastry, sugar, italy',\n ),\n hit(\n 4506703,\n 1280,\n 720,\n 'dumpling-gyoza-frying-pan-skillet-4506703',\n 'dumpling, gyoza, frying pan, skillet, pan, gyoza, gyoza, gyoza, gyoza, gyoza',\n ),\n hit(\n 9565430,\n 1280,\n 853,\n 'acacia-nature-tree-africa-etosha-9565430',\n 'acacia, nature, tree, africa, etosha, salt pan, scenery, panorama',\n ),\n hit(\n 1979272,\n 1280,\n 853,\n 'happy-valentines-day-woman-man-1979272',\n 'happy valentines day, woman, man, kitchen, food preparation, couple, relationship, together, cooking, pair, cutting, chopping, everyday life, blonde, cutting board, vegetables, happy couple, stove, cook, knife',\n ),\n hit(\n 5921491,\n 1280,\n 854,\n 'meal-food-tofu-rice-dish-cooked-5921491',\n 'meal, food, tofu, rice, dish, cooked, fried, tasty, delicious, cuisine, pan, bowls, kaiseki, flat lay, meal, food, tofu, tofu, rice, rice',\n ),\n hit(\n 64310,\n 1280,\n 850,\n 'tree-desert-namibia-clay-pan-64310',\n 'tree, desert, namibia, nature, clay pan, drought, africa, sand, sky, dunes, outdoors, discovery',\n ),\n hit(\n 8027678,\n 1280,\n 853,\n 'vegetable-pan-grilled-vegetables-8027678',\n 'vegetable pan, grilled vegetables, cooking, healthy, zucchini, broccoli, paprika, grilling, nourishment, cooking, cooking, cooking, healthy, healthy, healthy, healthy, healthy, zucchini',\n ),\n hit(\n 7887696,\n 1280,\n 853,\n 'weight-metal-old-rusted-pan-weigh-7887696',\n 'weight, metal, old, rusted, pan, weigh, weight, weight, weight, weight, weight',\n ),\n hit(\n 3060093,\n 1280,\n 854,\n 'salt-harvesting-vietnam-water-salt-3060093',\n 'salt harvesting, vietnam, water, salt, work, people, nature, reflection, hon khoi, salt field, salina, tropical, agriculture, asia, harvest, asian, hats, traditional',\n ),\n hit(\n 9384171,\n 1280,\n 853,\n 'food-japanese-meal-class-b-gourmet-9384171',\n 'food, japanese meal, class b gourmet, takoyaki, cooking, shop',\n ),\n hit(\n 8295778,\n 1280,\n 853,\n 'sweet-potatoes-potatoes-cook-meal-8295778',\n 'sweet potatoes, potatoes, cook, meal, food, grill, pan, food, food, food, food, food',\n ),\n hit(\n 3619206,\n 1280,\n 847,\n 'central-america-guatemala-rio-dulce-3619206',\n 'central america, guatemala, rio dulce, water, heaven, nature, clouds, landscape',\n ),\n hit(\n 6286592,\n 1280,\n 853,\n 'meatballs-cook-frying-pan-6286592',\n 'meatballs, cook, frying pan, minced meat, meat, food, meal, delicious, fry, sear, pan, closeup, meatballs, meatballs, meatballs, meatballs, meatballs',\n ),\n hit(\n 4965870,\n 1280,\n 853,\n 'ceanothus-concha-california-4965870',\n 'ceanothus concha, nature, california, native plants, spring, flower background, flowers, flower wallpaper, beautiful flowers, purple',\n ),\n hit(\n 4216459,\n 1280,\n 853,\n 'spain-salamanca-architecture-old-4216459',\n 'spain, salamanca, architecture, old, history, europe, tourism, church, casa conchas, salamanca, salamanca, salamanca, salamanca, salamanca',\n ),\n hit(\n 3619207,\n 1280,\n 848,\n 'central-america-guatemala-rio-dulce-3619207',\n 'central america, guatemala, rio dulce, water, nature, heaven, clouds, landscape',\n ),\n hit(\n 1835369,\n 1280,\n 726,\n 'cooking-wok-chinese-asian-food-1835369',\n 'cooking, wok, chinese, asian, food, frying pan, meal, pan, vegetables, stir fry, frying, cook, cooking food, cooking vegetables, cooking, cooking, cooking, cooking, cooking, wok',\n ),\n hit(\n 4813261,\n 1280,\n 853,\n 'meat-duck-duck-breast-raw-fry-4813261',\n 'meat, duck, duck breast, raw, fry, court, tasty, fried, sear, recipe, roast duck, food, poultry, food photo, kitchen, cook, meat, meat, meat, duck',\n ),\n hit(\n 4644941,\n 1280,\n 853,\n 'fat-frying-pan-4644941',\n 'fat, frying, pan',\n ),\n hit(\n 3246189,\n 1280,\n 853,\n 'food-pan-cooking-frying-pan-meal-3246189',\n 'food, pan, cooking, frying pan, meal, camping, breakfast, fire pit, morning, cook, outdoors, pit, fire, campfire, grill, scrambled, smoke, spatula, outside, skillet',\n ),\n hit(\n 6380762,\n 1280,\n 853,\n 'panpipe-musician-guatemalan-6380762',\n 'panpipe, musician, guatemalan, traditional costume, culture, man, pan flute, instrument, music, musical instrument, portrait, musician, musician, culture, pan flute, music, music, music, music, music',\n ),\n hit(\n 2119027,\n 1280,\n 853,\n 'country-life-pan-kitchen-2119027',\n 'country life, pan, kitchen, preparation, frying pan, farmer, kitchen appliances, farm yard, country life, country life, pan, kitchen, kitchen, kitchen, kitchen, farmer, kitchen appliances, kitchen appliances, kitchen appliances, kitchen appliances',\n ),\n hit(\n 2106825,\n 1280,\n 853,\n 'carrots-yellow-beets-vegetables-2106825',\n 'carrots, yellow beets, vegetables, carrot, food, vitamins, meal, fresh, bio, cook, yummy, vegetarian, vegetable plants, market fresh vegetables, orange, healthy, nourishment, vegan, ingredients, vegetable pan',\n ),\n hit(\n 3125689,\n 1280,\n 853,\n 'cook-figure-fried-egg-food-3125689',\n 'cook, figure, fried egg, food, frying pan, fried eggs, pan, yummy, eggs, grill, egg yolk, yolk, kitchen, fun',\n ),\n hit(\n 3619208,\n 1280,\n 847,\n 'central-america-guatemale-rio-dulce-3619208',\n 'central america, guatemale, rio dulce, water, heaven, clouds, nature, landscape',\n ),\n hit(\n 3619205,\n 1280,\n 847,\n 'central-america-guatemala-rio-dulce-3619205',\n 'central america, guatemala, rio dulce, nature, water, heaven, clouds, landscape',\n ),\n hit(\n 3941617,\n 1280,\n 853,\n 'meal-pan-chicken-tomato-3941617',\n 'meal, pan, chicken, tomato',\n ),\n hit(\n 109343,\n 1280,\n 860,\n 'the-concha-bay-san-sebastian-lights-109343',\n 'the concha bay, san sebastian, lights, highlights, san sebastian, san sebastian, san sebastian, san sebastian, san sebastian',\n ),\n hit(\n 1583152,\n 1280,\n 905,\n 'pan-vegetables-cook-1583152',\n 'pan, vegetables, cook, pan, pan, pan, pan, pan, cook',\n ),\n hit(\n 6941781,\n 1280,\n 854,\n 'roulades-beef-bacon-pan-grill-6941781',\n 'roulades, beef, bacon, pan, grill, cooking, bacon, bacon, bacon, bacon, bacon',\n ),\n hit(\n 4955734,\n 1280,\n 853,\n 'ceanothus-concha-bee-pollen-4955734',\n 'ceanothus concha, bee, pollen, pollinator, native plants, spring, nature, purple, blue bee',\n ),\n hit(\n 5979722,\n 1280,\n 853,\n 'cooking-butter-pan-frying-pan-cook-5979722',\n 'cooking, butter, pan, frying pan, cook, food prepartaion, kitchen, dairy, pan, frying pan, frying pan, frying pan, frying pan, frying pan',\n ),\n hit(\n 4644950,\n 1280,\n 853,\n 'frying-pan-fat-4644950',\n 'frying, pan, fat',\n ),\n hit(\n 5909434,\n 1280,\n 853,\n 'breakfast-bacon-cast-iron-skillet-5909434',\n 'breakfast, bacon, cast iron, skillet, fying pan, frying, cooking, pan fry, meal, meat, delicious, tasty, pork, bacon strips, food, cooking food, bacon, bacon, skillet, skillet',\n ),\n hit(\n 5003297,\n 1280,\n 853,\n 'butter-pan-cook-frying-pan-grill-5003297',\n 'butter, pan, cook, frying pan, grill, oil, fat, stove',\n ),\n hit(\n 3834178,\n 1280,\n 733,\n 'cakes-cake-mould-springform-pan-3834178',\n 'cakes, cake mould, springform pan, baking pan, to bake, apples, apple cake, pastries, baked, kitchen, food, meal, yummy, cake mould, apple cake, apple cake, apple cake, apple cake, apple cake',\n ),\n hit(\n 3125697,\n 1280,\n 793,\n 'fried-egg-pan-meal-grill-egg-yolk-3125697',\n 'fried egg, pan, meal, grill, egg yolk, food, fried eggs, frying pan, protein, cook, yolk, eggs, kitchen, egg, fried, yummy, tasty, fried egg, fried egg, fried egg',\n ),\n hit(\n 1195362,\n 1280,\n 853,\n 'royal-fan-court-frying-pan-1195362',\n 'royal fan, court, frying pan, frying pan, frying pan, frying pan, frying pan, frying pan',\n ),\n]\n\n/** `bakery`: the Espiga Dorada slots that searched the subject, answered 2026-10-13T20:16:23.366Z. */\nexport const BAKERY: StockPhoto[] = [\n hit(\n 1867459,\n 1280,\n 853,\n 'breads-pastries-croissants-1867459',\n 'breads, pastries, croissants, puff pastries, danish, baked goods, puff pastry, bakery, breakfast, delicious, food, fresh pastries, pastry shop, bakeshop, restaurant, shop, tasty, variety, bread display, bakery',\n ),\n hit(\n 6245204,\n 1280,\n 853,\n 'bread-basket-food-breakfast-snack-6245204',\n 'bread, basket, food, breakfast, snack, baked, delicious, bakery, closeup',\n ),\n hit(\n 8535650,\n 1280,\n 853,\n 'bread-sourdough-flaxseed-seeds-8535650',\n 'bread, sourdough, flaxseed, seeds, healthy, bake, homemade, breakfast, bakery, flour, bread, bread, breakfast, bakery, bakery, bakery, bakery, bakery, flour',\n ),\n hit(\n 3946227,\n 1280,\n 853,\n 'cake-homemade-dessert-roasted-3946227',\n 'cake, homemade, dessert, roasted, bakery, tasty, sweetness, party, cake, cake, cake, cake, cake',\n ),\n hit(\n 5470532,\n 1280,\n 853,\n 'bread-bakery-baked-flour-braided-5470532',\n 'bread, bakery, baked, flour, braided, food, fresh, challah, traditional, bread, bread, bread, bread, bread, bakery, bakery, bakery, bakery, challah',\n ),\n hit(\n 1284438,\n 1280,\n 719,\n 'bread-croissant-morning-puff-paste-1284438',\n 'bread, croissant, morning, puff paste, puff pastry, flaky pastry, bakery, breakfast, dessert, sweet, snack, homemade, delicious, cuisine, tasty, pie, bake, pastry, bun, nutrition',\n ),\n hit(\n 8447394,\n 1280,\n 853,\n 'bun-sesame-baked-goods-baked-brown-8447394',\n 'bun, sesame, baked goods, baked, brown, crust, fresh, grain bread, food, bakery, healthy, crispy, sesame, baked goods, food, food, food, bakery, bakery, bakery',\n ),\n hit(\n 1868396,\n 1280,\n 852,\n 'bakery-bread-baker-apron-baking-1868396',\n 'bakery, bread, baker, apron, baking, chef, cook, dough, flour, food, bakery, bakery, bakery, bakery, bread, bread, bread, bread, bread, baker',\n ),\n hit(\n 8233676,\n 1280,\n 960,\n 'cake-berries-dessert-sweet-8233676',\n 'cake, berries, dessert, sweet, chocolate, chocolate cake, rum cake, bakery, birthday cake, raspberries, blueberries, fruit, round cake, figs, food, cake, cake, cake, cake, cake',\n ),\n hit(\n 8503298,\n 1280,\n 853,\n 'bread-loaf-chef-cook-food-bakery-8503298',\n 'bread, loaf, chef, cook, food, bakery, bake, baking, bread, bread, bread, bread, bread, chef, bakery',\n ),\n hit(\n 5116009,\n 1280,\n 854,\n 'cupcakes-chocolate-muffin-food-5116009',\n 'cupcakes, chocolate, muffin, food, sweet, baking, delicious, bakery, pastry, icing, frosting, cocoa, simple, white, cupcakes, cupcakes, cupcakes, cupcakes, cupcakes, chocolate',\n ),\n hit(\n 1971552,\n 1280,\n 871,\n 'cake-piece-plate-dessert-pastry-1971552',\n 'cake, piece, plate, dessert, pastry, sweet, chocolate, cake slice, piece of cake, confectionery, bake, food, delicious, sweets, calories, eat, food photography, cake, cake, cake',\n ),\n hit(\n 3216243,\n 1280,\n 853,\n 'cookie-biscuit-round-sweet-snack-3216243',\n 'cookie, biscuit, round, sweet, snack, carbohydrate, sugar, bakery, unhealthy, baked, treat, smile, jolly jammers tm, fattening, cookie, cookie, cookie, cookie, cookie, biscuit',\n ),\n hit(\n 380178,\n 1280,\n 851,\n 'cupcakes-candles-birthday-380178',\n 'cupcakes, candles, candlelight, birthday candles, bithday celebration, bithday party, birthday cake, cake, party, happy birthday, celebrate, flame',\n ),\n hit(\n 2918172,\n 1280,\n 875,\n 'christmas-cookies-xmas-christmas-2918172',\n 'christmas cookies, xmas, christmas, gingerbread, decoration, food, sweets, dessert, yummy, festive, celebrate, delicious, holiday, cookies, christmas cookies, christmas cookies, christmas, christmas, christmas, christmas',\n ),\n hit(\n 1281053,\n 1280,\n 857,\n 'bread-baked-food-freshly-baked-1281053',\n 'bread, baked, food, freshly baked, artisan bread, dough, homemade, homemade bread, carbohydrates, eat, food photography, bread, bread, bread, bread, bread',\n ),\n hit(\n 1868573,\n 1280,\n 853,\n 'doughnuts-desserts-pastries-treats-1868573',\n 'doughnuts, desserts, pastries, treats, sweets, pastry shop, dessert shop, assorted, assortment, display, shop, store, baked goods, bake shop, baked, bakery, buffet, chocolate, confectionery, delicious',\n ),\n hit(\n 3641851,\n 1280,\n 936,\n 'plums-crumble-cake-pastry-3641851',\n 'plums, crumble cake, pastry, streuselkuchen, food, sweet, pie, cake, fruit cake, plum cake, sweet dish, fruits, baked, baked goods, fruity, delicious, tasty, closeup, pie, pie',\n ),\n hit(\n 399286,\n 1280,\n 853,\n 'bread-food-baked-artisan-breads-399286',\n 'bread, food, baked, artisan breads, homemade bread, freshly baked, carbohydrates, dough, baked goods, fresh, tasty, eat, food photography, bread, bread, bread, bread, bread',\n ),\n hit(\n 1283821,\n 1280,\n 764,\n 'cupcakes-fruits-dessert-food-1283821',\n 'cupcakes, fruits, dessert, food, pastry, cakes, berry, sweet, delicious, baked, bakery, cupcakes, dessert, dessert, dessert, dessert, food, pastry, cakes, cakes',\n ),\n hit(\n 599521,\n 1280,\n 853,\n 'bake-butter-flour-pile-599521',\n 'bake, butter, flour, pile, food preparation, baking, dough, sweets, pastries, sugar, flour, flour, flour, flour, flour, baking, baking, baking, sugar, sugar',\n ),\n hit(\n 1850628,\n 1280,\n 852,\n 'cupcakes-muffins-marshmallows-icing-1850628',\n 'cupcakes, muffins, marshmallows, icing, frosting, pastries, desserts, sweets, plate, treats, baked, homemade, homemade cupcakes, baked goods, baking, delicious, food, food photography, cupcakes, cupcakes',\n ),\n hit(\n 1850011,\n 1280,\n 854,\n 'cake-chocolate-chocolate-cake-1850011',\n 'cake, chocolate, chocolate cake, sliced cake, cake slice, dessert, sweet, food, eat, food photography, delicious, indulgence, pastry, plate, sinful cake, slice, cake, cake, cake, cake',\n ),\n hit(\n 7445434,\n 1280,\n 865,\n 'loaf-evening-bread-bread-crust-7445434',\n 'loaf, evening bread, bread, crust, breakfast, abendbrot, grain bread, sesame seeds, bread, bread, bread, bread, bread',\n ),\n hit(\n 943245,\n 1280,\n 837,\n 'new-year-background-dough-cook-943245',\n 'new year background, dough, cook, recipe, italian, flour, kitchen, preparation, white, ingredient, bakery, homemade, preparing, cuisine, pastry, bread, baking, pizza, traditional',\n ),\n hit(\n 3257019,\n 1280,\n 853,\n 'cakes-dessert-sugar-muffin-cupcake-3257019',\n 'cakes, dessert, sugar, muffin, cupcake, sweets, sprinkles, bakery, pink, meal, cakes, cakes, cakes, cakes, sugar, sugar, cupcake, cupcake, cupcake, bakery',\n ),\n hit(\n 1868925,\n 1280,\n 948,\n 'bakery-breads-assorted-assortment-1868925',\n 'bakery, breads, assorted, assortment, bread shop, food, pastries, shop, store, freshly baked, bakery, bakery, bakery, bakery, bakery, shop',\n ),\n hit(\n 821503,\n 1280,\n 853,\n 'bread-baking-fresh-home-made-food-821503',\n 'bread, baking, fresh, home made, food, bakery, healthy, flour, traditional, cuisine, homemade, loaf, eating, bread, bread, bread, bread, bread, baking',\n ),\n hit(\n 2285209,\n 1280,\n 853,\n 'cupcakes-muffins-dessert-food-cake-2285209',\n 'cupcakes, muffins, dessert, food, cake, sweet, frosting, icing, cupcakes, cupcakes, cake, cake, cake, cake, cake',\n ),\n hit(\n 9865309,\n 1280,\n 791,\n 'sourdough-bread-food-homemade-9865309',\n 'sourdough, bread, food, homemade, baking, fresh, baked, wheat, traditional, crust',\n ),\n hit(\n 4840960,\n 1280,\n 853,\n 'baker-bread-bake-nutrition-bakery-4840960',\n 'baker, bread, bake, nutrition, bakery, fresh, homemade, food, craftsmen, flour, baked, delicious, baker, baker, baker, baker, bread, bread, bread, bakery',\n ),\n hit(\n 9424463,\n 1280,\n 853,\n 'bun-food-fresh-bakery-9424463',\n 'bun, food, fresh, bakery, carbohydrates, to bake, dough, homemade, meal, yummy, nourishment, close up',\n ),\n hit(\n 2436370,\n 1280,\n 853,\n 'loaf-bakery-preparation-fresh-bread-2436370',\n 'loaf, bakery, preparation, fresh bread, baker, meal, nourishment, baguette, dough, flour, craft, authentic, rye bread, wheat bread, to bake, biological, bakery, baguette, baguette, baguette',\n ),\n hit(\n 2193537,\n 1280,\n 853,\n 'bread-loaves-crust-freshly-baked-2193537',\n 'bread, loaves, crust, freshly baked, bakery, bread crust, crispy, fresh, food, baked goods, homemade, baked bread, baked, flour, bread, bread, bread, bread, bread',\n ),\n hit(\n 2178371,\n 1280,\n 853,\n 'macaroons-dessert-sweets-snacks-2178371',\n 'macaroons, dessert, sweets, snacks, colors, food, dessert, dessert, dessert, dessert, dessert, sweets, food',\n ),\n hit(\n 8128550,\n 1280,\n 921,\n 'bread-loaves-food-bakery-craft-8128550',\n 'bread, loaves, food, bakery, craft, shelf, bread, bread, bread, bread, bread, bakery, bakery',\n ),\n hit(\n 6562091,\n 1280,\n 853,\n 'croissant-bread-pastry-breakfast-6562091',\n 'croissant, bread, pastry, breakfast, raspberry, dish, berry, croissant, croissant, croissant, croissant, bread, breakfast, breakfast, breakfast, breakfast, breakfast',\n ),\n hit(\n 279523,\n 1280,\n 763,\n 'cupcake-cake-sweets-sugar-carrot-279523',\n 'cupcake, cake, sweets, sugar, carrot, creme, candy, dough, dessert, brown, pastry, cupcake, cupcake, cake, cake, cake, cake, cake',\n ),\n hit(\n 1869969,\n 1280,\n 851,\n 'book-diary-notes-notepad-paper-1869969',\n 'book, diary, notes, notepad, paper, still life, write, blank pages, open notebook, journal, notebook, empty pages, pages, book, book, book, book, book, diary, paper',\n ),\n hit(\n 1868181,\n 1280,\n 853,\n 'desserts-pastries-shop-pastry-shop-1868181',\n 'desserts, pastries, shop, pastry shop, food, bakeshop, bakery, store, sweets, macaroons, pralines, chocolate candy, confections, bakery, bakery, bakery, bakery, bakery',\n ),\n]\n"],"names":["hit","id","width","height","slug","tags","provider","String","pageUrl","photographer","split","downloadUrl","PAN_DULCE_CONCHAS","BAKERY"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;;;;;;CAeC,GAED,MAAMA,MAAM,CACVC,IACAC,OACAC,QACAC,MACAC,OAEC,CAAA;QACCC,UAAU;QACVL,IAAIM,OAAON;QACXC;QACAC;QACAK,SAAS,CAAC,2BAA2B,EAAEJ,KAAK,CAAC,CAAC;QAC9CK,cAAc;QACdJ,MAAMA,KAAKK,KAAK,CAAC;QACjBC,aAAa,CAAC,yBAAyB,EAAEV,GAAG,SAAS,CAAC;IACxD,CAAA;AAEF,yGAAyG,GACzG,OAAO,MAAMW,oBAAkC;IAC7CZ,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,OACA,MACA,KACA,sCACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,qCACA;IAEFA,IACE,SACA,MACA,KACA,uCACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,0BACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,uCACA;IAEFA,IACE,SACA,MACA,KACA,oCACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,sCACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,mCACA;IAEFA,IACE,QACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,+BACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,uCACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,0BACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,sCACA;CAEH,CAAA;AAED,oGAAoG,GACpG,OAAO,MAAMa,SAAuB;IAClCb,IACE,SACA,MACA,KACA,sCACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,sCACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,QACA,MACA,KACA,oCACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,qCACA;IAEFA,IACE,QACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,wCACA;IAEFA,IACE,QACA,MACA,KACA,iCACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,QACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,QACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;IAEFA,IACE,SACA,MACA,KACA,yCACA;IAEFA,IACE,SACA,MACA,KACA,6CACA;IAEFA,IACE,SACA,MACA,KACA,iCACA;IAEFA,IACE,SACA,MACA,KACA,+CACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,SACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,4CACA;IAEFA,IACE,QACA,MACA,KACA,2CACA;IAEFA,IACE,SACA,MACA,KACA,0CACA;IAEFA,IACE,SACA,MACA,KACA,8CACA;CAEH,CAAA"}
@@ -20,6 +20,21 @@ import { type AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
20
20
  export declare const AI_LAYOUT_GAP_CODE = "layout-gap";
21
21
  /** The gaps a language-built page or frame carries, as the violation that asks again. */
22
22
  export declare function aiLayoutGapViolations(nodes: NodesMap): AiDoctrineViolation[];
23
+ /**
24
+ * The page with these sections taken out (AGL-3771), each with any link that
25
+ * scrolled to it: the last answer's sections that still show none of the
26
+ * items they were planned with. A home page refused whole for one such
27
+ * section left a published site with nothing at its address; the section
28
+ * goes, the rest of the page is kept, and the job says which went.
29
+ */
30
+ export declare function aiLayoutWithoutSections(input: NodesMap, rootId: string, sections: ReadonlyArray<{
31
+ id: string;
32
+ name: string;
33
+ why: string;
34
+ }>): {
35
+ nodes: NodesMap;
36
+ dropped: string[];
37
+ };
23
38
  /** The page or frame with every gap taken out, and what was taken. */
24
39
  export declare function aiLayoutWithoutGaps(input: NodesMap, rootId: string): {
25
40
  nodes: NodesMap;
@@ -192,17 +192,67 @@ function cloneNodes(input) {
192
192
  var _nodes_section_props;
193
193
  if (section === first || !nodes[section] || speaks(nodes, section, true)) continue;
194
194
  const label = String((_ref1 = (_nodes_section_props = nodes[section].props) == null ? void 0 : _nodes_section_props['ariaLabel']) != null ? _ref1 : section);
195
- remove(section);
196
- dropped.push(`the section "${label}", left with only its heading`);
197
- for (const [id, node] of Object.entries(nodes)){
198
- if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {
199
- dropped.push(`"${wordsOf(node)}", a link to that section`);
200
- prune(id);
201
- }
195
+ dropped.push(`the section "${label}", left with only its heading`, ...removeSection(nodes, section, remove, prune));
196
+ }
197
+ return dropped;
198
+ }
199
+ /**
200
+ * Takes a section out, and each link that scrolled to it. A page built in the
201
+ * layout language is written whole, so its page step and the site start read
202
+ * a planned section missing from it as one taken out, never one still to
203
+ * build (AGL-3771).
204
+ */ function removeSection(nodes, section, remove, prune) {
205
+ const dropped = [];
206
+ remove(section);
207
+ for (const [id, node] of Object.entries(nodes)){
208
+ if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {
209
+ dropped.push(`"${wordsOf(node)}", a link to that section`);
210
+ prune(id);
202
211
  }
203
212
  }
204
213
  return dropped;
205
214
  }
215
+ /**
216
+ * The page with these sections taken out (AGL-3771), each with any link that
217
+ * scrolled to it: the last answer's sections that still show none of the
218
+ * items they were planned with. A home page refused whole for one such
219
+ * section left a published site with nothing at its address; the section
220
+ * goes, the rest of the page is kept, and the job says which went.
221
+ */ export function aiLayoutWithoutSections(input, rootId, sections) {
222
+ var _ref;
223
+ var _nodes_rootId;
224
+ const present = sections.filter((section)=>input[section.id]);
225
+ if (!present.length) return {
226
+ nodes: input,
227
+ dropped: []
228
+ };
229
+ const nodes = cloneNodes(input);
230
+ const dropped = [];
231
+ const remove = (id)=>{
232
+ var _nodes_parent_nodes;
233
+ const parent = parentsOf(nodes).get(id);
234
+ for (const each of subtree(nodes, id))delete nodes[each];
235
+ if (parent && nodes[parent]) nodes[parent].nodes = ((_nodes_parent_nodes = nodes[parent].nodes) != null ? _nodes_parent_nodes : []).filter((child)=>child !== id);
236
+ };
237
+ /** A link and each element above it left with nothing to say, short of a section. */ const sectionRoots = new Set((_ref = (_nodes_rootId = nodes[rootId]) == null ? void 0 : _nodes_rootId.nodes) != null ? _ref : []);
238
+ const prune = (id)=>{
239
+ let parent = parentsOf(nodes).get(id);
240
+ remove(id);
241
+ while(parent && parent !== rootId && nodes[parent]){
242
+ if (sectionRoots.has(parent) || speaks(nodes, parent)) break;
243
+ const above = parentsOf(nodes).get(parent);
244
+ remove(parent);
245
+ parent = above;
246
+ }
247
+ };
248
+ for (const section of present){
249
+ dropped.push(`the section "${section.name}", ${section.why}`, ...removeSection(nodes, section.id, remove, prune));
250
+ }
251
+ return {
252
+ nodes: nodes,
253
+ dropped
254
+ };
255
+ }
206
256
  /** The page or frame with every gap taken out, and what was taken. */ export function aiLayoutWithoutGaps(input, rootId) {
207
257
  const found = gapsOf(input);
208
258
  if (!found.length) return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-gaps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { aiBracketedFacts, type AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { aiStoredScrollTargets } from '../runtime/ai-page-links'\n\n/**\n * No gap on a published page (AGL-3660). A guided start publishes what it\n * builds, so a fact the brief never gave — a phone number, an address, the\n * opening hours — is left out of a page or a frame built in the layout\n * language, never written as the bracketed gap (\"[phone number]\") a draft a\n * person edits first may carry. An answer holding one is asked again; the\n * last answer a generation takes has each one taken out instead: the line\n * that carries it, the row or card it leaves with nothing to say, and a\n * section left with only its heading.\n */\n\ninterface GapNode {\n componentId: string\n props?: Record<string, unknown>\n nodes?: string[]\n}\n\ntype GapNodes = Record<string, GapNode>\n\n/** Props that hold no words a visitor reads: an icon's drawing is path data. */\nconst NOT_WORDS = new Set(['iconPath', 'iconId'])\n\n/** The props a node's own words are in. */\nconst WORD_PROPS = ['children', 'primary', 'secondary', 'label'] as const\n\n/** Elements that say something with no words of their own. */\nconst SPEAKS_ALONE = new Set([\n 'image',\n 'form',\n 'layoutSlot',\n 'reusableInstance',\n 'muiDrawerToggle',\n 'muiDrawer',\n // The site's own records, listed by the elements that keep them (AGL-3676).\n 'product-grid',\n 'collectionEntries',\n 'cart',\n // A music site's player, placed empty for the owner's tracks (AGL-3716).\n 'musicPlayer',\n // The site's events, listed by the events-calendar plugin (AGL-3755).\n 'eventList',\n])\n\nconst HEADING_VARIANTS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'overline'])\n\n/** The violation code a gap is named by. */\nexport const AI_LAYOUT_GAP_CODE = 'layout-gap'\n\n/** Every string a value holds, however deep, its icon drawings aside. */\nfunction stringsOf(value: unknown, key = ''): string[] {\n if (NOT_WORDS.has(key)) return []\n if (typeof value === 'string') return [value]\n if (Array.isArray(value)) return value.flatMap((entry) => stringsOf(entry))\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>).flatMap(([name, entry]) => stringsOf(entry, name))\n }\n return []\n}\n\n/** Each node whose words carry a gap, with the gaps it carries. */\nfunction gapsOf(nodes: GapNodes): Array<{ id: string; facts: string[]; text: string }> {\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = stringsOf(node.props ?? {})\n const facts = aiBracketedFacts(texts)\n return facts.length ? [{ id, facts, text: texts.filter((text) => aiBracketedFacts([text]).length).join(' ') }] : []\n })\n}\n\n/** The gaps a language-built page or frame carries, as the violation that asks again. */\nexport function aiLayoutGapViolations(nodes: NodesMap): AiDoctrineViolation[] {\n const found = gapsOf(nodes as unknown as GapNodes)\n if (!found.length) return []\n const facts = aiBracketedFacts(found.flatMap((entry) => entry.facts))\n return [\n {\n rule: 14,\n code: AI_LAYOUT_GAP_CODE,\n message: `${facts.join(', ')} ${facts.length === 1 ? 'is a gap' : 'are gaps'} for a fact the brief does not give, and this site is published exactly as written. Leave each fact out: write the sentence without it, and make no list line, card or contact row for it. Never write a gap in square brackets, and never invent the fact.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\nfunction parentsOf(nodes: GapNodes): Map<string, string> {\n const parents = new Map<string, string>()\n for (const [id, node] of Object.entries(nodes)) for (const child of node.nodes ?? []) parents.set(child, id)\n return parents\n}\n\nfunction subtree(nodes: GapNodes, id: string): string[] {\n const ids: string[] = []\n const stack = [id]\n while (stack.length) {\n const next = stack.pop() as string\n if (!nodes[next] || ids.includes(next)) continue\n ids.push(next)\n stack.push(...(nodes[next].nodes ?? []))\n }\n return ids\n}\n\nfunction wordsOf(node: GapNode): string {\n for (const prop of WORD_PROPS) {\n const value = node.props?.[prop]\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return ''\n}\n\nfunction isHeading(node: GapNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n return HEADING_VARIANTS.has(String(node.props?.['variant'] ?? '')) || /^h[1-6]$/.test(String(node.props?.['component'] ?? ''))\n}\n\n/** Whether anything under a node says something; with `beyondHeadings`, anything but a heading. */\nfunction speaks(nodes: GapNodes, id: string, beyondHeadings = false): boolean {\n return subtree(nodes, id).some((each) => {\n const node = nodes[each]\n const says = SPEAKS_ALONE.has(node.componentId) || !!wordsOf(node)\n return says && !(beyondHeadings && isHeading(node))\n })\n}\n\n/** A node's props, its component's prop values copied too, so a rewrite never reaches the input. */\nfunction copyProps(props: Record<string, unknown>): Record<string, unknown> {\n const values = props['propValues']\n return values && typeof values === 'object' && !Array.isArray(values)\n ? { ...props, propValues: { ...(values as Record<string, unknown>) } }\n : { ...props }\n}\n\nfunction cloneNodes(input: NodesMap): GapNodes {\n return Object.fromEntries(\n Object.entries(input as unknown as GapNodes).map(([id, node]) => [\n id,\n {\n ...node,\n ...(node.props ? { props: copyProps(node.props) } : {}),\n ...(node.nodes ? { nodes: [...node.nodes] } : {}),\n },\n ]),\n )\n}\n\n/**\n * Takes each found node out, and each element above it left with nothing to\n * say, up to its section; a section other than the page's first left with\n * only its heading goes too, with any link that scrolled to it.\n */\nfunction pruneFound(nodes: GapNodes, rootId: string, found: ReadonlyArray<{ id: string; text: string }>): string[] {\n const dropped: string[] = []\n const sections = new Set(nodes[rootId]?.nodes ?? [])\n const touched = new Set<string>()\n const remove = (id: string) => {\n const parent = parentsOf(nodes).get(id)\n for (const each of subtree(nodes, id)) delete nodes[each]\n if (parent && nodes[parent]) nodes[parent].nodes = (nodes[parent].nodes ?? []).filter((child) => child !== id)\n }\n /** Takes a node out, and each element above it it leaves with nothing to say, short of a section. */\n const prune = (id: string) => {\n const parents = parentsOf(nodes)\n let section: string | undefined = id\n while (section && !sections.has(section)) section = parents.get(section)\n if (section && section !== id) touched.add(section)\n let parent = parents.get(id)\n remove(id)\n while (parent && parent !== rootId && nodes[parent]) {\n if (sections.has(parent) || speaks(nodes, parent)) break\n const above = parentsOf(nodes).get(parent)\n remove(parent)\n parent = above\n }\n }\n for (const entry of found) {\n if (!nodes[entry.id]) continue\n dropped.push(entry.text)\n prune(entry.id)\n }\n const first = nodes[rootId]?.nodes?.[0]\n for (const section of touched) {\n if (section === first || !nodes[section] || speaks(nodes, section, true)) continue\n const label = String(nodes[section].props?.['ariaLabel'] ?? section)\n remove(section)\n dropped.push(`the section \"${label}\", left with only its heading`)\n for (const [id, node] of Object.entries(nodes)) {\n if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {\n dropped.push(`\"${wordsOf(node)}\", a link to that section`)\n prune(id)\n }\n }\n }\n return dropped\n}\n\n/** The page or frame with every gap taken out, and what was taken. */\nexport function aiLayoutWithoutGaps(input: NodesMap, rootId: string): { nodes: NodesMap; dropped: string[] } {\n const found = gapsOf(input as unknown as GapNodes)\n if (!found.length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const dropped = pruneFound(nodes, rootId, found)\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n// ── Internal references ─────────────────────────────────────────────────\n\n/**\n * What a link may name, which never belongs in words a visitor reads: the\n * site's pages, with the title a reference to one is written as, and every\n * other id the page or frame was built against (its forms, components and\n * sections).\n */\nexport interface AiLayoutRefNames {\n pages: ReadonlyArray<{ id: string; label: string }>\n ids: readonly string[]\n}\n\n/** The violation code an internal reference in copy is named by. */\nexport const AI_LAYOUT_REF_CODE = 'layout-internal-ref'\n\n/** A link's own notation, or a template token other than the site's own (`{{host.…}}`). */\nconst REF = /\\b(?:page|new|form|component|screen):[A-Za-z0-9_-]+|\\{\\{(?!\\s*host\\.)[^{}]*\\}\\}/g\nconst PAGE_REF = /\\bpage:([A-Za-z0-9_-]+)/g\n\n/** The props whose words a visitor reads, and a component's prop values. */\nconst SHOWN_PROPS = [...WORD_PROPS, 'alt', 'title']\n\nfunction shownTexts(node: GapNode): Array<{ get: () => string; set: (value: string) => void }> {\n const props = node.props ?? {}\n const slots: Array<{ get: () => string; set: (value: string) => void }> = []\n for (const prop of SHOWN_PROPS) {\n if (typeof props[prop] === 'string') slots.push({ get: () => props[prop] as string, set: (value) => (props[prop] = value) })\n }\n const values = props['propValues']\n if (values && typeof values === 'object' && !Array.isArray(values)) {\n const record = values as Record<string, unknown>\n for (const name of Object.keys(record)) {\n if (typeof record[name] === 'string') slots.push({ get: () => record[name] as string, set: (value) => (record[name] = value) })\n }\n }\n return slots\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction refsIn(text: string, names: AiLayoutRefNames): string[] {\n const found = (text.match(REF) ?? []).map((ref) => ref.trim())\n for (const id of names.ids) {\n if (id.length >= 6 && new RegExp(`(^|[^A-Za-z0-9_-])${escapeRegExp(id)}($|[^A-Za-z0-9_-])`).test(text)) found.push(id)\n }\n return [...new Set(found)]\n}\n\n/** An entry's own token, which a collection repeater fills per entry when the page renders. */\nconst ENTRY_TOKEN = /\\{\\{\\s*entry\\.[A-Za-z]+\\s*\\}\\}/g\n\n/** Every node under a Collection Entries block: its template, repeated once per entry (AGL-3676). */\nfunction repeatedByEntries(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (node.componentId !== 'collectionEntries') continue\n for (const each of subtree(nodes, id)) if (each !== id) repeated.add(each)\n }\n return repeated\n}\n\n/** A record's own token, which a repeat over a dataset fills per record when the page renders. */\nconst ITEM_TOKEN = /\\{\\{\\s*item\\.[A-Za-z][A-Za-z0-9_]*(?:\\.[A-Za-z][A-Za-z0-9_]*)?\\s*\\}\\}/g\n\n/** Every node a repeat over a dataset copies once per record (AGL-3616), the repeating node included. */\nfunction repeatedByDataset(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n const key = node.props?.['repeatDataset']\n if (typeof key !== 'string' || !key.trim()) continue\n for (const each of subtree(nodes, id)) repeated.add(each)\n }\n return repeated\n}\n\nfunction refsOf(nodes: GapNodes, names: AiLayoutRefNames): Array<{ id: string; refs: string[]; text: string }> {\n // A post's card names its post by the entry's tokens, which the page fills\n // per post: words a visitor reads as the post's own, never a reference.\n // A dataset's card names its record by the record's, the same way (AGL-3616).\n const repeated = repeatedByEntries(nodes)\n const records = repeatedByDataset(nodes)\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = shownTexts(node).map((slot) => {\n const text = repeated.has(id) ? slot.get().replace(ENTRY_TOKEN, '') : slot.get()\n return records.has(id) ? text.replace(ITEM_TOKEN, '') : text\n })\n const refs = [...new Set(texts.flatMap((text) => refsIn(text, names)))]\n return refs.length ? [{ id, refs, text: texts.filter((text) => refsIn(text, names).length).join(' ') }] : []\n })\n}\n\n/** Internal references in a language-built page's or frame's words, as the violation that asks again. */\nexport function aiLayoutRefViolations(nodes: NodesMap, names: AiLayoutRefNames): AiDoctrineViolation[] {\n const found = refsOf(nodes as unknown as GapNodes, names)\n if (!found.length) return []\n const refs = [...new Set(found.flatMap((entry) => entry.refs))].map((ref) => `\"${ref}\"`)\n return [\n {\n rule: 14,\n code: AI_LAYOUT_REF_CODE,\n message: `${refs.join(', ')} ${refs.length === 1 ? 'is a link reference' : 'are link references'} written into words a visitor reads. A reference goes only in a button's or a list item's to; in text, name the page by its title, or leave the reference out.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\n/**\n * The page or frame with no internal reference in its words: a page's\n * reference is written as that page's title, and a line that still names a\n * reference is taken out, as a gap is. Returns what was rewritten and taken.\n */\nexport function aiLayoutWithoutRefs(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n): { nodes: NodesMap; dropped: string[] } {\n if (!refsOf(input as unknown as GapNodes, names).length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const labels = new Map(names.pages.map((page) => [page.id, page.label]))\n const dropped: string[] = []\n for (const node of Object.values(nodes)) {\n for (const slot of shownTexts(node)) {\n const before = slot.get()\n const after = before.replace(PAGE_REF, (ref, id: string) => labels.get(id) ?? ref)\n if (after !== before) {\n slot.set(after)\n dropped.push(`\"${before}\" written as \"${after}\"`)\n }\n }\n }\n dropped.push(...pruneFound(nodes, rootId, refsOf(nodes, names)))\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n/**\n * A language-built page's or frame's words held to both (AGL-3660): before\n * the last answer, each gap and each internal reference is a violation that\n * asks again; in the last, they are taken out and the rest is kept.\n */\nexport function aiLayoutCopyCheck(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n last: boolean,\n): { nodes: NodesMap; dropped: string[]; violations: AiDoctrineViolation[] } {\n if (!last) {\n return { nodes: input, dropped: [], violations: [...aiLayoutRefViolations(input, names), ...aiLayoutGapViolations(input)] }\n }\n const refs = aiLayoutWithoutRefs(input, rootId, names)\n const gaps = aiLayoutWithoutGaps(refs.nodes, rootId)\n return { nodes: gaps.nodes, dropped: [...refs.dropped, ...gaps.dropped], violations: [] }\n}\n"],"names":["aiBracketedFacts","aiStoredScrollTargets","NOT_WORDS","Set","WORD_PROPS","SPEAKS_ALONE","HEADING_VARIANTS","AI_LAYOUT_GAP_CODE","stringsOf","value","key","has","Array","isArray","flatMap","entry","Object","entries","name","gapsOf","nodes","id","node","texts","props","facts","length","text","filter","join","aiLayoutGapViolations","found","rule","code","message","nodeIds","map","parentsOf","parents","Map","child","set","subtree","ids","stack","next","pop","includes","push","wordsOf","prop","trim","isHeading","componentId","String","test","speaks","beyondHeadings","some","each","says","copyProps","values","propValues","cloneNodes","input","fromEntries","pruneFound","rootId","dropped","sections","touched","remove","parent","get","prune","section","add","above","first","label","aiLayoutWithoutGaps","AI_LAYOUT_REF_CODE","REF","PAGE_REF","SHOWN_PROPS","shownTexts","slots","record","keys","escapeRegExp","replace","refsIn","names","match","ref","RegExp","ENTRY_TOKEN","repeatedByEntries","repeated","ITEM_TOKEN","repeatedByDataset","refsOf","records","slot","refs","aiLayoutRefViolations","aiLayoutWithoutRefs","labels","pages","page","before","after","aiLayoutCopyCheck","last","violations","gaps"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-gaps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { aiBracketedFacts, type AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { aiStoredScrollTargets } from '../runtime/ai-page-links'\n\n/**\n * No gap on a published page (AGL-3660). A guided start publishes what it\n * builds, so a fact the brief never gave — a phone number, an address, the\n * opening hours — is left out of a page or a frame built in the layout\n * language, never written as the bracketed gap (\"[phone number]\") a draft a\n * person edits first may carry. An answer holding one is asked again; the\n * last answer a generation takes has each one taken out instead: the line\n * that carries it, the row or card it leaves with nothing to say, and a\n * section left with only its heading.\n */\n\ninterface GapNode {\n componentId: string\n props?: Record<string, unknown>\n nodes?: string[]\n}\n\ntype GapNodes = Record<string, GapNode>\n\n/** Props that hold no words a visitor reads: an icon's drawing is path data. */\nconst NOT_WORDS = new Set(['iconPath', 'iconId'])\n\n/** The props a node's own words are in. */\nconst WORD_PROPS = ['children', 'primary', 'secondary', 'label'] as const\n\n/** Elements that say something with no words of their own. */\nconst SPEAKS_ALONE = new Set([\n 'image',\n 'form',\n 'layoutSlot',\n 'reusableInstance',\n 'muiDrawerToggle',\n 'muiDrawer',\n // The site's own records, listed by the elements that keep them (AGL-3676).\n 'product-grid',\n 'collectionEntries',\n 'cart',\n // A music site's player, placed empty for the owner's tracks (AGL-3716).\n 'musicPlayer',\n // The site's events, listed by the events-calendar plugin (AGL-3755).\n 'eventList',\n])\n\nconst HEADING_VARIANTS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'overline'])\n\n/** The violation code a gap is named by. */\nexport const AI_LAYOUT_GAP_CODE = 'layout-gap'\n\n/** Every string a value holds, however deep, its icon drawings aside. */\nfunction stringsOf(value: unknown, key = ''): string[] {\n if (NOT_WORDS.has(key)) return []\n if (typeof value === 'string') return [value]\n if (Array.isArray(value)) return value.flatMap((entry) => stringsOf(entry))\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>).flatMap(([name, entry]) => stringsOf(entry, name))\n }\n return []\n}\n\n/** Each node whose words carry a gap, with the gaps it carries. */\nfunction gapsOf(nodes: GapNodes): Array<{ id: string; facts: string[]; text: string }> {\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = stringsOf(node.props ?? {})\n const facts = aiBracketedFacts(texts)\n return facts.length ? [{ id, facts, text: texts.filter((text) => aiBracketedFacts([text]).length).join(' ') }] : []\n })\n}\n\n/** The gaps a language-built page or frame carries, as the violation that asks again. */\nexport function aiLayoutGapViolations(nodes: NodesMap): AiDoctrineViolation[] {\n const found = gapsOf(nodes as unknown as GapNodes)\n if (!found.length) return []\n const facts = aiBracketedFacts(found.flatMap((entry) => entry.facts))\n return [\n {\n rule: 14,\n code: AI_LAYOUT_GAP_CODE,\n message: `${facts.join(', ')} ${facts.length === 1 ? 'is a gap' : 'are gaps'} for a fact the brief does not give, and this site is published exactly as written. Leave each fact out: write the sentence without it, and make no list line, card or contact row for it. Never write a gap in square brackets, and never invent the fact.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\nfunction parentsOf(nodes: GapNodes): Map<string, string> {\n const parents = new Map<string, string>()\n for (const [id, node] of Object.entries(nodes)) for (const child of node.nodes ?? []) parents.set(child, id)\n return parents\n}\n\nfunction subtree(nodes: GapNodes, id: string): string[] {\n const ids: string[] = []\n const stack = [id]\n while (stack.length) {\n const next = stack.pop() as string\n if (!nodes[next] || ids.includes(next)) continue\n ids.push(next)\n stack.push(...(nodes[next].nodes ?? []))\n }\n return ids\n}\n\nfunction wordsOf(node: GapNode): string {\n for (const prop of WORD_PROPS) {\n const value = node.props?.[prop]\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return ''\n}\n\nfunction isHeading(node: GapNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n return HEADING_VARIANTS.has(String(node.props?.['variant'] ?? '')) || /^h[1-6]$/.test(String(node.props?.['component'] ?? ''))\n}\n\n/** Whether anything under a node says something; with `beyondHeadings`, anything but a heading. */\nfunction speaks(nodes: GapNodes, id: string, beyondHeadings = false): boolean {\n return subtree(nodes, id).some((each) => {\n const node = nodes[each]\n const says = SPEAKS_ALONE.has(node.componentId) || !!wordsOf(node)\n return says && !(beyondHeadings && isHeading(node))\n })\n}\n\n/** A node's props, its component's prop values copied too, so a rewrite never reaches the input. */\nfunction copyProps(props: Record<string, unknown>): Record<string, unknown> {\n const values = props['propValues']\n return values && typeof values === 'object' && !Array.isArray(values)\n ? { ...props, propValues: { ...(values as Record<string, unknown>) } }\n : { ...props }\n}\n\nfunction cloneNodes(input: NodesMap): GapNodes {\n return Object.fromEntries(\n Object.entries(input as unknown as GapNodes).map(([id, node]) => [\n id,\n {\n ...node,\n ...(node.props ? { props: copyProps(node.props) } : {}),\n ...(node.nodes ? { nodes: [...node.nodes] } : {}),\n },\n ]),\n )\n}\n\n/**\n * Takes each found node out, and each element above it left with nothing to\n * say, up to its section; a section other than the page's first left with\n * only its heading goes too, with any link that scrolled to it.\n */\nfunction pruneFound(nodes: GapNodes, rootId: string, found: ReadonlyArray<{ id: string; text: string }>): string[] {\n const dropped: string[] = []\n const sections = new Set(nodes[rootId]?.nodes ?? [])\n const touched = new Set<string>()\n const remove = (id: string) => {\n const parent = parentsOf(nodes).get(id)\n for (const each of subtree(nodes, id)) delete nodes[each]\n if (parent && nodes[parent]) nodes[parent].nodes = (nodes[parent].nodes ?? []).filter((child) => child !== id)\n }\n /** Takes a node out, and each element above it it leaves with nothing to say, short of a section. */\n const prune = (id: string) => {\n const parents = parentsOf(nodes)\n let section: string | undefined = id\n while (section && !sections.has(section)) section = parents.get(section)\n if (section && section !== id) touched.add(section)\n let parent = parents.get(id)\n remove(id)\n while (parent && parent !== rootId && nodes[parent]) {\n if (sections.has(parent) || speaks(nodes, parent)) break\n const above = parentsOf(nodes).get(parent)\n remove(parent)\n parent = above\n }\n }\n for (const entry of found) {\n if (!nodes[entry.id]) continue\n dropped.push(entry.text)\n prune(entry.id)\n }\n const first = nodes[rootId]?.nodes?.[0]\n for (const section of touched) {\n if (section === first || !nodes[section] || speaks(nodes, section, true)) continue\n const label = String(nodes[section].props?.['ariaLabel'] ?? section)\n dropped.push(`the section \"${label}\", left with only its heading`, ...removeSection(nodes, section, remove, prune))\n }\n return dropped\n}\n\n/**\n * Takes a section out, and each link that scrolled to it. A page built in the\n * layout language is written whole, so its page step and the site start read\n * a planned section missing from it as one taken out, never one still to\n * build (AGL-3771).\n */\nfunction removeSection(nodes: GapNodes, section: string, remove: (id: string) => void, prune: (id: string) => void): string[] {\n const dropped: string[] = []\n remove(section)\n for (const [id, node] of Object.entries(nodes)) {\n if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {\n dropped.push(`\"${wordsOf(node)}\", a link to that section`)\n prune(id)\n }\n }\n return dropped\n}\n\n/**\n * The page with these sections taken out (AGL-3771), each with any link that\n * scrolled to it: the last answer's sections that still show none of the\n * items they were planned with. A home page refused whole for one such\n * section left a published site with nothing at its address; the section\n * goes, the rest of the page is kept, and the job says which went.\n */\nexport function aiLayoutWithoutSections(\n input: NodesMap,\n rootId: string,\n sections: ReadonlyArray<{ id: string; name: string; why: string }>,\n): { nodes: NodesMap; dropped: string[] } {\n const present = sections.filter((section) => (input as unknown as GapNodes)[section.id])\n if (!present.length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const dropped: string[] = []\n const remove = (id: string) => {\n const parent = parentsOf(nodes).get(id)\n for (const each of subtree(nodes, id)) delete nodes[each]\n if (parent && nodes[parent]) nodes[parent].nodes = (nodes[parent].nodes ?? []).filter((child) => child !== id)\n }\n /** A link and each element above it left with nothing to say, short of a section. */\n const sectionRoots = new Set(nodes[rootId]?.nodes ?? [])\n const prune = (id: string) => {\n let parent = parentsOf(nodes).get(id)\n remove(id)\n while (parent && parent !== rootId && nodes[parent]) {\n if (sectionRoots.has(parent) || speaks(nodes, parent)) break\n const above = parentsOf(nodes).get(parent)\n remove(parent)\n parent = above\n }\n }\n for (const section of present) {\n dropped.push(`the section \"${section.name}\", ${section.why}`, ...removeSection(nodes, section.id, remove, prune))\n }\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n/** The page or frame with every gap taken out, and what was taken. */\nexport function aiLayoutWithoutGaps(input: NodesMap, rootId: string): { nodes: NodesMap; dropped: string[] } {\n const found = gapsOf(input as unknown as GapNodes)\n if (!found.length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const dropped = pruneFound(nodes, rootId, found)\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n// ── Internal references ─────────────────────────────────────────────────\n\n/**\n * What a link may name, which never belongs in words a visitor reads: the\n * site's pages, with the title a reference to one is written as, and every\n * other id the page or frame was built against (its forms, components and\n * sections).\n */\nexport interface AiLayoutRefNames {\n pages: ReadonlyArray<{ id: string; label: string }>\n ids: readonly string[]\n}\n\n/** The violation code an internal reference in copy is named by. */\nexport const AI_LAYOUT_REF_CODE = 'layout-internal-ref'\n\n/** A link's own notation, or a template token other than the site's own (`{{host.…}}`). */\nconst REF = /\\b(?:page|new|form|component|screen):[A-Za-z0-9_-]+|\\{\\{(?!\\s*host\\.)[^{}]*\\}\\}/g\nconst PAGE_REF = /\\bpage:([A-Za-z0-9_-]+)/g\n\n/** The props whose words a visitor reads, and a component's prop values. */\nconst SHOWN_PROPS = [...WORD_PROPS, 'alt', 'title']\n\nfunction shownTexts(node: GapNode): Array<{ get: () => string; set: (value: string) => void }> {\n const props = node.props ?? {}\n const slots: Array<{ get: () => string; set: (value: string) => void }> = []\n for (const prop of SHOWN_PROPS) {\n if (typeof props[prop] === 'string') slots.push({ get: () => props[prop] as string, set: (value) => (props[prop] = value) })\n }\n const values = props['propValues']\n if (values && typeof values === 'object' && !Array.isArray(values)) {\n const record = values as Record<string, unknown>\n for (const name of Object.keys(record)) {\n if (typeof record[name] === 'string') slots.push({ get: () => record[name] as string, set: (value) => (record[name] = value) })\n }\n }\n return slots\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction refsIn(text: string, names: AiLayoutRefNames): string[] {\n const found = (text.match(REF) ?? []).map((ref) => ref.trim())\n for (const id of names.ids) {\n if (id.length >= 6 && new RegExp(`(^|[^A-Za-z0-9_-])${escapeRegExp(id)}($|[^A-Za-z0-9_-])`).test(text)) found.push(id)\n }\n return [...new Set(found)]\n}\n\n/** An entry's own token, which a collection repeater fills per entry when the page renders. */\nconst ENTRY_TOKEN = /\\{\\{\\s*entry\\.[A-Za-z]+\\s*\\}\\}/g\n\n/** Every node under a Collection Entries block: its template, repeated once per entry (AGL-3676). */\nfunction repeatedByEntries(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (node.componentId !== 'collectionEntries') continue\n for (const each of subtree(nodes, id)) if (each !== id) repeated.add(each)\n }\n return repeated\n}\n\n/** A record's own token, which a repeat over a dataset fills per record when the page renders. */\nconst ITEM_TOKEN = /\\{\\{\\s*item\\.[A-Za-z][A-Za-z0-9_]*(?:\\.[A-Za-z][A-Za-z0-9_]*)?\\s*\\}\\}/g\n\n/** Every node a repeat over a dataset copies once per record (AGL-3616), the repeating node included. */\nfunction repeatedByDataset(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n const key = node.props?.['repeatDataset']\n if (typeof key !== 'string' || !key.trim()) continue\n for (const each of subtree(nodes, id)) repeated.add(each)\n }\n return repeated\n}\n\nfunction refsOf(nodes: GapNodes, names: AiLayoutRefNames): Array<{ id: string; refs: string[]; text: string }> {\n // A post's card names its post by the entry's tokens, which the page fills\n // per post: words a visitor reads as the post's own, never a reference.\n // A dataset's card names its record by the record's, the same way (AGL-3616).\n const repeated = repeatedByEntries(nodes)\n const records = repeatedByDataset(nodes)\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = shownTexts(node).map((slot) => {\n const text = repeated.has(id) ? slot.get().replace(ENTRY_TOKEN, '') : slot.get()\n return records.has(id) ? text.replace(ITEM_TOKEN, '') : text\n })\n const refs = [...new Set(texts.flatMap((text) => refsIn(text, names)))]\n return refs.length ? [{ id, refs, text: texts.filter((text) => refsIn(text, names).length).join(' ') }] : []\n })\n}\n\n/** Internal references in a language-built page's or frame's words, as the violation that asks again. */\nexport function aiLayoutRefViolations(nodes: NodesMap, names: AiLayoutRefNames): AiDoctrineViolation[] {\n const found = refsOf(nodes as unknown as GapNodes, names)\n if (!found.length) return []\n const refs = [...new Set(found.flatMap((entry) => entry.refs))].map((ref) => `\"${ref}\"`)\n return [\n {\n rule: 14,\n code: AI_LAYOUT_REF_CODE,\n message: `${refs.join(', ')} ${refs.length === 1 ? 'is a link reference' : 'are link references'} written into words a visitor reads. A reference goes only in a button's or a list item's to; in text, name the page by its title, or leave the reference out.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\n/**\n * The page or frame with no internal reference in its words: a page's\n * reference is written as that page's title, and a line that still names a\n * reference is taken out, as a gap is. Returns what was rewritten and taken.\n */\nexport function aiLayoutWithoutRefs(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n): { nodes: NodesMap; dropped: string[] } {\n if (!refsOf(input as unknown as GapNodes, names).length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const labels = new Map(names.pages.map((page) => [page.id, page.label]))\n const dropped: string[] = []\n for (const node of Object.values(nodes)) {\n for (const slot of shownTexts(node)) {\n const before = slot.get()\n const after = before.replace(PAGE_REF, (ref, id: string) => labels.get(id) ?? ref)\n if (after !== before) {\n slot.set(after)\n dropped.push(`\"${before}\" written as \"${after}\"`)\n }\n }\n }\n dropped.push(...pruneFound(nodes, rootId, refsOf(nodes, names)))\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n/**\n * A language-built page's or frame's words held to both (AGL-3660): before\n * the last answer, each gap and each internal reference is a violation that\n * asks again; in the last, they are taken out and the rest is kept.\n */\nexport function aiLayoutCopyCheck(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n last: boolean,\n): { nodes: NodesMap; dropped: string[]; violations: AiDoctrineViolation[] } {\n if (!last) {\n return { nodes: input, dropped: [], violations: [...aiLayoutRefViolations(input, names), ...aiLayoutGapViolations(input)] }\n }\n const refs = aiLayoutWithoutRefs(input, rootId, names)\n const gaps = aiLayoutWithoutGaps(refs.nodes, rootId)\n return { nodes: gaps.nodes, dropped: [...refs.dropped, ...gaps.dropped], violations: [] }\n}\n"],"names":["aiBracketedFacts","aiStoredScrollTargets","NOT_WORDS","Set","WORD_PROPS","SPEAKS_ALONE","HEADING_VARIANTS","AI_LAYOUT_GAP_CODE","stringsOf","value","key","has","Array","isArray","flatMap","entry","Object","entries","name","gapsOf","nodes","id","node","texts","props","facts","length","text","filter","join","aiLayoutGapViolations","found","rule","code","message","nodeIds","map","parentsOf","parents","Map","child","set","subtree","ids","stack","next","pop","includes","push","wordsOf","prop","trim","isHeading","componentId","String","test","speaks","beyondHeadings","some","each","says","copyProps","values","propValues","cloneNodes","input","fromEntries","pruneFound","rootId","dropped","sections","touched","remove","parent","get","prune","section","add","above","first","label","removeSection","aiLayoutWithoutSections","present","sectionRoots","why","aiLayoutWithoutGaps","AI_LAYOUT_REF_CODE","REF","PAGE_REF","SHOWN_PROPS","shownTexts","slots","record","keys","escapeRegExp","replace","refsIn","names","match","ref","RegExp","ENTRY_TOKEN","repeatedByEntries","repeated","ITEM_TOKEN","repeatedByDataset","refsOf","records","slot","refs","aiLayoutRefViolations","aiLayoutWithoutRefs","labels","pages","page","before","after","aiLayoutCopyCheck","last","violations","gaps"],"mappings":"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"}