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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.d.ts +1 -1
  3. package/src/lib/activity/ai-activity-actions.js +1 -0
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/assets/site-styles/beauty.webp +0 -0
  6. package/src/lib/assets/site-styles/blog.webp +0 -0
  7. package/src/lib/assets/site-styles/business.webp +0 -0
  8. package/src/lib/assets/site-styles/coming-soon.webp +0 -0
  9. package/src/lib/assets/site-styles/education.webp +0 -0
  10. package/src/lib/assets/site-styles/events.webp +0 -0
  11. package/src/lib/assets/site-styles/fitness.webp +0 -0
  12. package/src/lib/assets/site-styles/landing.webp +0 -0
  13. package/src/lib/assets/site-styles/music.webp +0 -0
  14. package/src/lib/assets/site-styles/nonprofit.webp +0 -0
  15. package/src/lib/assets/site-styles/personal.webp +0 -0
  16. package/src/lib/assets/site-styles/photography.webp +0 -0
  17. package/src/lib/assets/site-styles/portfolio.webp +0 -0
  18. package/src/lib/assets/site-styles/professional.webp +0 -0
  19. package/src/lib/assets/site-styles/realestate.webp +0 -0
  20. package/src/lib/assets/site-styles/restaurant.webp +0 -0
  21. package/src/lib/assets/site-styles/store.webp +0 -0
  22. package/src/lib/assets/site-styles/studio.webp +0 -0
  23. package/src/lib/assets/site-styles/trades.webp +0 -0
  24. package/src/lib/assets/site-styles/wellness.webp +0 -0
  25. package/src/lib/assets/site-styles/yoga.webp +0 -0
  26. package/src/lib/components/ai-credits-card.component.d.ts +4 -3
  27. package/src/lib/components/ai-credits-card.component.js +15 -15
  28. package/src/lib/components/ai-credits-card.component.js.map +1 -1
  29. package/src/lib/components/ai-job-input-form.component.d.ts +36 -0
  30. package/src/lib/components/ai-job-input-form.component.js +100 -0
  31. package/src/lib/components/ai-job-input-form.component.js.map +1 -0
  32. package/src/lib/components/ai-job-requests.d.ts +4 -0
  33. package/src/lib/components/ai-job-requests.js +4 -0
  34. package/src/lib/components/ai-job-requests.js.map +1 -1
  35. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  36. package/src/lib/components/ai-site-start-card.component.js +99 -31
  37. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  38. package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
  39. package/src/lib/components/ai-site-start-steps.component.js +651 -15
  40. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  41. package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
  42. package/src/lib/components/ai-site-start-tracking.js +14 -0
  43. package/src/lib/components/ai-site-start-tracking.js.map +1 -1
  44. package/src/lib/components/ai-site-style-previews.js +26 -22
  45. package/src/lib/components/ai-site-style-previews.js.map +1 -1
  46. package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
  47. package/src/lib/components/ai-upsell-dialog.component.js +8 -6
  48. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  49. package/src/lib/components/assist-build-card.component.js +11 -1
  50. package/src/lib/components/assist-build-card.component.js.map +1 -1
  51. package/src/lib/components/assist-jobs-drawer.component.js +9 -1
  52. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  53. package/src/lib/components/assist-panel.component.js +8 -4
  54. package/src/lib/components/assist-panel.component.js.map +1 -1
  55. package/src/lib/components/staff-org-ai-card.component.js +4 -7
  56. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  57. package/src/lib/declarations.d.ts +2 -2
  58. package/src/lib/declarations.js +9 -5
  59. package/src/lib/declarations.js.map +1 -1
  60. package/src/lib/jobs/ai-build-capabilities.js +3 -1
  61. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  62. package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
  63. package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
  64. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
  65. package/src/lib/jobs/ai-job-build-step.js +5 -3
  66. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-form-step.d.ts +15 -1
  68. package/src/lib/jobs/ai-job-form-step.js +75 -8
  69. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  70. package/src/lib/jobs/ai-job-page-language.d.ts +13 -0
  71. package/src/lib/jobs/ai-job-page-language.js +37 -1
  72. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  73. package/src/lib/jobs/ai-job-page-step.d.ts +17 -0
  74. package/src/lib/jobs/ai-job-page-step.js +37 -16
  75. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  76. package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
  77. package/src/lib/jobs/ai-job-plan-step.js +103 -29
  78. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  79. package/src/lib/jobs/ai-job-settings-step.js +4 -3
  80. package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
  81. package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
  82. package/src/lib/jobs/ai-job-site-datasets.js +140 -14
  83. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  84. package/src/lib/jobs/ai-job-site-look.js +18 -5
  85. package/src/lib/jobs/ai-job-site-look.js.map +1 -1
  86. package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
  87. package/src/lib/jobs/ai-job-site-step.js +95 -22
  88. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  89. package/src/lib/jobs/ai-job-text-step.d.ts +8 -1
  90. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  91. package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
  92. package/src/lib/jobs/ai-job-tracking-step.js +28 -5
  93. package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
  94. package/src/lib/jobs/ai-jobs.d.ts +8 -1
  95. package/src/lib/jobs/ai-jobs.js +63 -8
  96. package/src/lib/jobs/ai-jobs.js.map +1 -1
  97. package/src/lib/jobs/ai-layout-stock-photos.d.ts +27 -2
  98. package/src/lib/jobs/ai-layout-stock-photos.js +190 -19
  99. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  100. package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
  101. package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
  102. package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
  103. package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
  104. package/src/lib/jobs/ai-site-inspiration.js +346 -0
  105. package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
  106. package/src/lib/jobs/ai-site-publish.d.ts +26 -1
  107. package/src/lib/jobs/ai-site-publish.js +162 -13
  108. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  109. package/src/lib/jobs/ai-theme-brand-inputs.d.ts +16 -0
  110. package/src/lib/jobs/ai-theme-brand-inputs.js +18 -4
  111. package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
  112. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
  113. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
  114. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
  115. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
  116. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
  117. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
  118. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
  119. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
  120. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
  121. package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
  122. package/src/lib/layout-language/ai-layout-compiler.js +438 -25
  123. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  124. package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
  125. package/src/lib/layout-language/ai-layout-contact.js +137 -0
  126. package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
  127. package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
  128. package/src/lib/layout-language/ai-layout-copy.js +34 -2
  129. package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
  130. package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
  131. package/src/lib/layout-language/ai-layout-design.js +411 -97
  132. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  133. package/src/lib/layout-language/ai-layout-frame.js +23 -0
  134. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  135. package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
  136. package/src/lib/layout-language/ai-layout-labels.js +12 -6
  137. package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
  138. package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
  139. package/src/lib/layout-language/ai-layout-language.js +4 -3
  140. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  141. package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
  142. package/src/lib/layout-language/ai-layout-links.js +30 -0
  143. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  144. package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
  145. package/src/lib/layout-language/ai-layout-pictures.js +145 -22
  146. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  147. package/src/lib/model/ai-build-job.d.ts +16 -1
  148. package/src/lib/model/ai-build-job.js +40 -0
  149. package/src/lib/model/ai-build-job.js.map +1 -1
  150. package/src/lib/model/ai-job-input.d.ts +33 -0
  151. package/src/lib/model/ai-job-input.js +59 -0
  152. package/src/lib/model/ai-job-input.js.map +1 -0
  153. package/src/lib/model/ai-job-notice.d.ts +4 -0
  154. package/src/lib/model/ai-job-notice.js +11 -1
  155. package/src/lib/model/ai-job-notice.js.map +1 -1
  156. package/src/lib/model/ai-jobs.types.d.ts +40 -1
  157. package/src/lib/model/ai-jobs.types.js.map +1 -1
  158. package/src/lib/model/ai-site-details.d.ts +122 -0
  159. package/src/lib/model/ai-site-details.js +300 -0
  160. package/src/lib/model/ai-site-details.js.map +1 -0
  161. package/src/lib/model/ai-site-entity.d.ts +181 -0
  162. package/src/lib/model/ai-site-entity.js +390 -0
  163. package/src/lib/model/ai-site-entity.js.map +1 -0
  164. package/src/lib/model/ai-site-inspiration.d.ts +72 -0
  165. package/src/lib/model/ai-site-inspiration.js +109 -0
  166. package/src/lib/model/ai-site-inspiration.js.map +1 -0
  167. package/src/lib/model/ai-site-job.d.ts +13 -1
  168. package/src/lib/model/ai-site-job.js +15 -2
  169. package/src/lib/model/ai-site-job.js.map +1 -1
  170. package/src/lib/model/ai-site-kinds.js +109 -21
  171. package/src/lib/model/ai-site-kinds.js.map +1 -1
  172. package/src/lib/model/ai-site-look.d.ts +10 -0
  173. package/src/lib/model/ai-site-look.js +24 -0
  174. package/src/lib/model/ai-site-look.js.map +1 -1
  175. package/src/lib/model/ai-site-start-seo.d.ts +19 -5
  176. package/src/lib/model/ai-site-start-seo.js +25 -7
  177. package/src/lib/model/ai-site-start-seo.js.map +1 -1
  178. package/src/lib/model/ai-site-start-steps.d.ts +9 -3
  179. package/src/lib/model/ai-site-start-steps.js +67 -5
  180. package/src/lib/model/ai-site-start-steps.js.map +1 -1
  181. package/src/lib/model/ai-site-start.d.ts +68 -4
  182. package/src/lib/model/ai-site-start.js +166 -13
  183. package/src/lib/model/ai-site-start.js.map +1 -1
  184. package/src/lib/model/ai-upsell-copy.js +4 -1
  185. package/src/lib/model/ai-upsell-copy.js.map +1 -1
  186. package/src/lib/plan-entitlements.d.ts +4 -3
  187. package/src/lib/plan-entitlements.js +3 -8
  188. package/src/lib/plan-entitlements.js.map +1 -1
  189. package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
  190. package/src/lib/runtime/ai-dataset-generation.js +12 -2
  191. package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
  192. package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
  193. package/src/lib/runtime/ai-doctrine-validators.js +54 -44
  194. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  195. package/src/lib/runtime/ai-doctrine.js +1 -1
  196. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  197. package/src/lib/runtime/ai-node-tree.d.ts +7 -0
  198. package/src/lib/runtime/ai-node-tree.js +9 -1
  199. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  200. package/src/lib/server/ai-create-route.d.ts +1 -1
  201. package/src/lib/server/ai-create-route.js +3 -4
  202. package/src/lib/server/ai-create-route.js.map +1 -1
  203. package/src/lib/server/ai-jobs-resume.js +27 -3
  204. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  205. package/src/lib/server/ai-jobs-route.js +7 -0
  206. package/src/lib/server/ai-jobs-route.js.map +1 -1
  207. package/src/lib/usage/assist-credits.d.ts +4 -4
  208. package/src/lib/usage/assist-credits.js +4 -4
  209. package/src/lib/usage/assist-credits.js.map +1 -1
  210. package/src/lib/usage/staff-org-ai-view.js +6 -3
  211. package/src/lib/usage/staff-org-ai-view.js.map +1 -1
  212. package/src/lib/usage/staff-org-ai.d.ts +4 -2
  213. package/src/lib/usage/staff-org-ai.js +7 -7
  214. package/src/lib/usage/staff-org-ai.js.map +1 -1
  215. package/src/lib/usage/staff-org-usage-ai.js +2 -2
  216. package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
@@ -40,7 +40,28 @@
40
40
  * many items each shows are still the model's, page by page.
41
41
  *
42
42
  * Pure: no tree, no theme, no network.
43
- */ /** What the compiler is told about the site a page belongs to. */ const FAMILIES = {
43
+ */ /** What the compiler is told about the site a page belongs to. */ /** Every hero arrangement, as {@link AiLayoutHeroVariant} lists them. */ export const AI_LAYOUT_HERO_VARIANTS = [
44
+ 'cover',
45
+ 'poster',
46
+ 'split',
47
+ 'bleed',
48
+ 'editorial',
49
+ 'column',
50
+ 'collage',
51
+ 'panel',
52
+ 'portrait'
53
+ ];
54
+ /** The heroes whose words take half the page or less, so their title is the page's title size, never display. */ export const AI_LAYOUT_HALF_HEROES = new Set([
55
+ 'split',
56
+ 'bleed',
57
+ 'collage',
58
+ 'panel'
59
+ ]);
60
+ /** The heroes that lay their words over a photo, which pins the section to the site's dark scheme. */ export const AI_LAYOUT_OVER_PHOTO_HEROES = new Set([
61
+ 'cover',
62
+ 'poster'
63
+ ]);
64
+ const FAMILIES = {
44
65
  portfolio: 'gallery',
45
66
  photography: 'gallery',
46
67
  studio: 'gallery',
@@ -52,75 +73,382 @@
52
73
  beauty: 'calm',
53
74
  store: 'retail'
54
75
  };
55
- /** The heroes each kind opens with, best suited first. */ const HEROES = {
56
- photography: [
57
- 'cover',
58
- 'editorial'
59
- ],
60
- portfolio: [
61
- 'editorial',
62
- 'split'
63
- ],
64
- studio: [
65
- 'editorial',
66
- 'cover'
67
- ],
68
- blog: [
69
- 'editorial',
70
- 'cover',
71
- 'split'
72
- ],
73
- restaurant: [
74
- 'cover',
75
- 'split'
76
- ],
77
- events: [
78
- 'cover',
79
- 'split'
80
- ],
81
- fitness: [
82
- 'cover',
83
- 'split'
84
- ],
85
- music: [
86
- 'cover',
87
- 'editorial'
88
- ],
89
- nonprofit: [
90
- 'cover',
91
- 'split'
92
- ],
93
- realestate: [
94
- 'cover',
95
- 'split'
96
- ],
97
- trades: [
98
- 'cover',
99
- 'split'
100
- ],
101
- yoga: [
102
- 'split',
103
- 'cover'
104
- ],
105
- wellness: [
106
- 'split',
107
- 'cover'
108
- ],
109
- beauty: [
110
- 'split',
111
- 'cover',
112
- 'editorial'
113
- ],
114
- store: [
115
- 'split',
116
- 'editorial'
117
- ]
118
- };
119
- const DEFAULT_HEROES = [
120
- 'split',
121
- 'cover',
122
- 'editorial'
76
+ const BOTH_STEPS = [
77
+ 'timeline',
78
+ 'numbers'
79
+ ];
80
+ const MOSTLY_MOSAIC = [
81
+ 'mosaic',
82
+ 'mosaic',
83
+ 'even'
123
84
  ];
85
+ /** Each kind's signature, by its `AI_SITE_KINDS` id. */ export const AI_LAYOUT_KIND_SIGNATURES = {
86
+ // Clear and confident: a contained split, or the words on a card over the place.
87
+ business: {
88
+ heroes: [
89
+ 'split',
90
+ 'panel'
91
+ ],
92
+ groups: [
93
+ 'cards',
94
+ 'icons'
95
+ ],
96
+ steps: BOTH_STEPS,
97
+ features: 0.6,
98
+ coverClose: 0.3,
99
+ splitHeads: 0.5,
100
+ pictures: MOSTLY_MOSAIC
101
+ },
102
+ // Direct: a full-bleed job photo with the promise at the start, services as tight cards.
103
+ trades: {
104
+ heroes: [
105
+ 'cover',
106
+ 'split'
107
+ ],
108
+ groups: [
109
+ 'cards'
110
+ ],
111
+ steps: [
112
+ 'numbers'
113
+ ],
114
+ features: 0.5,
115
+ coverClose: 0.7,
116
+ splitHeads: 0.2,
117
+ pictures: MOSTLY_MOSAIC
118
+ },
119
+ // Measured: the words on a card over the office, practice areas ruled, headings beside them.
120
+ professional: {
121
+ heroes: [
122
+ 'panel',
123
+ 'editorial'
124
+ ],
125
+ groups: [
126
+ 'ruled'
127
+ ],
128
+ steps: [
129
+ 'timeline'
130
+ ],
131
+ features: 0.5,
132
+ coverClose: 0.2,
133
+ splitHeads: 0.8,
134
+ pictures: MOSTLY_MOSAIC
135
+ },
136
+ // Reassuring: half the band a calm room, or a quiet centered masthead.
137
+ wellness: {
138
+ heroes: [
139
+ 'bleed',
140
+ 'column'
141
+ ],
142
+ groups: [
143
+ 'icons',
144
+ 'cards'
145
+ ],
146
+ steps: BOTH_STEPS,
147
+ features: 0.7,
148
+ coverClose: 0.2,
149
+ splitHeads: 0.3,
150
+ pictures: MOSTLY_MOSAIC
151
+ },
152
+ // Photo-led: the room full-bleed with the name centered over it, the menu as lines.
153
+ restaurant: {
154
+ heroes: [
155
+ 'poster',
156
+ 'bleed'
157
+ ],
158
+ groups: [
159
+ 'ruled',
160
+ 'cards'
161
+ ],
162
+ steps: BOTH_STEPS,
163
+ features: 0.8,
164
+ coverClose: 0.7,
165
+ splitHeads: 0.2,
166
+ pictures: MOSTLY_MOSAIC
167
+ },
168
+ // A storefront: the products themselves, two of them, beside the words.
169
+ store: {
170
+ heroes: [
171
+ 'collage',
172
+ 'split'
173
+ ],
174
+ groups: [
175
+ 'icons'
176
+ ],
177
+ steps: BOTH_STEPS,
178
+ features: 1,
179
+ coverClose: 0.2,
180
+ splitHeads: 0.35,
181
+ pictures: MOSTLY_MOSAIC
182
+ },
183
+ // The work first: display type then a wide picture, or the words beside two pieces.
184
+ portfolio: {
185
+ heroes: [
186
+ 'editorial',
187
+ 'collage'
188
+ ],
189
+ groups: [
190
+ 'ruled'
191
+ ],
192
+ steps: [
193
+ 'timeline'
194
+ ],
195
+ features: 0.5,
196
+ coverClose: 0.2,
197
+ splitHeads: 0.5,
198
+ pictures: [
199
+ 'mosaic'
200
+ ]
201
+ },
202
+ // Oversized statements over a wide picture, or full-bleed and dark.
203
+ studio: {
204
+ heroes: [
205
+ 'editorial',
206
+ 'cover'
207
+ ],
208
+ groups: [
209
+ 'ruled',
210
+ 'cards'
211
+ ],
212
+ steps: [
213
+ 'timeline'
214
+ ],
215
+ features: 0.4,
216
+ coverClose: 0.5,
217
+ splitHeads: 0.6,
218
+ pictures: [
219
+ 'mosaic',
220
+ 'even'
221
+ ]
222
+ },
223
+ // Pictures first, few words.
224
+ photography: {
225
+ heroes: [
226
+ 'editorial',
227
+ 'poster'
228
+ ],
229
+ groups: [
230
+ 'ruled'
231
+ ],
232
+ steps: [
233
+ 'timeline'
234
+ ],
235
+ features: 0.6,
236
+ coverClose: 0.6,
237
+ splitHeads: 0.35,
238
+ pictures: [
239
+ 'even',
240
+ 'mosaic'
241
+ ]
242
+ },
243
+ // Reading-first: a centered masthead over the lead picture.
244
+ blog: {
245
+ heroes: [
246
+ 'column',
247
+ 'editorial'
248
+ ],
249
+ groups: [
250
+ 'ruled'
251
+ ],
252
+ steps: [
253
+ 'timeline'
254
+ ],
255
+ features: 0.4,
256
+ coverClose: 0.1,
257
+ splitHeads: 0.7,
258
+ pictures: MOSTLY_MOSAIC
259
+ },
260
+ // Celebratory: two pictures of the day beside its name, or the name over one.
261
+ events: {
262
+ heroes: [
263
+ 'collage',
264
+ 'poster'
265
+ ],
266
+ groups: [
267
+ 'cards',
268
+ 'icons'
269
+ ],
270
+ steps: [
271
+ 'timeline'
272
+ ],
273
+ features: 0.7,
274
+ coverClose: 0.6,
275
+ splitHeads: 0.35,
276
+ pictures: MOSTLY_MOSAIC
277
+ },
278
+ // High energy: half the band a photo, half a loud headline; or full-bleed.
279
+ fitness: {
280
+ heroes: [
281
+ 'bleed',
282
+ 'cover'
283
+ ],
284
+ groups: [
285
+ 'cards'
286
+ ],
287
+ steps: [
288
+ 'numbers'
289
+ ],
290
+ features: 0.5,
291
+ coverClose: 0.7,
292
+ splitHeads: 0.2,
293
+ pictures: MOSTLY_MOSAIC
294
+ },
295
+ // Calm and spacious: a centered masthead, or a soft split.
296
+ yoga: {
297
+ heroes: [
298
+ 'column',
299
+ 'split'
300
+ ],
301
+ groups: [
302
+ 'icons',
303
+ 'cards'
304
+ ],
305
+ steps: BOTH_STEPS,
306
+ features: 0.6,
307
+ coverClose: 0.2,
308
+ splitHeads: 0.2,
309
+ pictures: MOSTLY_MOSAIC
310
+ },
311
+ // Image-led: two pictures of the work and the place, or half the band one.
312
+ beauty: {
313
+ heroes: [
314
+ 'collage',
315
+ 'bleed'
316
+ ],
317
+ groups: [
318
+ 'ruled',
319
+ 'cards'
320
+ ],
321
+ steps: BOTH_STEPS,
322
+ features: 0.8,
323
+ coverClose: 0.3,
324
+ splitHeads: 0.35,
325
+ pictures: MOSTLY_MOSAIC
326
+ },
327
+ // Polished: the home full-bleed with the words on a card, or under a scrim.
328
+ realestate: {
329
+ heroes: [
330
+ 'panel',
331
+ 'cover'
332
+ ],
333
+ groups: [
334
+ 'cards',
335
+ 'ruled'
336
+ ],
337
+ steps: BOTH_STEPS,
338
+ features: 0.8,
339
+ coverClose: 0.5,
340
+ splitHeads: 0.35,
341
+ pictures: MOSTLY_MOSAIC
342
+ },
343
+ // Encouraging: a contained split, or the teacher's portrait over the words.
344
+ education: {
345
+ heroes: [
346
+ 'split',
347
+ 'portrait'
348
+ ],
349
+ groups: [
350
+ 'icons',
351
+ 'cards'
352
+ ],
353
+ steps: [
354
+ 'numbers'
355
+ ],
356
+ features: 0.6,
357
+ coverClose: 0.2,
358
+ splitHeads: 0.35,
359
+ pictures: MOSTLY_MOSAIC
360
+ },
361
+ // Mission-led: the people full-bleed with the cause over them, or two pictures beside it.
362
+ nonprofit: {
363
+ heroes: [
364
+ 'cover',
365
+ 'collage'
366
+ ],
367
+ groups: [
368
+ 'icons',
369
+ 'cards'
370
+ ],
371
+ steps: [
372
+ 'numbers'
373
+ ],
374
+ features: 0.6,
375
+ coverClose: 0.7,
376
+ splitHeads: 0.35,
377
+ pictures: MOSTLY_MOSAIC
378
+ },
379
+ // Loud: the name huge over a full-bleed photo, at the start or centered.
380
+ music: {
381
+ heroes: [
382
+ 'cover',
383
+ 'poster'
384
+ ],
385
+ groups: [
386
+ 'ruled'
387
+ ],
388
+ steps: [
389
+ 'timeline'
390
+ ],
391
+ features: 0.5,
392
+ coverClose: 0.8,
393
+ splitHeads: 0.35,
394
+ pictures: MOSTLY_MOSAIC
395
+ },
396
+ // A person: their portrait over who they are, or a quiet masthead.
397
+ personal: {
398
+ heroes: [
399
+ 'portrait',
400
+ 'column'
401
+ ],
402
+ groups: [
403
+ 'ruled'
404
+ ],
405
+ steps: [
406
+ 'timeline'
407
+ ],
408
+ features: 0.4,
409
+ coverClose: 0.1,
410
+ splitHeads: 0.6,
411
+ pictures: MOSTLY_MOSAIC
412
+ },
413
+ // One action: the promise centered over the picture of the thing, or over a photo.
414
+ landing: {
415
+ heroes: [
416
+ 'column',
417
+ 'poster'
418
+ ],
419
+ groups: [
420
+ 'icons',
421
+ 'cards'
422
+ ],
423
+ steps: [
424
+ 'numbers'
425
+ ],
426
+ features: 0.5,
427
+ coverClose: 0.3,
428
+ splitHeads: 0.2,
429
+ pictures: MOSTLY_MOSAIC
430
+ },
431
+ // Short and striking: one big line over a photo, or on a card over it.
432
+ 'coming-soon': {
433
+ heroes: [
434
+ 'poster',
435
+ 'panel'
436
+ ],
437
+ groups: [
438
+ 'icons',
439
+ 'ruled'
440
+ ],
441
+ steps: BOTH_STEPS,
442
+ features: 0.3,
443
+ coverClose: 0.2,
444
+ splitHeads: 0.2,
445
+ pictures: MOSTLY_MOSAIC
446
+ }
447
+ };
448
+ /** The signature of a kind; an unknown kind draws as a business site. */ export function aiLayoutKindSignature(kind) {
449
+ var _AI_LAYOUT_KIND_SIGNATURES_kind;
450
+ return (_AI_LAYOUT_KIND_SIGNATURES_kind = AI_LAYOUT_KIND_SIGNATURES[kind]) != null ? _AI_LAYOUT_KIND_SIGNATURES_kind : AI_LAYOUT_KIND_SIGNATURES['business'];
451
+ }
124
452
  /** A small deterministic generator over a seed (mulberry32). */ function random(seed) {
125
453
  let state = seed >>> 0;
126
454
  return ()=>{
@@ -138,22 +466,18 @@ const pick = (next, list)=>list[Math.floor(next() * list.length) % list.length];
138
466
  }
139
467
  /**
140
468
  * A site's choices, the same for every page of it, each drawn at even odds
141
- * among the options that suit the kind (AGL-3899), so two sites of one kind
142
- * are drawn apart. Favoring each list's first option made the newest
143
- * production sites one design: the kind's first hero won 55% before the
144
- * pick, a business site opened split, groups were ruled two times in three
145
- * and every text section took a photo.
469
+ * among the options its kind's signature lists (AGL-3899, AGL-3985), so two
470
+ * sites of one kind are drawn apart and two kinds never read as one page.
471
+ * Favoring each list's first option made the newest production sites one
472
+ * design: the kind's first hero won 55% before the pick, a business site
473
+ * opened split, groups were ruled two times in three and every text section
474
+ * took a photo.
146
475
  */ export function aiLayoutDesignChoices(design) {
147
- var _HEROES_design_kind;
148
476
  const next = random(design.seed ^ 0x2f6b9d1);
149
477
  const family = aiLayoutFamily(design.kind);
150
- const heroes = (_HEROES_design_kind = HEROES[design.kind]) != null ? _HEROES_design_kind : DEFAULT_HEROES;
151
- const hero = pick(next, heroes);
152
- const group = pick(next, [
153
- 'cards',
154
- 'ruled',
155
- 'icons'
156
- ]);
478
+ const signature = aiLayoutKindSignature(design.kind);
479
+ const hero = pick(next, signature.heroes);
480
+ const group = pick(next, signature.groups);
157
481
  return {
158
482
  hero,
159
483
  // A store's reasons to buy — the making, the shipping, the care — read
@@ -162,25 +486,15 @@ const pick = (next, list)=>list[Math.floor(next() * list.length) % list.length];
162
486
  // of a title and a sentence the beta.237 Willow Wick home showed four
163
487
  // bands of (AGL-3676).
164
488
  group: family === 'retail' ? 'icons' : group,
165
- steps: pick(next, [
166
- 'timeline',
167
- 'numbers'
168
- ]),
489
+ steps: pick(next, signature.steps),
169
490
  // Whether a section of words takes a photo beside it: some sites are
170
491
  // photo-led, some let their type carry the page. A storefront never
171
492
  // shows a band of text alone (AGL-3676), so its story always takes one.
172
- features: next() < 0.6 || family === 'retail',
493
+ features: next() < signature.features || family === 'retail',
173
494
  featureLeft: next() < 0.5,
174
- coverClose: heroes.includes('cover') && next() < 0.7,
175
- splitHeads: family === 'editorial' || family === 'standard' ? next() < 0.6 : next() < 0.35,
176
- pictures: design.kind === 'photography' ? pick(next, [
177
- 'even',
178
- 'mosaic'
179
- ]) : pick(next, [
180
- 'mosaic',
181
- 'mosaic',
182
- 'even'
183
- ]),
495
+ coverClose: next() < signature.coverClose,
496
+ splitHeads: next() < signature.splitHeads,
497
+ pictures: pick(next, signature.pictures),
184
498
  // Drawn last, so every choice above stays what it was for a seed.
185
499
  writing: pick(next, [
186
500
  'grid',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-design.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\n/**\n * THE DESIGNER LAYER of the layout compiler (AGL-3660): how a page the model\n * described in the layout language is ARRANGED, decided by code from the\n * site's kind and its job's seed — never asked of the model, so a recorded\n * answer compiles into the new design with no new call.\n *\n * Why. A page compiled one way for every site read like a SaaS landing page:\n * a centered text hero, three outlined cards, a numbered row, a solid form\n * band. The designer templates people compare a site to (Squarespace's Loam,\n * Clove, Auburn, Rivoli, Matsuya; Wix's portfolio and restaurant sets) are\n * built from a handful of moves instead — a full-bleed photo hero with the\n * type over it, image and text side by side, work shown as pictures in an\n * uneven grid, writing as a list under thin rules, a closing band over a\n * photo — and they pick those moves for the KIND of site.\n *\n * So a site's design is a set of VARIANTS — the hero's arrangement, how its\n * groups are drawn, whether its text sections take a picture beside them,\n * how its closing section reads — each chosen among the ones that suit the\n * kind, by the seed. The choices are the site's: every page of one site is\n * drawn with the same hero and the same group style, so the site reads as\n * one design, and two sites of one kind come out differently.\n *\n * This is NOT a template library. A variant is a way of drawing whatever the\n * model wrote; the sections, their order, their bands, their words and how\n * many items each shows are still the model's, page by page.\n *\n * Pure: no tree, no theme, no network.\n */\n\n/** What the compiler is told about the site a page belongs to. */\nexport interface AiLayoutDesign {\n /** The site's kind (`AI_SITE_KINDS`); an unknown one draws as a business site. */\n kind: string\n /** The job's design seed: the same for every page of one site. */\n seed: number\n /** Whether this page is the site's home, which opens tallest. */\n home: boolean\n}\n\n/**\n * How the first section of a page is drawn:\n * - `cover`: a full-bleed photo with the words over it on a scrim;\n * - `split`: the words beside a tall photo, inside the page's measure;\n * - `editorial`: display type across the page, the lede and the buttons in\n * a row under it, and a wide photo below.\n */\nexport type AiLayoutHeroVariant = 'cover' | 'split' | 'editorial'\n\n/**\n * How a group of cards is drawn where no component draws it:\n * - `cards`: the theme's cards (the compiler's first design);\n * - `ruled`: open columns, each under a thin rule, no card around it;\n * - `pictures`: a picture over each item's title and caption, in an uneven\n * grid (work, products);\n * - `articles`: a picture over each title and summary, in even columns (writing);\n * - `menu`: two columns of lines under rules (dishes, prices left out);\n * - `icons`: open columns, each led by an icon in a soft disc over its title\n * and words — a storefront's reasons to buy (shipping, small batch,\n * returns), never a block of text (AGL-3676).\n */\nexport type AiLayoutGroupVariant = 'cards' | 'ruled' | 'pictures' | 'articles' | 'menu' | 'icons'\n\n/** How a numbered group is drawn: a row of numbers, or a ruled list beside its heading. */\nexport type AiLayoutStepsVariant = 'numbers' | 'timeline'\n\n/** The site-level choices a page is drawn with. */\nexport interface AiLayoutDesignChoices {\n hero: AiLayoutHeroVariant\n /** The group style of a section that names no subject the kind draws its own way. */\n group: 'cards' | 'ruled' | 'icons'\n steps: AiLayoutStepsVariant\n /** Whether a section of words takes a photo beside it. */\n features: boolean\n /** Whether the photo of the first such section sits on the left. */\n featureLeft: boolean\n /** Whether a closing section of words is drawn over a photo. */\n coverClose: boolean\n /** Whether a section's heading sits beside its items rather than over them. */\n splitHeads: boolean\n /** How work is laid out where the kind shows it as pictures. */\n pictures: 'mosaic' | 'even'\n /**\n * How a blog's own posts are listed where a page shows them (AGL-3676):\n * `grid`, a cover over each post's date, title and excerpt in columns; or\n * `ruled`, a list under thin rules, the date in its own column and a small\n * cover beside each title — the two ways an editorial site lists writing.\n */\n writing: 'grid' | 'ruled'\n}\n\ntype Family = 'gallery' | 'editorial' | 'hospitality' | 'calm' | 'retail' | 'standard'\n\nconst FAMILIES: Readonly<Record<string, Family>> = {\n portfolio: 'gallery',\n photography: 'gallery',\n studio: 'gallery',\n blog: 'editorial',\n restaurant: 'hospitality',\n events: 'hospitality',\n yoga: 'calm',\n wellness: 'calm',\n beauty: 'calm',\n store: 'retail',\n}\n\n/** The heroes each kind opens with, best suited first. */\nconst HEROES: Readonly<Record<string, readonly AiLayoutHeroVariant[]>> = {\n photography: ['cover', 'editorial'],\n portfolio: ['editorial', 'split'],\n studio: ['editorial', 'cover'],\n blog: ['editorial', 'cover', 'split'],\n restaurant: ['cover', 'split'],\n events: ['cover', 'split'],\n fitness: ['cover', 'split'],\n music: ['cover', 'editorial'],\n nonprofit: ['cover', 'split'],\n realestate: ['cover', 'split'],\n trades: ['cover', 'split'],\n yoga: ['split', 'cover'],\n wellness: ['split', 'cover'],\n beauty: ['split', 'cover', 'editorial'],\n store: ['split', 'editorial'],\n}\nconst DEFAULT_HEROES: readonly AiLayoutHeroVariant[] = ['split', 'cover', 'editorial']\n\n/** A small deterministic generator over a seed (mulberry32). */\nfunction random(seed: number): () => number {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let t = state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nconst pick = <T>(next: () => number, list: readonly T[]): T => list[Math.floor(next() * list.length) % list.length] as T\n\n/** The family a kind's pages are drawn in. */\nexport function aiLayoutFamily(kind: string): Family {\n return FAMILIES[kind] ?? 'standard'\n}\n\n/**\n * A site's choices, the same for every page of it, each drawn at even odds\n * among the options that suit the kind (AGL-3899), so two sites of one kind\n * are drawn apart. Favoring each list's first option made the newest\n * production sites one design: the kind's first hero won 55% before the\n * pick, a business site opened split, groups were ruled two times in three\n * and every text section took a photo.\n */\nexport function aiLayoutDesignChoices(design: AiLayoutDesign): AiLayoutDesignChoices {\n const next = random(design.seed ^ 0x2f6b9d1)\n const family = aiLayoutFamily(design.kind)\n const heroes = HEROES[design.kind] ?? DEFAULT_HEROES\n const hero = pick(next, heroes)\n const group = pick(next, ['cards', 'ruled', 'icons'] as const)\n return {\n hero,\n // A store's reasons to buy — the making, the shipping, the care — read\n // as open columns each led by an icon, the way a storefront theme draws\n // them: never a SaaS page's feature cards, and never the plain columns\n // of a title and a sentence the beta.237 Willow Wick home showed four\n // bands of (AGL-3676).\n group: family === 'retail' ? 'icons' : group,\n steps: pick(next, ['timeline', 'numbers'] as const),\n // Whether a section of words takes a photo beside it: some sites are\n // photo-led, some let their type carry the page. A storefront never\n // shows a band of text alone (AGL-3676), so its story always takes one.\n features: next() < 0.6 || family === 'retail',\n featureLeft: next() < 0.5,\n coverClose: heroes.includes('cover') && next() < 0.7,\n splitHeads: family === 'editorial' || family === 'standard' ? next() < 0.6 : next() < 0.35,\n pictures: design.kind === 'photography' ? pick(next, ['even', 'mosaic'] as const) : pick(next, ['mosaic', 'mosaic', 'even'] as const),\n // Drawn last, so every choice above stays what it was for a seed.\n writing: pick(next, ['grid', 'ruled'] as const),\n }\n}\n\nconst WORK = /\\b(work|works|project|projects|portfolio|gallery|galleries|case|cases|studies|selected|commission|commissions|illustration|illustrations|photo|photos|series|book|books|editorial|brand|collection|collections|wedding|weddings|portrait|portraits|session|sessions|shoot|shoots)\\b/i\nconst WRITING = /\\b(article|articles|post|posts|writing|writings|latest|featured|journey|journeys|recipe|recipes|story|stories|essay|essays|guide|guides|issue|issues|episode|episodes)\\b/i\nconst DISHES = /\\b(menu|menus|dish|dishes|breakfast|brunch|lunch|dinner|pastry|pastries|coffee|drink|drinks|cocktail|cocktails|wine|wines|highlight|highlights|favorite|favorites|favourites|special|specials|plate|plates|bake|bakes|bread|cake|cakes)\\b/i\n// A store's own products are listed by its Product grid (AGL-3676), never drawn\n// from words: what a store shows as pictures is how its range is grouped — its\n// collections, gift sets, occasions and scents. \"Candle care and burn notes\" is no band of pictures.\n/** Words a section uses for itself when it is a showing of pictures, on any kind of site (AGL-3923). */\nconst GALLERY = /\\b(gallery|galleries|portfolio|portfolios|lookbook|lookbooks)\\b/i\nconst CATEGORIES = /\\b(collection|collections|range|ranges|categor(?:y|ies)|gift|gifts|gift sets?|sets|occasion|occasions|scent|scents|edit|edits)\\b/i\n\n/**\n * How a section's group of cards is drawn, from what the section is about:\n * work in a gallery kind and products in a store are pictures, writing in a\n * blog is articles, dishes in a restaurant a menu; anything else takes the\n * site's own group style.\n */\nexport function aiLayoutGroupVariant(\n design: AiLayoutDesign,\n choices: AiLayoutDesignChoices,\n words: string,\n): AiLayoutGroupVariant {\n const family = aiLayoutFamily(design.kind)\n if (family === 'gallery' && (WORK.test(words) || design.kind === 'photography')) return 'pictures'\n if (family === 'editorial' && WRITING.test(words)) return 'articles'\n if (family === 'hospitality' && DISHES.test(words)) return 'menu'\n if (family === 'retail' && CATEGORIES.test(words)) return 'pictures'\n // A section that calls itself a gallery or a portfolio shows pictures on any\n // kind of site (AGL-3923): the beta.265 Kasvora Marketing Solutions home drew\n // its \"Selected work gallery\" as four cards of icons.\n if (GALLERY.test(words)) return 'pictures'\n return choices.group\n}\n"],"names":["FAMILIES","portfolio","photography","studio","blog","restaurant","events","yoga","wellness","beauty","store","HEROES","fitness","music","nonprofit","realestate","trades","DEFAULT_HEROES","random","seed","state","t","Math","imul","pick","next","list","floor","length","aiLayoutFamily","kind","aiLayoutDesignChoices","design","family","heroes","hero","group","steps","features","featureLeft","coverClose","includes","splitHeads","pictures","writing","WORK","WRITING","DISHES","GALLERY","CATEGORIES","aiLayoutGroupVariant","choices","words","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-design.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\n/**\n * THE DESIGNER LAYER of the layout compiler (AGL-3660): how a page the model\n * described in the layout language is ARRANGED, decided by code from the\n * site's kind and its job's seed — never asked of the model, so a recorded\n * answer compiles into the new design with no new call.\n *\n * Why. A page compiled one way for every site read like a SaaS landing page:\n * a centered text hero, three outlined cards, a numbered row, a solid form\n * band. The designer templates people compare a site to (Squarespace's Loam,\n * Clove, Auburn, Rivoli, Matsuya; Wix's portfolio and restaurant sets) are\n * built from a handful of moves instead — a full-bleed photo hero with the\n * type over it, image and text side by side, work shown as pictures in an\n * uneven grid, writing as a list under thin rules, a closing band over a\n * photo — and they pick those moves for the KIND of site.\n *\n * So a site's design is a set of VARIANTS — the hero's arrangement, how its\n * groups are drawn, whether its text sections take a picture beside them,\n * how its closing section reads — each chosen among the ones that suit the\n * kind, by the seed. The choices are the site's: every page of one site is\n * drawn with the same hero and the same group style, so the site reads as\n * one design, and two sites of one kind come out differently.\n *\n * This is NOT a template library. A variant is a way of drawing whatever the\n * model wrote; the sections, their order, their bands, their words and how\n * many items each shows are still the model's, page by page.\n *\n * Pure: no tree, no theme, no network.\n */\n\n/** What the compiler is told about the site a page belongs to. */\nexport interface AiLayoutDesign {\n /** The site's kind (`AI_SITE_KINDS`); an unknown one draws as a business site. */\n kind: string\n /** The job's design seed: the same for every page of one site. */\n seed: number\n /** Whether this page is the site's home, which opens tallest. */\n home: boolean\n}\n\n/**\n * How the first section of a page is drawn:\n * - `cover`: a full-bleed photo with the words over it on a scrim, set at\n * the start of the page;\n * - `poster`: a full-bleed photo, taller, the words centered over it in\n * display type (AGL-3985);\n * - `split`: the words beside a tall photo, inside the page's measure;\n * - `bleed`: the band in two halves, a photo filling one edge to edge and\n * the words in the other (AGL-3985);\n * - `editorial`: display type across the page, the lede and the buttons in\n * a row under it, and a wide photo below;\n * - `column`: a masthead — the words centered in a narrow column, a wide\n * photo under them (AGL-3985);\n * - `collage`: the words beside two photos, a tall one and a smaller one\n * laid over its corner (AGL-3985);\n * - `panel`: a full-bleed photo, undimmed, with the words on a card of the\n * page's surface over its lower corner (AGL-3985);\n * - `portrait`: a round photo over the words, all centered: a person's own\n * page (AGL-3985).\n */\nexport type AiLayoutHeroVariant = 'cover' | 'poster' | 'split' | 'bleed' | 'editorial' | 'column' | 'collage' | 'panel' | 'portrait'\n\n/** Every hero arrangement, as {@link AiLayoutHeroVariant} lists them. */\nexport const AI_LAYOUT_HERO_VARIANTS: readonly AiLayoutHeroVariant[] = ['cover', 'poster', 'split', 'bleed', 'editorial', 'column', 'collage', 'panel', 'portrait']\n\n/** The heroes whose words take half the page or less, so their title is the page's title size, never display. */\nexport const AI_LAYOUT_HALF_HEROES: ReadonlySet<AiLayoutHeroVariant> = new Set(['split', 'bleed', 'collage', 'panel'])\n\n/** The heroes that lay their words over a photo, which pins the section to the site's dark scheme. */\nexport const AI_LAYOUT_OVER_PHOTO_HEROES: ReadonlySet<AiLayoutHeroVariant> = new Set(['cover', 'poster'])\n\n/**\n * How a group of cards is drawn where no component draws it:\n * - `cards`: the theme's cards (the compiler's first design);\n * - `ruled`: open columns, each under a thin rule, no card around it;\n * - `pictures`: a picture over each item's title and caption, in an uneven\n * grid (work, products);\n * - `articles`: a picture over each title and summary, in even columns (writing);\n * - `menu`: two columns of lines under rules (dishes, prices left out);\n * - `icons`: open columns, each led by an icon in a soft disc over its title\n * and words — a storefront's reasons to buy (shipping, small batch,\n * returns), never a block of text (AGL-3676).\n */\nexport type AiLayoutGroupVariant = 'cards' | 'ruled' | 'pictures' | 'articles' | 'menu' | 'icons'\n\n/** How a numbered group is drawn: a row of numbers, or a ruled list beside its heading. */\nexport type AiLayoutStepsVariant = 'numbers' | 'timeline'\n\n/** The site-level choices a page is drawn with. */\nexport interface AiLayoutDesignChoices {\n hero: AiLayoutHeroVariant\n /** The group style of a section that names no subject the kind draws its own way. */\n group: 'cards' | 'ruled' | 'icons'\n steps: AiLayoutStepsVariant\n /** Whether a section of words takes a photo beside it. */\n features: boolean\n /** Whether the photo of the first such section sits on the left. */\n featureLeft: boolean\n /** Whether a closing section of words is drawn over a photo. */\n coverClose: boolean\n /** Whether a section's heading sits beside its items rather than over them. */\n splitHeads: boolean\n /** How work is laid out where the kind shows it as pictures. */\n pictures: 'mosaic' | 'even'\n /**\n * How a blog's own posts are listed where a page shows them (AGL-3676):\n * `grid`, a cover over each post's date, title and excerpt in columns; or\n * `ruled`, a list under thin rules, the date in its own column and a small\n * cover beside each title — the two ways an editorial site lists writing.\n */\n writing: 'grid' | 'ruled'\n}\n\ntype Family = 'gallery' | 'editorial' | 'hospitality' | 'calm' | 'retail' | 'standard'\ntype GroupStyle = AiLayoutDesignChoices['group']\n\nconst FAMILIES: Readonly<Record<string, Family>> = {\n portfolio: 'gallery',\n photography: 'gallery',\n studio: 'gallery',\n blog: 'editorial',\n restaurant: 'hospitality',\n events: 'hospitality',\n yoga: 'calm',\n wellness: 'calm',\n beauty: 'calm',\n store: 'retail',\n}\n\n/**\n * A kind's LAYOUT SIGNATURE (AGL-3985): the arrangements its pages are drawn\n * in, so a café, a law firm and a band never read as one page in three\n * colors. Before it, six kinds shared one list of heroes and every kind drew\n * its groups, its photo-led sections and its headings beside their items at\n * the same odds; the guided start's 21 style previews, which the real\n * compiler draws, read as one page — a header bar, a split or banded hero,\n * one section.\n *\n * - `heroes`: the two first sections the kind opens with, each drawn at even\n * odds (AGL-3899). The first is the one its style card shows. No two kinds\n * share the same two.\n * - `groups`: the group styles a section of items takes where its subject\n * picks none.\n * - `steps`: how its numbered groups read.\n * - `features`: the chance a section of words takes a photo beside it — a\n * café's or a salon's pages are photo-led, a law firm's or a writer's let\n * the type carry them.\n * - `coverClose`: the chance a closing call to action is drawn over a photo.\n * - `splitHeads`: the chance a heading sits beside its items: an\n * editorial, considered page.\n * - `pictures`: how its work is laid out where it shows it as pictures.\n */\nexport interface AiLayoutKindSignature {\n heroes: readonly [AiLayoutHeroVariant, AiLayoutHeroVariant]\n groups: readonly GroupStyle[]\n steps: readonly AiLayoutStepsVariant[]\n features: number\n coverClose: number\n splitHeads: number\n pictures: readonly AiLayoutDesignChoices['pictures'][]\n}\n\nconst BOTH_STEPS: readonly AiLayoutStepsVariant[] = ['timeline', 'numbers']\nconst MOSTLY_MOSAIC: readonly AiLayoutDesignChoices['pictures'][] = ['mosaic', 'mosaic', 'even']\n\n/** Each kind's signature, by its `AI_SITE_KINDS` id. */\nexport const AI_LAYOUT_KIND_SIGNATURES: Readonly<Record<string, AiLayoutKindSignature>> = {\n // Clear and confident: a contained split, or the words on a card over the place.\n business: { heroes: ['split', 'panel'], groups: ['cards', 'icons'], steps: BOTH_STEPS, features: 0.6, coverClose: 0.3, splitHeads: 0.5, pictures: MOSTLY_MOSAIC },\n // Direct: a full-bleed job photo with the promise at the start, services as tight cards.\n trades: { heroes: ['cover', 'split'], groups: ['cards'], steps: ['numbers'], features: 0.5, coverClose: 0.7, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n // Measured: the words on a card over the office, practice areas ruled, headings beside them.\n professional: { heroes: ['panel', 'editorial'], groups: ['ruled'], steps: ['timeline'], features: 0.5, coverClose: 0.2, splitHeads: 0.8, pictures: MOSTLY_MOSAIC },\n // Reassuring: half the band a calm room, or a quiet centered masthead.\n wellness: { heroes: ['bleed', 'column'], groups: ['icons', 'cards'], steps: BOTH_STEPS, features: 0.7, coverClose: 0.2, splitHeads: 0.3, pictures: MOSTLY_MOSAIC },\n // Photo-led: the room full-bleed with the name centered over it, the menu as lines.\n restaurant: { heroes: ['poster', 'bleed'], groups: ['ruled', 'cards'], steps: BOTH_STEPS, features: 0.8, coverClose: 0.7, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n // A storefront: the products themselves, two of them, beside the words.\n store: { heroes: ['collage', 'split'], groups: ['icons'], steps: BOTH_STEPS, features: 1, coverClose: 0.2, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // The work first: display type then a wide picture, or the words beside two pieces.\n portfolio: { heroes: ['editorial', 'collage'], groups: ['ruled'], steps: ['timeline'], features: 0.5, coverClose: 0.2, splitHeads: 0.5, pictures: ['mosaic'] },\n // Oversized statements over a wide picture, or full-bleed and dark.\n studio: { heroes: ['editorial', 'cover'], groups: ['ruled', 'cards'], steps: ['timeline'], features: 0.4, coverClose: 0.5, splitHeads: 0.6, pictures: ['mosaic', 'even'] },\n // Pictures first, few words.\n photography: { heroes: ['editorial', 'poster'], groups: ['ruled'], steps: ['timeline'], features: 0.6, coverClose: 0.6, splitHeads: 0.35, pictures: ['even', 'mosaic'] },\n // Reading-first: a centered masthead over the lead picture.\n blog: { heroes: ['column', 'editorial'], groups: ['ruled'], steps: ['timeline'], features: 0.4, coverClose: 0.1, splitHeads: 0.7, pictures: MOSTLY_MOSAIC },\n // Celebratory: two pictures of the day beside its name, or the name over one.\n events: { heroes: ['collage', 'poster'], groups: ['cards', 'icons'], steps: ['timeline'], features: 0.7, coverClose: 0.6, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // High energy: half the band a photo, half a loud headline; or full-bleed.\n fitness: { heroes: ['bleed', 'cover'], groups: ['cards'], steps: ['numbers'], features: 0.5, coverClose: 0.7, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n // Calm and spacious: a centered masthead, or a soft split.\n yoga: { heroes: ['column', 'split'], groups: ['icons', 'cards'], steps: BOTH_STEPS, features: 0.6, coverClose: 0.2, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n // Image-led: two pictures of the work and the place, or half the band one.\n beauty: { heroes: ['collage', 'bleed'], groups: ['ruled', 'cards'], steps: BOTH_STEPS, features: 0.8, coverClose: 0.3, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // Polished: the home full-bleed with the words on a card, or under a scrim.\n realestate: { heroes: ['panel', 'cover'], groups: ['cards', 'ruled'], steps: BOTH_STEPS, features: 0.8, coverClose: 0.5, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // Encouraging: a contained split, or the teacher's portrait over the words.\n education: { heroes: ['split', 'portrait'], groups: ['icons', 'cards'], steps: ['numbers'], features: 0.6, coverClose: 0.2, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // Mission-led: the people full-bleed with the cause over them, or two pictures beside it.\n nonprofit: { heroes: ['cover', 'collage'], groups: ['icons', 'cards'], steps: ['numbers'], features: 0.6, coverClose: 0.7, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // Loud: the name huge over a full-bleed photo, at the start or centered.\n music: { heroes: ['cover', 'poster'], groups: ['ruled'], steps: ['timeline'], features: 0.5, coverClose: 0.8, splitHeads: 0.35, pictures: MOSTLY_MOSAIC },\n // A person: their portrait over who they are, or a quiet masthead.\n personal: { heroes: ['portrait', 'column'], groups: ['ruled'], steps: ['timeline'], features: 0.4, coverClose: 0.1, splitHeads: 0.6, pictures: MOSTLY_MOSAIC },\n // One action: the promise centered over the picture of the thing, or over a photo.\n landing: { heroes: ['column', 'poster'], groups: ['icons', 'cards'], steps: ['numbers'], features: 0.5, coverClose: 0.3, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n // Short and striking: one big line over a photo, or on a card over it.\n 'coming-soon': { heroes: ['poster', 'panel'], groups: ['icons', 'ruled'], steps: BOTH_STEPS, features: 0.3, coverClose: 0.2, splitHeads: 0.2, pictures: MOSTLY_MOSAIC },\n}\n\n/** The signature of a kind; an unknown kind draws as a business site. */\nexport function aiLayoutKindSignature(kind: string): AiLayoutKindSignature {\n return AI_LAYOUT_KIND_SIGNATURES[kind] ?? (AI_LAYOUT_KIND_SIGNATURES['business'] as AiLayoutKindSignature)\n}\n\n/** A small deterministic generator over a seed (mulberry32). */\nfunction random(seed: number): () => number {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let t = state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nconst pick = <T>(next: () => number, list: readonly T[]): T => list[Math.floor(next() * list.length) % list.length] as T\n\n/** The family a kind's pages are drawn in. */\nexport function aiLayoutFamily(kind: string): Family {\n return FAMILIES[kind] ?? 'standard'\n}\n\n/**\n * A site's choices, the same for every page of it, each drawn at even odds\n * among the options its kind's signature lists (AGL-3899, AGL-3985), so two\n * sites of one kind are drawn apart and two kinds never read as one page.\n * Favoring each list's first option made the newest production sites one\n * design: the kind's first hero won 55% before the pick, a business site\n * opened split, groups were ruled two times in three and every text section\n * took a photo.\n */\nexport function aiLayoutDesignChoices(design: AiLayoutDesign): AiLayoutDesignChoices {\n const next = random(design.seed ^ 0x2f6b9d1)\n const family = aiLayoutFamily(design.kind)\n const signature = aiLayoutKindSignature(design.kind)\n const hero = pick(next, signature.heroes)\n const group = pick(next, signature.groups)\n return {\n hero,\n // A store's reasons to buy — the making, the shipping, the care — read\n // as open columns each led by an icon, the way a storefront theme draws\n // them: never a SaaS page's feature cards, and never the plain columns\n // of a title and a sentence the beta.237 Willow Wick home showed four\n // bands of (AGL-3676).\n group: family === 'retail' ? 'icons' : group,\n steps: pick(next, signature.steps),\n // Whether a section of words takes a photo beside it: some sites are\n // photo-led, some let their type carry the page. A storefront never\n // shows a band of text alone (AGL-3676), so its story always takes one.\n features: next() < signature.features || family === 'retail',\n featureLeft: next() < 0.5,\n coverClose: next() < signature.coverClose,\n splitHeads: next() < signature.splitHeads,\n pictures: pick(next, signature.pictures),\n // Drawn last, so every choice above stays what it was for a seed.\n writing: pick(next, ['grid', 'ruled'] as const),\n }\n}\n\nconst WORK = /\\b(work|works|project|projects|portfolio|gallery|galleries|case|cases|studies|selected|commission|commissions|illustration|illustrations|photo|photos|series|book|books|editorial|brand|collection|collections|wedding|weddings|portrait|portraits|session|sessions|shoot|shoots)\\b/i\nconst WRITING = /\\b(article|articles|post|posts|writing|writings|latest|featured|journey|journeys|recipe|recipes|story|stories|essay|essays|guide|guides|issue|issues|episode|episodes)\\b/i\nconst DISHES = /\\b(menu|menus|dish|dishes|breakfast|brunch|lunch|dinner|pastry|pastries|coffee|drink|drinks|cocktail|cocktails|wine|wines|highlight|highlights|favorite|favorites|favourites|special|specials|plate|plates|bake|bakes|bread|cake|cakes)\\b/i\n// A store's own products are listed by its Product grid (AGL-3676), never drawn\n// from words: what a store shows as pictures is how its range is grouped — its\n// collections, gift sets, occasions and scents. \"Candle care and burn notes\" is no band of pictures.\n/** Words a section uses for itself when it is a showing of pictures, on any kind of site (AGL-3923). */\nconst GALLERY = /\\b(gallery|galleries|portfolio|portfolios|lookbook|lookbooks)\\b/i\nconst CATEGORIES = /\\b(collection|collections|range|ranges|categor(?:y|ies)|gift|gifts|gift sets?|sets|occasion|occasions|scent|scents|edit|edits)\\b/i\n\n/**\n * How a section's group of cards is drawn, from what the section is about:\n * work in a gallery kind and products in a store are pictures, writing in a\n * blog is articles, dishes in a restaurant a menu; anything else takes the\n * site's own group style.\n */\nexport function aiLayoutGroupVariant(\n design: AiLayoutDesign,\n choices: AiLayoutDesignChoices,\n words: string,\n): AiLayoutGroupVariant {\n const family = aiLayoutFamily(design.kind)\n if (family === 'gallery' && (WORK.test(words) || design.kind === 'photography')) return 'pictures'\n if (family === 'editorial' && WRITING.test(words)) return 'articles'\n if (family === 'hospitality' && DISHES.test(words)) return 'menu'\n if (family === 'retail' && CATEGORIES.test(words)) return 'pictures'\n // A section that calls itself a gallery or a portfolio shows pictures on any\n // kind of site (AGL-3923): the beta.265 Kasvora Marketing Solutions home drew\n // its \"Selected work gallery\" as four cards of icons.\n if (GALLERY.test(words)) return 'pictures'\n return choices.group\n}\n"],"names":["AI_LAYOUT_HERO_VARIANTS","AI_LAYOUT_HALF_HEROES","Set","AI_LAYOUT_OVER_PHOTO_HEROES","FAMILIES","portfolio","photography","studio","blog","restaurant","events","yoga","wellness","beauty","store","BOTH_STEPS","MOSTLY_MOSAIC","AI_LAYOUT_KIND_SIGNATURES","business","heroes","groups","steps","features","coverClose","splitHeads","pictures","trades","professional","fitness","realestate","education","nonprofit","music","personal","landing","aiLayoutKindSignature","kind","random","seed","state","t","Math","imul","pick","next","list","floor","length","aiLayoutFamily","aiLayoutDesignChoices","design","family","signature","hero","group","featureLeft","writing","WORK","WRITING","DISHES","GALLERY","CATEGORIES","aiLayoutGroupVariant","choices","words","test"],"mappings":"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"}
@@ -16,6 +16,7 @@
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  import { AI_ICON_LIBRARY } from "../runtime/ai-icon-library.js";
18
18
  import { AI_LAYOUT_MAX_IMAGES, bandSx, compileFlow, compileRow, destinationProps } from "./ai-layout-compiler.js";
19
+ import { aiLayoutContactItems, aiLayoutContactShown } from "./ai-layout-contact.js";
19
20
  import { aiLayoutFitText } from "./ai-layout-copy.js";
20
21
  import { aiLayoutRepairFrameHollows } from "./ai-layout-frame-hollows.js";
21
22
  import { AI_LAYOUT_CART_ELEMENT } from "./ai-layout-listings.js";
@@ -609,6 +610,28 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
609
610
  weights.push(1);
610
611
  }
611
612
  }
613
+ // How to reach the business (AGL-3979): the phone and email it gave, as
614
+ // links that call and write, in a column of their own under its heading,
615
+ // wherever the design's footer did not already show them.
616
+ const contact = page.targets.contact;
617
+ const contactItems = contact ? aiLayoutContactItems(contact, aiLayoutContactShown(blocks, contact)) : [];
618
+ if (contactItems.length) {
619
+ columns.push([
620
+ {
621
+ kind: 'heading',
622
+ text: aiLayoutLabel('contact', plan.language)
623
+ },
624
+ {
625
+ kind: 'list',
626
+ items: contactItems
627
+ }
628
+ ]);
629
+ weights.push(1);
630
+ page.settled.push({
631
+ at: 'footer',
632
+ what: 'the phone and email the business gave, in a column of their own'
633
+ });
634
+ }
612
635
  const filled = columns.map((column, position)=>{
613
636
  var _weights_position;
614
637
  return {