@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232

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 (305) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +8 -4
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  5. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  7. package/src/lib/components/ai-assist-provider.component.js +9 -10
  8. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  9. package/src/lib/components/ai-job-follow.component.js +1 -0
  10. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  11. package/src/lib/components/ai-job-links.js +13 -0
  12. package/src/lib/components/ai-job-links.js.map +1 -1
  13. package/src/lib/components/ai-job-plan.component.js +3 -1
  14. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  15. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  16. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  17. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  18. package/src/lib/components/ai-media-create.component.js +107 -55
  19. package/src/lib/components/ai-media-create.component.js.map +1 -1
  20. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  21. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  22. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  23. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  24. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  25. package/src/lib/components/ai-site-build-page.component.js +20 -8
  26. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  27. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  28. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  29. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  30. package/src/lib/components/ai-site-start-card.component.js +80 -77
  31. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  32. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  33. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  34. package/src/lib/components/ai-usage-strip.component.js +1 -0
  35. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  36. package/src/lib/components/assist-panel.component.d.ts +10 -0
  37. package/src/lib/components/assist-panel.component.js +119 -9
  38. package/src/lib/components/assist-panel.component.js.map +1 -1
  39. package/src/lib/components/assist-signals-page.component.js +2 -0
  40. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  41. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  42. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  43. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  44. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  45. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  46. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  47. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  48. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  49. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  50. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  51. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  52. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  53. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  54. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  55. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  56. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  57. package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
  58. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  59. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  60. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  61. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  64. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  65. package/src/lib/jobs/ai-job-build-step.js +14 -1
  66. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  68. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  69. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  70. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  71. package/src/lib/jobs/ai-job-generation.js +13 -2
  72. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  73. package/src/lib/jobs/ai-job-insight-step.js +29 -5
  74. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  75. package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
  76. package/src/lib/jobs/ai-job-layout-language.js +200 -0
  77. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  78. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  79. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  80. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  81. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  82. package/src/lib/jobs/ai-job-layout-step.js +107 -6
  83. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  84. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  85. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  86. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  87. package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
  88. package/src/lib/jobs/ai-job-page-language.js +418 -0
  89. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  90. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  91. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  92. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  93. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  94. package/src/lib/jobs/ai-job-page-step.js +115 -14
  95. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
  97. package/src/lib/jobs/ai-job-plan-step.js +62 -22
  98. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  99. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  100. package/src/lib/jobs/ai-job-site-content.js +443 -0
  101. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  102. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  103. package/src/lib/jobs/ai-job-site-look.js +322 -0
  104. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  105. package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
  106. package/src/lib/jobs/ai-job-site-step.js +183 -27
  107. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  108. package/src/lib/jobs/ai-job-workflow-step.js +2 -2
  109. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  110. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  111. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  112. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  113. package/src/lib/jobs/ai-jobs.d.ts +1 -1
  114. package/src/lib/jobs/ai-jobs.js +17 -8
  115. package/src/lib/jobs/ai-jobs.js.map +1 -1
  116. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  117. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  118. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  119. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  120. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  121. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  122. package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
  123. package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
  124. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  125. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  126. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  127. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  128. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  129. package/src/lib/layout-language/ai-layout-frame.js +458 -0
  130. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  131. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  132. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  133. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  134. package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
  135. package/src/lib/layout-language/ai-layout-language.js +529 -0
  136. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  137. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  138. package/src/lib/layout-language/ai-layout-links.js +109 -0
  139. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  140. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  141. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  142. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  143. package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
  144. package/src/lib/layout-language/ai-layout-store.js +75 -0
  145. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  146. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  147. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  148. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  149. package/src/lib/lazy-widget.d.ts +45 -0
  150. package/src/lib/lazy-widget.js +53 -0
  151. package/src/lib/lazy-widget.js.map +1 -0
  152. package/src/lib/model/ai-build-job.js +2 -1
  153. package/src/lib/model/ai-build-job.js.map +1 -1
  154. package/src/lib/model/ai-build-progress.d.ts +20 -3
  155. package/src/lib/model/ai-build-progress.js +52 -7
  156. package/src/lib/model/ai-build-progress.js.map +1 -1
  157. package/src/lib/model/ai-edit-job.d.ts +109 -0
  158. package/src/lib/model/ai-edit-job.js +157 -0
  159. package/src/lib/model/ai-edit-job.js.map +1 -0
  160. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  161. package/src/lib/model/ai-host-publish-context.js +104 -0
  162. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  163. package/src/lib/model/ai-job-activity.d.ts +22 -0
  164. package/src/lib/model/ai-job-activity.js +23 -1
  165. package/src/lib/model/ai-job-activity.js.map +1 -1
  166. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  167. package/src/lib/model/ai-job-failure-copy.js +56 -1
  168. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  169. package/src/lib/model/ai-job-notice.d.ts +3 -1
  170. package/src/lib/model/ai-job-notice.js +31 -9
  171. package/src/lib/model/ai-job-notice.js.map +1 -1
  172. package/src/lib/model/ai-jobs.types.d.ts +22 -1
  173. package/src/lib/model/ai-jobs.types.js.map +1 -1
  174. package/src/lib/model/ai-logic-job.d.ts +18 -1
  175. package/src/lib/model/ai-logic-job.js +24 -3
  176. package/src/lib/model/ai-logic-job.js.map +1 -1
  177. package/src/lib/model/ai-media-credits.d.ts +8 -4
  178. package/src/lib/model/ai-media-credits.js +11 -4
  179. package/src/lib/model/ai-media-credits.js.map +1 -1
  180. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  181. package/src/lib/model/ai-media-kinds.js +98 -0
  182. package/src/lib/model/ai-media-kinds.js.map +1 -0
  183. package/src/lib/model/ai-site-build-progress.d.ts +9 -1
  184. package/src/lib/model/ai-site-build-progress.js +54 -14
  185. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  186. package/src/lib/model/ai-site-context.d.ts +111 -0
  187. package/src/lib/model/ai-site-context.js +222 -0
  188. package/src/lib/model/ai-site-context.js.map +1 -0
  189. package/src/lib/model/ai-site-job.d.ts +18 -2
  190. package/src/lib/model/ai-site-job.js +21 -5
  191. package/src/lib/model/ai-site-job.js.map +1 -1
  192. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  193. package/src/lib/model/ai-site-kinds.js +1942 -0
  194. package/src/lib/model/ai-site-kinds.js.map +1 -0
  195. package/src/lib/model/ai-site-look.d.ts +150 -0
  196. package/src/lib/model/ai-site-look.js +1236 -0
  197. package/src/lib/model/ai-site-look.js.map +1 -0
  198. package/src/lib/model/ai-site-memory.d.ts +80 -0
  199. package/src/lib/model/ai-site-memory.js +171 -0
  200. package/src/lib/model/ai-site-memory.js.map +1 -0
  201. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  202. package/src/lib/model/ai-site-start.d.ts +15 -3
  203. package/src/lib/model/ai-site-start.js +21 -4
  204. package/src/lib/model/ai-site-start.js.map +1 -1
  205. package/src/lib/model/assist-follow-up.d.ts +92 -0
  206. package/src/lib/model/assist-follow-up.js +128 -0
  207. package/src/lib/model/assist-follow-up.js.map +1 -0
  208. package/src/lib/plugin-config.js +2 -0
  209. package/src/lib/plugin-config.js.map +1 -1
  210. package/src/lib/plugin.js +99 -48
  211. package/src/lib/plugin.js.map +1 -1
  212. package/src/lib/providers/anthropic.d.ts +9 -1
  213. package/src/lib/providers/anthropic.js +143 -9
  214. package/src/lib/providers/anthropic.js.map +1 -1
  215. package/src/lib/providers/catalog.d.ts +38 -8
  216. package/src/lib/providers/catalog.js +106 -14
  217. package/src/lib/providers/catalog.js.map +1 -1
  218. package/src/lib/providers/contract.d.ts +29 -0
  219. package/src/lib/providers/contract.js.map +1 -1
  220. package/src/lib/providers/image-contract.d.ts +22 -1
  221. package/src/lib/providers/image-contract.js +40 -1
  222. package/src/lib/providers/image-contract.js.map +1 -1
  223. package/src/lib/providers/image-endpoints.d.ts +24 -0
  224. package/src/lib/providers/image-endpoints.js +28 -0
  225. package/src/lib/providers/image-endpoints.js.map +1 -0
  226. package/src/lib/providers/model-choice.js +23 -2
  227. package/src/lib/providers/model-choice.js.map +1 -1
  228. package/src/lib/providers/routing.js +18 -0
  229. package/src/lib/providers/routing.js.map +1 -1
  230. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  231. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  232. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  233. package/src/lib/providers/vertex-image.d.ts +13 -27
  234. package/src/lib/providers/vertex-image.js +15 -42
  235. package/src/lib/providers/vertex-image.js.map +1 -1
  236. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  237. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  238. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  239. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  240. package/src/lib/runtime/ai-dev-env.js +72 -0
  241. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  242. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  243. package/src/lib/runtime/ai-dev-replay.js +313 -0
  244. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  245. package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
  246. package/src/lib/runtime/ai-doctrine-validators.js +436 -4
  247. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  248. package/src/lib/runtime/ai-doctrine.js +16 -3
  249. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  250. package/src/lib/runtime/ai-eval-live.js +4 -0
  251. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  252. package/src/lib/runtime/ai-eval.d.ts +13 -1
  253. package/src/lib/runtime/ai-eval.js +84 -1
  254. package/src/lib/runtime/ai-eval.js.map +1 -1
  255. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  256. package/src/lib/runtime/ai-icon-library.js +254 -0
  257. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  258. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  259. package/src/lib/runtime/ai-live-batch.js +89 -0
  260. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  261. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  262. package/src/lib/runtime/ai-node-tree.js +30 -4
  263. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  264. package/src/lib/runtime/ai-palette.d.ts +6 -0
  265. package/src/lib/runtime/ai-palette.generated.js +70 -0
  266. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  267. package/src/lib/runtime/ai-palette.js.map +1 -1
  268. package/src/lib/runtime/ai-runtime.js +46 -2
  269. package/src/lib/runtime/ai-runtime.js.map +1 -1
  270. package/src/lib/runtime/site-context.d.ts +58 -0
  271. package/src/lib/runtime/site-context.js +127 -0
  272. package/src/lib/runtime/site-context.js.map +1 -0
  273. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  274. package/src/lib/server/ai-admin-conversations.js +197 -0
  275. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  276. package/src/lib/server/ai-media-image.d.ts +5 -5
  277. package/src/lib/server/ai-media-image.js +25 -11
  278. package/src/lib/server/ai-media-image.js.map +1 -1
  279. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  280. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  281. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  282. package/src/lib/server/assist-chat.d.ts +11 -0
  283. package/src/lib/server/assist-chat.js +57 -18
  284. package/src/lib/server/assist-chat.js.map +1 -1
  285. package/src/lib/server/assist-edit-applied.js +16 -3
  286. package/src/lib/server/assist-edit-applied.js.map +1 -1
  287. package/src/lib/server/assist-view-context.d.ts +19 -1
  288. package/src/lib/server/assist-view-context.js +38 -1
  289. package/src/lib/server/assist-view-context.js.map +1 -1
  290. package/src/lib/server.js +10 -0
  291. package/src/lib/server.js.map +1 -1
  292. package/src/lib/subprocessors.js +17 -1
  293. package/src/lib/subprocessors.js.map +1 -1
  294. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  295. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  296. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  297. package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
  298. package/src/lib/tools/ai-workflow-tool.js +16 -14
  299. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  300. package/src/lib/usage/assist-usage.d.ts +3 -0
  301. package/src/lib/usage/assist-usage.js +4 -2
  302. package/src/lib/usage/assist-usage.js.map +1 -1
  303. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  304. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  305. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -0,0 +1,80 @@
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
+ /**
18
+ * Where a link the layout language names goes (AGL-3660), resolved by code to
19
+ * a destination rule 10 admits: a page of this site by its id, a section of
20
+ * this page by the platform's Scroll to element interaction, or an `https:`
21
+ * address the brief itself gave. A destination that resolves to nothing, or
22
+ * to the home page under words that do not say home, is no destination, and
23
+ * the compiler leaves the button out rather than shipping a dead control.
24
+ */
25
+ /** A page of the site a link may go to: built, or minted on a guided start's plan. */
26
+ export interface AiLayoutPage {
27
+ id: string;
28
+ label: string;
29
+ slug: string;
30
+ }
31
+ /** Everything a page's links resolve against. */
32
+ export interface AiLayoutTargets {
33
+ /** The page being built, which no link of its own goes to. */
34
+ pageId?: string | null;
35
+ /** The site's pages a link may go to. */
36
+ pages: readonly AiLayoutPage[];
37
+ /** The site's home pages, which only words that say home link (rule 10). */
38
+ homeIds: readonly string[];
39
+ /** The saved forms a page may place. */
40
+ forms: readonly {
41
+ id: string;
42
+ name: string;
43
+ }[];
44
+ /** The page that places the site's form, where that is another page. */
45
+ formPageId?: string | null;
46
+ /** The reusable components a page may place, with the props each fills. */
47
+ components: readonly {
48
+ id: string;
49
+ name: string;
50
+ props: Record<string, string>;
51
+ }[];
52
+ /** Every word the job was given: the brief, the site's answers, its profile. */
53
+ facts: string;
54
+ }
55
+ /** Where a link resolved to. */
56
+ export type AiLayoutDestination = {
57
+ kind: 'page';
58
+ screenId: string;
59
+ } | {
60
+ kind: 'section';
61
+ index: number;
62
+ } | {
63
+ kind: 'href';
64
+ href: string;
65
+ };
66
+ export interface AiLayoutLinkScope {
67
+ /** The page's section names in plan order. */
68
+ sections: readonly string[];
69
+ /** The section the link sits in, which it does not scroll to. */
70
+ from: number;
71
+ /** The section of this page that places the site's form, if one does. */
72
+ formSection: number | null;
73
+ }
74
+ /**
75
+ * Where a link goes: the destination its `to` names, read the language's
76
+ * way; failing that, for words that ask a visitor to get in touch, the site's
77
+ * form; failing that, a page whose name the words say. `null` is a link with
78
+ * nowhere to go, which is left out.
79
+ */
80
+ export declare function aiLayoutResolveLink(to: string | undefined, label: string, scope: AiLayoutLinkScope, targets: AiLayoutTargets): AiLayoutDestination | null;
@@ -0,0 +1,109 @@
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
+ */ import { aiPageLinkSection } from "../runtime/ai-page-links.js";
17
+ /** The words of a label or a name, as they are compared. */ function wordsOf(text) {
18
+ return text.toLowerCase().split(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "u")).filter(Boolean);
19
+ }
20
+ function slugOf(slug) {
21
+ return `/${slug.replace(/^\/+|\/+$/g, '')}`.toLowerCase();
22
+ }
23
+ const HOME_WORDS = /\bhome(?:\s?page)?\b/i;
24
+ /** Words that ask a visitor to get in touch, which the site's form answers. */ const CONTACT_WORDS = /\b(?:contact|book|booking|call|quote|estimate|appointment|schedule|reserve|reservation|get in touch|request|visit|enquire|inquire|enquiry|inquiry|message|talk|consult|consultation|order|sign up|join|apply|start)\b/i;
25
+ /** The page a word names: an id, a label or an address. */ function pageNamed(word, targets) {
26
+ var _targets_pages_find, _targets_pages_find1;
27
+ const value = word.trim();
28
+ if (!value) return null;
29
+ const byId = targets.pages.find((page)=>page.id === value);
30
+ if (byId) return byId;
31
+ const lower = value.toLowerCase();
32
+ if (lower.startsWith('/')) return (_targets_pages_find = targets.pages.find((page)=>slugOf(page.slug) === slugOf(lower))) != null ? _targets_pages_find : null;
33
+ const words = wordsOf(value).join(' ');
34
+ return (_targets_pages_find1 = targets.pages.find((page)=>wordsOf(page.label).join(' ') === words)) != null ? _targets_pages_find1 : null;
35
+ }
36
+ /** A destination rule 10 admits, from the page it names, for the words a link shows. */ function pageDestination(page, label, targets) {
37
+ if (!page || page.id === targets.pageId) return null;
38
+ if (targets.homeIds.includes(page.id) && !HOME_WORDS.test(label) && !isHomeSlug(page.slug)) return null;
39
+ if (isHomeSlug(page.slug) && !HOME_WORDS.test(label)) return null;
40
+ return {
41
+ kind: 'page',
42
+ screenId: page.id
43
+ };
44
+ }
45
+ function isHomeSlug(slug) {
46
+ return slugOf(slug) === '/';
47
+ }
48
+ /** Where the site's form is: a section of this page, else the page that places it. */ function formDestination(scope, targets) {
49
+ if (scope.formSection !== null && scope.formSection !== scope.from) return {
50
+ kind: 'section',
51
+ index: scope.formSection
52
+ };
53
+ if (targets.formPageId && targets.formPageId !== targets.pageId) {
54
+ return {
55
+ kind: 'page',
56
+ screenId: targets.formPageId
57
+ };
58
+ }
59
+ return null;
60
+ }
61
+ /**
62
+ * Where a link goes: the destination its `to` names, read the language's
63
+ * way; failing that, for words that ask a visitor to get in touch, the site's
64
+ * form; failing that, a page whose name the words say. `null` is a link with
65
+ * nowhere to go, which is left out.
66
+ */ export function aiLayoutResolveLink(to, label, scope, targets) {
67
+ const named = to ? resolveNamed(to.trim(), label, scope, targets) : null;
68
+ if (named) return named;
69
+ if (CONTACT_WORDS.test(label)) {
70
+ const form = formDestination(scope, targets);
71
+ if (form) return form;
72
+ const contact = targets.pages.find((page)=>page.id !== targets.pageId && !isHomeSlug(page.slug) && CONTACT_WORDS.test(page.label));
73
+ if (contact) return {
74
+ kind: 'page',
75
+ screenId: contact.id
76
+ };
77
+ }
78
+ const labelWords = new Set(wordsOf(label));
79
+ const said = targets.pages.filter((page)=>page.id !== targets.pageId && !isHomeSlug(page.slug) && wordsOf(page.label).length > 0 && wordsOf(page.label).every((word)=>labelWords.has(word)));
80
+ return said.length === 1 ? {
81
+ kind: 'page',
82
+ screenId: said[0].id
83
+ } : null;
84
+ }
85
+ function resolveNamed(to, label, scope, targets) {
86
+ const lower = to.toLowerCase();
87
+ if (lower === 'form' || lower.startsWith('form:') || targets.forms.some((form)=>form.id === to)) {
88
+ return formDestination(scope, targets);
89
+ }
90
+ if (lower.startsWith('https://')) {
91
+ return targets.facts.includes(to) ? {
92
+ kind: 'href',
93
+ href: to
94
+ } : null;
95
+ }
96
+ const section = lower.startsWith('#') ? to.slice(1) : lower.startsWith('section:') ? to.slice(8) : null;
97
+ if (section !== null) {
98
+ const number = Number(section.trim());
99
+ const index = Number.isInteger(number) && number >= 1 && number <= scope.sections.length ? number - 1 : aiPageLinkSection(section, scope.sections);
100
+ return index !== null && index !== scope.from ? {
101
+ kind: 'section',
102
+ index
103
+ } : null;
104
+ }
105
+ const page = pageNamed(lower.startsWith('page:') ? to.slice(5) : to, targets);
106
+ return pageDestination(page, label, targets);
107
+ }
108
+
109
+ //# sourceMappingURL=ai-layout-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-links.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 { aiPageLinkSection } from '../runtime/ai-page-links'\n\n/**\n * Where a link the layout language names goes (AGL-3660), resolved by code to\n * a destination rule 10 admits: a page of this site by its id, a section of\n * this page by the platform's Scroll to element interaction, or an `https:`\n * address the brief itself gave. A destination that resolves to nothing, or\n * to the home page under words that do not say home, is no destination, and\n * the compiler leaves the button out rather than shipping a dead control.\n */\n\n/** A page of the site a link may go to: built, or minted on a guided start's plan. */\nexport interface AiLayoutPage {\n id: string\n label: string\n slug: string\n}\n\n/** Everything a page's links resolve against. */\nexport interface AiLayoutTargets {\n /** The page being built, which no link of its own goes to. */\n pageId?: string | null\n /** The site's pages a link may go to. */\n pages: readonly AiLayoutPage[]\n /** The site's home pages, which only words that say home link (rule 10). */\n homeIds: readonly string[]\n /** The saved forms a page may place. */\n forms: readonly { id: string; name: string }[]\n /** The page that places the site's form, where that is another page. */\n formPageId?: string | null\n /** The reusable components a page may place, with the props each fills. */\n components: readonly {\n id: string\n name: string\n props: Record<string, string>\n }[]\n /** Every word the job was given: the brief, the site's answers, its profile. */\n facts: string\n}\n\n/** Where a link resolved to. */\nexport type AiLayoutDestination =\n | { kind: 'page'; screenId: string }\n | { kind: 'section'; index: number }\n | { kind: 'href'; href: string }\n\n/** The words of a label or a name, as they are compared. */\nfunction wordsOf(text: string): string[] {\n return text\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean)\n}\n\nfunction slugOf(slug: string): string {\n return `/${slug.replace(/^\\/+|\\/+$/g, '')}`.toLowerCase()\n}\n\nconst HOME_WORDS = /\\bhome(?:\\s?page)?\\b/i\n\n/** Words that ask a visitor to get in touch, which the site's form answers. */\nconst CONTACT_WORDS =\n /\\b(?:contact|book|booking|call|quote|estimate|appointment|schedule|reserve|reservation|get in touch|request|visit|enquire|inquire|enquiry|inquiry|message|talk|consult|consultation|order|sign up|join|apply|start)\\b/i\n\nexport interface AiLayoutLinkScope {\n /** The page's section names in plan order. */\n sections: readonly string[]\n /** The section the link sits in, which it does not scroll to. */\n from: number\n /** The section of this page that places the site's form, if one does. */\n formSection: number | null\n}\n\n/** The page a word names: an id, a label or an address. */\nfunction pageNamed(\n word: string,\n targets: AiLayoutTargets,\n): AiLayoutPage | null {\n const value = word.trim()\n if (!value) return null\n const byId = targets.pages.find((page) => page.id === value)\n if (byId) return byId\n const lower = value.toLowerCase()\n if (lower.startsWith('/'))\n return (\n targets.pages.find((page) => slugOf(page.slug) === slugOf(lower)) ?? null\n )\n const words = wordsOf(value).join(' ')\n return (\n targets.pages.find((page) => wordsOf(page.label).join(' ') === words) ??\n null\n )\n}\n\n/** A destination rule 10 admits, from the page it names, for the words a link shows. */\nfunction pageDestination(\n page: AiLayoutPage | null,\n label: string,\n targets: AiLayoutTargets,\n): AiLayoutDestination | null {\n if (!page || page.id === targets.pageId) return null\n if (\n targets.homeIds.includes(page.id) &&\n !HOME_WORDS.test(label) &&\n !isHomeSlug(page.slug)\n )\n return null\n if (isHomeSlug(page.slug) && !HOME_WORDS.test(label)) return null\n return { kind: 'page', screenId: page.id }\n}\n\nfunction isHomeSlug(slug: string): boolean {\n return slugOf(slug) === '/'\n}\n\n/** Where the site's form is: a section of this page, else the page that places it. */\nfunction formDestination(\n scope: AiLayoutLinkScope,\n targets: AiLayoutTargets,\n): AiLayoutDestination | null {\n if (scope.formSection !== null && scope.formSection !== scope.from)\n return { kind: 'section', index: scope.formSection }\n if (targets.formPageId && targets.formPageId !== targets.pageId) {\n return { kind: 'page', screenId: targets.formPageId }\n }\n return null\n}\n\n/**\n * Where a link goes: the destination its `to` names, read the language's\n * way; failing that, for words that ask a visitor to get in touch, the site's\n * form; failing that, a page whose name the words say. `null` is a link with\n * nowhere to go, which is left out.\n */\nexport function aiLayoutResolveLink(\n to: string | undefined,\n label: string,\n scope: AiLayoutLinkScope,\n targets: AiLayoutTargets,\n): AiLayoutDestination | null {\n const named = to ? resolveNamed(to.trim(), label, scope, targets) : null\n if (named) return named\n if (CONTACT_WORDS.test(label)) {\n const form = formDestination(scope, targets)\n if (form) return form\n const contact = targets.pages.find(\n (page) =>\n page.id !== targets.pageId &&\n !isHomeSlug(page.slug) &&\n CONTACT_WORDS.test(page.label),\n )\n if (contact) return { kind: 'page', screenId: contact.id }\n }\n const labelWords = new Set(wordsOf(label))\n const said = targets.pages.filter(\n (page) =>\n page.id !== targets.pageId &&\n !isHomeSlug(page.slug) &&\n wordsOf(page.label).length > 0 &&\n wordsOf(page.label).every((word) => labelWords.has(word)),\n )\n return said.length === 1 ? { kind: 'page', screenId: said[0].id } : null\n}\n\nfunction resolveNamed(\n to: string,\n label: string,\n scope: AiLayoutLinkScope,\n targets: AiLayoutTargets,\n): AiLayoutDestination | null {\n const lower = to.toLowerCase()\n if (\n lower === 'form' ||\n lower.startsWith('form:') ||\n targets.forms.some((form) => form.id === to)\n ) {\n return formDestination(scope, targets)\n }\n if (lower.startsWith('https://')) {\n return targets.facts.includes(to) ? { kind: 'href', href: to } : null\n }\n const section = lower.startsWith('#')\n ? to.slice(1)\n : lower.startsWith('section:')\n ? to.slice(8)\n : null\n if (section !== null) {\n const number = Number(section.trim())\n const index =\n Number.isInteger(number) && number >= 1 && number <= scope.sections.length\n ? number - 1\n : aiPageLinkSection(section, scope.sections)\n return index !== null && index !== scope.from\n ? { kind: 'section', index }\n : null\n }\n const page = pageNamed(lower.startsWith('page:') ? to.slice(5) : to, targets)\n return pageDestination(page, label, targets)\n}\n"],"names":["aiPageLinkSection","wordsOf","text","toLowerCase","split","filter","Boolean","slugOf","slug","replace","HOME_WORDS","CONTACT_WORDS","pageNamed","word","targets","value","trim","byId","pages","find","page","id","lower","startsWith","words","join","label","pageDestination","pageId","homeIds","includes","test","isHomeSlug","kind","screenId","formDestination","scope","formSection","from","index","formPageId","aiLayoutResolveLink","to","named","resolveNamed","form","contact","labelWords","Set","said","length","every","has","forms","some","facts","href","section","slice","number","Number","isInteger","sections"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,QAAQ,8BAA0B;AA8C5D,0DAA0D,GAC1D,SAASC,QAAQC,IAAY;IAC3B,OAAOA,KACJC,WAAW,GACXC,KAAK,CAAC,8maACNC,MAAM,CAACC;AACZ;AAEA,SAASC,OAAOC,IAAY;IAC1B,OAAO,CAAC,CAAC,EAAEA,KAAKC,OAAO,CAAC,cAAc,KAAK,CAACN,WAAW;AACzD;AAEA,MAAMO,aAAa;AAEnB,6EAA6E,GAC7E,MAAMC,gBACJ;AAWF,yDAAyD,GACzD,SAASC,UACPC,IAAY,EACZC,OAAwB;QASpBA,qBAIFA;IAXF,MAAMC,QAAQF,KAAKG,IAAI;IACvB,IAAI,CAACD,OAAO,OAAO;IACnB,MAAME,OAAOH,QAAQI,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKC,EAAE,KAAKN;IACtD,IAAIE,MAAM,OAAOA;IACjB,MAAMK,QAAQP,MAAMZ,WAAW;IAC/B,IAAImB,MAAMC,UAAU,CAAC,MACnB,QACET,sBAAAA,QAAQI,KAAK,CAACC,IAAI,CAAC,CAACC,OAASb,OAAOa,KAAKZ,IAAI,MAAMD,OAAOe,mBAA1DR,sBAAqE;IAEzE,MAAMU,QAAQvB,QAAQc,OAAOU,IAAI,CAAC;IAClC,QACEX,uBAAAA,QAAQI,KAAK,CAACC,IAAI,CAAC,CAACC,OAASnB,QAAQmB,KAAKM,KAAK,EAAED,IAAI,CAAC,SAASD,kBAA/DV,uBACA;AAEJ;AAEA,sFAAsF,GACtF,SAASa,gBACPP,IAAyB,EACzBM,KAAa,EACbZ,OAAwB;IAExB,IAAI,CAACM,QAAQA,KAAKC,EAAE,KAAKP,QAAQc,MAAM,EAAE,OAAO;IAChD,IACEd,QAAQe,OAAO,CAACC,QAAQ,CAACV,KAAKC,EAAE,KAChC,CAACX,WAAWqB,IAAI,CAACL,UACjB,CAACM,WAAWZ,KAAKZ,IAAI,GAErB,OAAO;IACT,IAAIwB,WAAWZ,KAAKZ,IAAI,KAAK,CAACE,WAAWqB,IAAI,CAACL,QAAQ,OAAO;IAC7D,OAAO;QAAEO,MAAM;QAAQC,UAAUd,KAAKC,EAAE;IAAC;AAC3C;AAEA,SAASW,WAAWxB,IAAY;IAC9B,OAAOD,OAAOC,UAAU;AAC1B;AAEA,oFAAoF,GACpF,SAAS2B,gBACPC,KAAwB,EACxBtB,OAAwB;IAExB,IAAIsB,MAAMC,WAAW,KAAK,QAAQD,MAAMC,WAAW,KAAKD,MAAME,IAAI,EAChE,OAAO;QAAEL,MAAM;QAAWM,OAAOH,MAAMC,WAAW;IAAC;IACrD,IAAIvB,QAAQ0B,UAAU,IAAI1B,QAAQ0B,UAAU,KAAK1B,QAAQc,MAAM,EAAE;QAC/D,OAAO;YAAEK,MAAM;YAAQC,UAAUpB,QAAQ0B,UAAU;QAAC;IACtD;IACA,OAAO;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASC,oBACdC,EAAsB,EACtBhB,KAAa,EACbU,KAAwB,EACxBtB,OAAwB;IAExB,MAAM6B,QAAQD,KAAKE,aAAaF,GAAG1B,IAAI,IAAIU,OAAOU,OAAOtB,WAAW;IACpE,IAAI6B,OAAO,OAAOA;IAClB,IAAIhC,cAAcoB,IAAI,CAACL,QAAQ;QAC7B,MAAMmB,OAAOV,gBAAgBC,OAAOtB;QACpC,IAAI+B,MAAM,OAAOA;QACjB,MAAMC,UAAUhC,QAAQI,KAAK,CAACC,IAAI,CAChC,CAACC,OACCA,KAAKC,EAAE,KAAKP,QAAQc,MAAM,IAC1B,CAACI,WAAWZ,KAAKZ,IAAI,KACrBG,cAAcoB,IAAI,CAACX,KAAKM,KAAK;QAEjC,IAAIoB,SAAS,OAAO;YAAEb,MAAM;YAAQC,UAAUY,QAAQzB,EAAE;QAAC;IAC3D;IACA,MAAM0B,aAAa,IAAIC,IAAI/C,QAAQyB;IACnC,MAAMuB,OAAOnC,QAAQI,KAAK,CAACb,MAAM,CAC/B,CAACe,OACCA,KAAKC,EAAE,KAAKP,QAAQc,MAAM,IAC1B,CAACI,WAAWZ,KAAKZ,IAAI,KACrBP,QAAQmB,KAAKM,KAAK,EAAEwB,MAAM,GAAG,KAC7BjD,QAAQmB,KAAKM,KAAK,EAAEyB,KAAK,CAAC,CAACtC,OAASkC,WAAWK,GAAG,CAACvC;IAEvD,OAAOoC,KAAKC,MAAM,KAAK,IAAI;QAAEjB,MAAM;QAAQC,UAAUe,IAAI,CAAC,EAAE,CAAC5B,EAAE;IAAC,IAAI;AACtE;AAEA,SAASuB,aACPF,EAAU,EACVhB,KAAa,EACbU,KAAwB,EACxBtB,OAAwB;IAExB,MAAMQ,QAAQoB,GAAGvC,WAAW;IAC5B,IACEmB,UAAU,UACVA,MAAMC,UAAU,CAAC,YACjBT,QAAQuC,KAAK,CAACC,IAAI,CAAC,CAACT,OAASA,KAAKxB,EAAE,KAAKqB,KACzC;QACA,OAAOP,gBAAgBC,OAAOtB;IAChC;IACA,IAAIQ,MAAMC,UAAU,CAAC,aAAa;QAChC,OAAOT,QAAQyC,KAAK,CAACzB,QAAQ,CAACY,MAAM;YAAET,MAAM;YAAQuB,MAAMd;QAAG,IAAI;IACnE;IACA,MAAMe,UAAUnC,MAAMC,UAAU,CAAC,OAC7BmB,GAAGgB,KAAK,CAAC,KACTpC,MAAMC,UAAU,CAAC,cACfmB,GAAGgB,KAAK,CAAC,KACT;IACN,IAAID,YAAY,MAAM;QACpB,MAAME,SAASC,OAAOH,QAAQzC,IAAI;QAClC,MAAMuB,QACJqB,OAAOC,SAAS,CAACF,WAAWA,UAAU,KAAKA,UAAUvB,MAAM0B,QAAQ,CAACZ,MAAM,GACtES,SAAS,IACT3D,kBAAkByD,SAASrB,MAAM0B,QAAQ;QAC/C,OAAOvB,UAAU,QAAQA,UAAUH,MAAME,IAAI,GACzC;YAAEL,MAAM;YAAWM;QAAM,IACzB;IACN;IACA,MAAMnB,OAAOR,UAAUU,MAAMC,UAAU,CAAC,WAAWmB,GAAGgB,KAAK,CAAC,KAAKhB,IAAI5B;IACrE,OAAOa,gBAAgBP,MAAMM,OAAOZ;AACtC"}
@@ -0,0 +1,144 @@
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 { NodesMap } from '@aglyn/aglyn/types/nodes';
18
+ /**
19
+ * THE PICTURES OF A LANGUAGE PAGE (AGL-3660).
20
+ *
21
+ * The compiler writes a picture slot as a frame holding an icon and an
22
+ * `image` with alt text and no source (`ai-layout-compiler.ts`), and stays
23
+ * pure: it never looks a photo up. This module is what runs after it, on the
24
+ * stored page — it finds each empty slot, decides what kind of picture the
25
+ * slot is, and fills it with a photo the site can serve from its own origin.
26
+ *
27
+ * Two sources fill a slot, in order:
28
+ *
29
+ * 1. A stock photo searched for by the slot's own description and copied into
30
+ * the site's media library (`jobs/ai-layout-stock-photos.ts`), when the
31
+ * deployment has a stock-photo key.
32
+ * 2. Otherwise, or when the search finds nothing fitting, one of the STARTER
33
+ * PHOTOS every new site is born with (`DEFAULT_SITE_IMAGES`, served at
34
+ * `/_static/starter/` by the tenant and the console alike, credited in the
35
+ * CREDITS file beside them). They are the platform's own files, so a
36
+ * visitor's browser asks no other host for them.
37
+ *
38
+ * The slot keeps the alt text the model wrote whichever source fills it.
39
+ */
40
+ /** What a picture slot is for, read from where it sits and what it says. */
41
+ export type AiLayoutPictureRole = 'hero' | 'about' | 'gallery';
42
+ /** One empty picture slot of a stored page, in document order. */
43
+ export interface AiLayoutPictureSlot {
44
+ /** The `image` node. */
45
+ imageId: string;
46
+ /** The frame around it, which sets the slot's shape. */
47
+ frameId: string | null;
48
+ /** The placeholder icon sharing the frame, taken out once a photo fills it. */
49
+ iconId: string | null;
50
+ alt: string;
51
+ /** The frame's shape as width over height; 1 when it names none. */
52
+ aspect: number;
53
+ /** The plan index of the section it sits in, or -1. */
54
+ sectionIndex: number;
55
+ role: AiLayoutPictureRole;
56
+ }
57
+ /** A photo a slot is filled with. */
58
+ export interface AiLayoutPicturePhoto {
59
+ /** The site-served address: a starter path or the media library's own. */
60
+ src: string;
61
+ width: number;
62
+ height: number;
63
+ }
64
+ /** A slot's role: the first section's picture is the hero; people make an about picture. */
65
+ export declare function aiLayoutPictureRole(input: {
66
+ sectionIndex: number;
67
+ sectionName: string;
68
+ alt: string;
69
+ }): AiLayoutPictureRole;
70
+ /**
71
+ * Every empty picture slot of a stored page, walked from its root in
72
+ * document order. A slot that already has a source — one the owner placed, or
73
+ * an earlier pass filled — is not a slot.
74
+ */
75
+ export declare function aiLayoutPictureSlots(nodes: NodesMap, rootId: string, sections: {
76
+ ids: readonly string[];
77
+ names: readonly string[];
78
+ }): AiLayoutPictureSlot[];
79
+ /** The starter photos, by name. */
80
+ export declare const AI_LAYOUT_STARTER_PHOTOS: {
81
+ readonly hero: {
82
+ readonly src: "/_static/starter/hero-team.jpg";
83
+ readonly width: 1600;
84
+ readonly height: 1067;
85
+ };
86
+ readonly about: {
87
+ readonly src: "/_static/starter/about-owner.jpg";
88
+ readonly width: 1600;
89
+ readonly height: 900;
90
+ };
91
+ readonly desk: {
92
+ readonly src: "/_static/starter/gallery-desk.jpg";
93
+ readonly width: 1600;
94
+ readonly height: 1068;
95
+ };
96
+ readonly plants: {
97
+ readonly src: "/_static/starter/gallery-plants.jpg";
98
+ readonly width: 1200;
99
+ readonly height: 900;
100
+ };
101
+ readonly craft: {
102
+ readonly src: "/_static/starter/gallery-craft.jpg";
103
+ readonly width: 1600;
104
+ readonly height: 1065;
105
+ };
106
+ };
107
+ /** A small stable number from a seed, so the same job always picks the same photos. */
108
+ export declare function aiLayoutSeedNumber(seed: string): number;
109
+ /**
110
+ * A starter photo for each slot. The hero's and the gallery's pools are
111
+ * rotated by the seed, so sites do not all open with the same photo, while an
112
+ * about picture always leads with the owner; no photo repeats on a page
113
+ * while one is still unused; and a page with more slots than photos starts
114
+ * the round again from its role's pool.
115
+ */
116
+ export declare function aiLayoutStarterPhotos(slots: readonly AiLayoutPictureSlot[], seed: string): AiLayoutPicturePhoto[];
117
+ /**
118
+ * The page with each slot filled by its photo: the `image` given its source,
119
+ * its intrinsic size and how it loads (the hero eagerly, as the starter home
120
+ * page loads its own), and the placeholder icon taken out of the frame. The
121
+ * alt text is kept as the model wrote it. A slot given no photo is left as
122
+ * the compiler wrote it.
123
+ */
124
+ export declare function aiLayoutPlacePictures(nodes: NodesMap, slots: readonly AiLayoutPictureSlot[], photos: ReadonlyArray<AiLayoutPicturePhoto | null>): NodesMap;
125
+ /**
126
+ * A source of photos found for the slots themselves — a stock search copied
127
+ * into the site's library — answering one photo or `null` per slot, in order.
128
+ * It may throw; a slot it leaves unfilled takes a starter photo.
129
+ */
130
+ export type AiLayoutPictureSource = (slots: readonly AiLayoutPictureSlot[]) => Promise<ReadonlyArray<AiLayoutPicturePhoto | null>>;
131
+ /**
132
+ * Fills a stored page's empty picture slots (AGL-3660): from `source` where
133
+ * one is given and it answers, else from the starter photos. Never throws,
134
+ * and never leaves a page worse than it came: a picture is decoration, and a
135
+ * job never fails over one.
136
+ */
137
+ export declare function aiResolveLayoutPictures(nodes: NodesMap, input: {
138
+ rootId: string;
139
+ sectionIds: readonly string[];
140
+ sectionNames: readonly string[];
141
+ /** A per-job word, so two sites do not open with the same photo. */
142
+ seed: string;
143
+ source?: AiLayoutPictureSource | null;
144
+ }): Promise<NodesMap>;
@@ -0,0 +1,214 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { DEFAULT_SITE_IMAGES } from "@aglyn/aglyn/app-utils/default-site";
18
+ /** Words that say a picture shows people, or a section is about them. */ const PEOPLE = /\b(about|team|story|owner|owners|founder|founders|staff|crew|people|person|portrait|meet|family|families|who we are|our (?:team|crew|people|story)|therapist|counsel(?:l)?or|coach|doctor|dentist|stylist|trainer|smil(?:e|es|ing)|headshot)\b/i;
19
+ /** A frame's `aspectRatio` (`'16 / 9'`) as a number. */ function aspectOf(value) {
20
+ const match = /^\s*(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)\s*$/.exec(String(value != null ? value : ''));
21
+ if (!match) return 1;
22
+ const ratio = Number(match[1]) / Number(match[2]);
23
+ return Number.isFinite(ratio) && ratio > 0 ? ratio : 1;
24
+ }
25
+ /** A slot's role: the first section's picture is the hero; people make an about picture. */ export function aiLayoutPictureRole(input) {
26
+ if (input.sectionIndex === 0) return 'hero';
27
+ if (PEOPLE.test(input.sectionName) || PEOPLE.test(input.alt)) return 'about';
28
+ return 'gallery';
29
+ }
30
+ /**
31
+ * Every empty picture slot of a stored page, walked from its root in
32
+ * document order. A slot that already has a source — one the owner placed, or
33
+ * an earlier pass filled — is not a slot.
34
+ */ export function aiLayoutPictureSlots(nodes, rootId, sections) {
35
+ const map = nodes;
36
+ const slots = [];
37
+ const seen = new Set();
38
+ const visit = (id, parentId, sectionIndex)=>{
39
+ var _node_nodes;
40
+ if (seen.has(id)) return;
41
+ seen.add(id);
42
+ const node = map[id];
43
+ if (!node) return;
44
+ const index = sections.ids.indexOf(id);
45
+ const here = index >= 0 ? index : sectionIndex;
46
+ if (node.componentId === 'image') {
47
+ var _node_props, _node_props1;
48
+ const src = (_node_props = node.props) == null ? void 0 : _node_props['src'];
49
+ const alt = typeof ((_node_props1 = node.props) == null ? void 0 : _node_props1['alt']) === 'string' ? node.props['alt'].trim() : '';
50
+ if ((typeof src !== 'string' || !src.trim()) && alt) {
51
+ var _ref, _sections_names_here;
52
+ var _frame_nodes, _frame_sx;
53
+ const frame = parentId ? map[parentId] : undefined;
54
+ const iconId = (_ref = frame == null ? void 0 : (_frame_nodes = frame.nodes) == null ? void 0 : _frame_nodes.find((child)=>{
55
+ var _map_child;
56
+ return child !== id && ((_map_child = map[child]) == null ? void 0 : _map_child.componentId) === 'icon';
57
+ })) != null ? _ref : null;
58
+ slots.push({
59
+ imageId: id,
60
+ frameId: frame ? parentId : null,
61
+ iconId,
62
+ alt,
63
+ aspect: aspectOf(frame == null ? void 0 : (_frame_sx = frame.sx) == null ? void 0 : _frame_sx['aspectRatio']),
64
+ sectionIndex: here,
65
+ role: aiLayoutPictureRole({
66
+ sectionIndex: here,
67
+ sectionName: here >= 0 ? (_sections_names_here = sections.names[here]) != null ? _sections_names_here : '' : '',
68
+ alt
69
+ })
70
+ });
71
+ }
72
+ }
73
+ for (const child of (_node_nodes = node.nodes) != null ? _node_nodes : [])visit(child, id, here);
74
+ };
75
+ visit(rootId, null, -1);
76
+ return slots;
77
+ }
78
+ /** The starter photos, by name. */ export const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES;
79
+ /**
80
+ * The starter photos each role draws from, best first. A hero draws from the
81
+ * wide photos that read as a page's opening, an about picture leads with the
82
+ * owner, and the rest are the gallery's.
83
+ */ const STARTER_POOLS = {
84
+ hero: [
85
+ 'hero',
86
+ 'desk',
87
+ 'craft'
88
+ ],
89
+ about: [
90
+ 'about',
91
+ 'hero'
92
+ ],
93
+ gallery: [
94
+ 'desk',
95
+ 'plants',
96
+ 'craft'
97
+ ]
98
+ };
99
+ const ALL_STARTERS = Object.keys(AI_LAYOUT_STARTER_PHOTOS);
100
+ /** A small stable number from a seed, so the same job always picks the same photos. */ export function aiLayoutSeedNumber(seed) {
101
+ let hash = 2166136261;
102
+ for(let index = 0; index < seed.length; index += 1){
103
+ hash ^= seed.charCodeAt(index);
104
+ hash = Math.imul(hash, 16777619);
105
+ }
106
+ return hash >>> 0;
107
+ }
108
+ function rotate(list, by) {
109
+ if (!list.length) return [];
110
+ const start = by % list.length;
111
+ return [
112
+ ...list.slice(start),
113
+ ...list.slice(0, start)
114
+ ];
115
+ }
116
+ /**
117
+ * A starter photo for each slot. The hero's and the gallery's pools are
118
+ * rotated by the seed, so sites do not all open with the same photo, while an
119
+ * about picture always leads with the owner; no photo repeats on a page
120
+ * while one is still unused; and a page with more slots than photos starts
121
+ * the round again from its role's pool.
122
+ */ export function aiLayoutStarterPhotos(slots, seed) {
123
+ const turn = aiLayoutSeedNumber(seed);
124
+ const used = new Set();
125
+ return slots.map((slot)=>{
126
+ var _find;
127
+ // An about picture leads with the owner on every site; the others turn.
128
+ const pool = slot.role === 'about' ? [
129
+ ...STARTER_POOLS.about
130
+ ] : rotate(STARTER_POOLS[slot.role], turn);
131
+ const rest = rotate(ALL_STARTERS.filter((name)=>!pool.includes(name)), turn);
132
+ if (used.size >= ALL_STARTERS.length) used.clear();
133
+ const name = (_find = [
134
+ ...pool,
135
+ ...rest
136
+ ].find((candidate)=>!used.has(candidate))) != null ? _find : pool[0];
137
+ used.add(name);
138
+ const photo = AI_LAYOUT_STARTER_PHOTOS[name];
139
+ return {
140
+ src: photo.src,
141
+ width: photo.width,
142
+ height: photo.height
143
+ };
144
+ });
145
+ }
146
+ /**
147
+ * The page with each slot filled by its photo: the `image` given its source,
148
+ * its intrinsic size and how it loads (the hero eagerly, as the starter home
149
+ * page loads its own), and the placeholder icon taken out of the frame. The
150
+ * alt text is kept as the model wrote it. A slot given no photo is left as
151
+ * the compiler wrote it.
152
+ */ export function aiLayoutPlacePictures(nodes, slots, photos) {
153
+ const next = _extends({}, nodes);
154
+ slots.forEach((slot, index)=>{
155
+ var _node_props;
156
+ const photo = photos[index];
157
+ const node = next[slot.imageId];
158
+ if (!photo || !node) return;
159
+ next[slot.imageId] = _extends({}, node, {
160
+ props: _extends({}, (_node_props = node['props']) != null ? _node_props : {}, {
161
+ src: photo.src,
162
+ intrinsicWidth: photo.width,
163
+ intrinsicHeight: photo.height,
164
+ loading: slot.role === 'hero' ? 'eager' : 'lazy'
165
+ })
166
+ });
167
+ if (slot.iconId && slot.frameId && next[slot.frameId]) {
168
+ var _frame_nodes;
169
+ const frame = next[slot.frameId];
170
+ next[slot.frameId] = _extends({}, frame, {
171
+ nodes: ((_frame_nodes = frame['nodes']) != null ? _frame_nodes : []).filter((child)=>child !== slot.iconId)
172
+ });
173
+ delete next[slot.iconId];
174
+ }
175
+ });
176
+ return next;
177
+ }
178
+ /**
179
+ * Fills a stored page's empty picture slots (AGL-3660): from `source` where
180
+ * one is given and it answers, else from the starter photos. Never throws,
181
+ * and never leaves a page worse than it came: a picture is decoration, and a
182
+ * job never fails over one.
183
+ */ export async function aiResolveLayoutPictures(nodes, input) {
184
+ try {
185
+ const slots = aiLayoutPictureSlots(nodes, input.rootId, {
186
+ ids: input.sectionIds,
187
+ names: input.sectionNames
188
+ });
189
+ if (!slots.length) return nodes;
190
+ let found = [];
191
+ if (input.source) {
192
+ try {
193
+ found = await input.source(slots);
194
+ } catch (error) {
195
+ console.warn('ai layout pictures: the stock source failed; starter photos fill the page', {
196
+ error
197
+ });
198
+ found = [];
199
+ }
200
+ }
201
+ const starters = aiLayoutStarterPhotos(slots, input.seed);
202
+ return aiLayoutPlacePictures(nodes, slots, slots.map((_slot, index)=>{
203
+ var _found_index;
204
+ return (_found_index = found[index]) != null ? _found_index : starters[index];
205
+ }));
206
+ } catch (error) {
207
+ console.error('ai layout pictures: the page keeps its empty picture slots', {
208
+ error
209
+ });
210
+ return nodes;
211
+ }
212
+ }
213
+
214
+ //# sourceMappingURL=ai-layout-pictures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-pictures.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 { DEFAULT_SITE_IMAGES } from '@aglyn/aglyn/app-utils/default-site'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\n\n/**\n * THE PICTURES OF A LANGUAGE PAGE (AGL-3660).\n *\n * The compiler writes a picture slot as a frame holding an icon and an\n * `image` with alt text and no source (`ai-layout-compiler.ts`), and stays\n * pure: it never looks a photo up. This module is what runs after it, on the\n * stored page — it finds each empty slot, decides what kind of picture the\n * slot is, and fills it with a photo the site can serve from its own origin.\n *\n * Two sources fill a slot, in order:\n *\n * 1. A stock photo searched for by the slot's own description and copied into\n * the site's media library (`jobs/ai-layout-stock-photos.ts`), when the\n * deployment has a stock-photo key.\n * 2. Otherwise, or when the search finds nothing fitting, one of the STARTER\n * PHOTOS every new site is born with (`DEFAULT_SITE_IMAGES`, served at\n * `/_static/starter/` by the tenant and the console alike, credited in the\n * CREDITS file beside them). They are the platform's own files, so a\n * visitor's browser asks no other host for them.\n *\n * The slot keeps the alt text the model wrote whichever source fills it.\n */\n\n/** What a picture slot is for, read from where it sits and what it says. */\nexport type AiLayoutPictureRole = 'hero' | 'about' | 'gallery'\n\n/** One empty picture slot of a stored page, in document order. */\nexport interface AiLayoutPictureSlot {\n /** The `image` node. */\n imageId: string\n /** The frame around it, which sets the slot's shape. */\n frameId: string | null\n /** The placeholder icon sharing the frame, taken out once a photo fills it. */\n iconId: string | null\n alt: string\n /** The frame's shape as width over height; 1 when it names none. */\n aspect: number\n /** The plan index of the section it sits in, or -1. */\n sectionIndex: number\n role: AiLayoutPictureRole\n}\n\n/** A photo a slot is filled with. */\nexport interface AiLayoutPicturePhoto {\n /** The site-served address: a starter path or the media library's own. */\n src: string\n width: number\n height: number\n}\n\n/** Words that say a picture shows people, or a section is about them. */\nconst PEOPLE =\n /\\b(about|team|story|owner|owners|founder|founders|staff|crew|people|person|portrait|meet|family|families|who we are|our (?:team|crew|people|story)|therapist|counsel(?:l)?or|coach|doctor|dentist|stylist|trainer|smil(?:e|es|ing)|headshot)\\b/i\n\n/** A frame's `aspectRatio` (`'16 / 9'`) as a number. */\nfunction aspectOf(value: unknown): number {\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*\\/\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(String(value ?? ''))\n if (!match) return 1\n const ratio = Number(match[1]) / Number(match[2])\n return Number.isFinite(ratio) && ratio > 0 ? ratio : 1\n}\n\n/** A slot's role: the first section's picture is the hero; people make an about picture. */\nexport function aiLayoutPictureRole(input: {\n sectionIndex: number\n sectionName: string\n alt: string\n}): AiLayoutPictureRole {\n if (input.sectionIndex === 0) return 'hero'\n if (PEOPLE.test(input.sectionName) || PEOPLE.test(input.alt)) return 'about'\n return 'gallery'\n}\n\n/**\n * Every empty picture slot of a stored page, walked from its root in\n * document order. A slot that already has a source — one the owner placed, or\n * an earlier pass filled — is not a slot.\n */\nexport function aiLayoutPictureSlots(\n nodes: NodesMap,\n rootId: string,\n sections: { ids: readonly string[]; names: readonly string[] },\n): AiLayoutPictureSlot[] {\n const map = nodes as unknown as Record<\n string,\n { componentId?: string; props?: Record<string, unknown>; sx?: Record<string, unknown>; nodes?: string[] }\n >\n const slots: AiLayoutPictureSlot[] = []\n const seen = new Set<string>()\n const visit = (id: string, parentId: string | null, sectionIndex: number) => {\n if (seen.has(id)) return\n seen.add(id)\n const node = map[id]\n if (!node) return\n const index = sections.ids.indexOf(id)\n const here = index >= 0 ? index : sectionIndex\n if (node.componentId === 'image') {\n const src = node.props?.['src']\n const alt = typeof node.props?.['alt'] === 'string' ? (node.props['alt'] as string).trim() : ''\n if ((typeof src !== 'string' || !src.trim()) && alt) {\n const frame = parentId ? map[parentId] : undefined\n const iconId =\n frame?.nodes?.find((child) => child !== id && map[child]?.componentId === 'icon') ?? null\n slots.push({\n imageId: id,\n frameId: frame ? parentId : null,\n iconId,\n alt,\n aspect: aspectOf(frame?.sx?.['aspectRatio']),\n sectionIndex: here,\n role: aiLayoutPictureRole({\n sectionIndex: here,\n sectionName: here >= 0 ? (sections.names[here] ?? '') : '',\n alt,\n }),\n })\n }\n }\n for (const child of node.nodes ?? []) visit(child, id, here)\n }\n visit(rootId, null, -1)\n return slots\n}\n\n/** The starter photos, by name. */\nexport const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES\n\ntype StarterName = keyof typeof AI_LAYOUT_STARTER_PHOTOS\n\n/**\n * The starter photos each role draws from, best first. A hero draws from the\n * wide photos that read as a page's opening, an about picture leads with the\n * owner, and the rest are the gallery's.\n */\nconst STARTER_POOLS: Readonly<Record<AiLayoutPictureRole, readonly StarterName[]>> = {\n hero: ['hero', 'desk', 'craft'],\n about: ['about', 'hero'],\n gallery: ['desk', 'plants', 'craft'],\n}\n\nconst ALL_STARTERS = Object.keys(AI_LAYOUT_STARTER_PHOTOS) as StarterName[]\n\n/** A small stable number from a seed, so the same job always picks the same photos. */\nexport function aiLayoutSeedNumber(seed: string): number {\n let hash = 2166136261\n for (let index = 0; index < seed.length; index += 1) {\n hash ^= seed.charCodeAt(index)\n hash = Math.imul(hash, 16777619)\n }\n return hash >>> 0\n}\n\nfunction rotate<T>(list: readonly T[], by: number): T[] {\n if (!list.length) return []\n const start = by % list.length\n return [...list.slice(start), ...list.slice(0, start)]\n}\n\n/**\n * A starter photo for each slot. The hero's and the gallery's pools are\n * rotated by the seed, so sites do not all open with the same photo, while an\n * about picture always leads with the owner; no photo repeats on a page\n * while one is still unused; and a page with more slots than photos starts\n * the round again from its role's pool.\n */\nexport function aiLayoutStarterPhotos(\n slots: readonly AiLayoutPictureSlot[],\n seed: string,\n): AiLayoutPicturePhoto[] {\n const turn = aiLayoutSeedNumber(seed)\n const used = new Set<StarterName>()\n return slots.map((slot) => {\n // An about picture leads with the owner on every site; the others turn.\n const pool =\n slot.role === 'about' ? [...STARTER_POOLS.about] : rotate(STARTER_POOLS[slot.role], turn)\n const rest = rotate(\n ALL_STARTERS.filter((name) => !pool.includes(name)),\n turn,\n )\n if (used.size >= ALL_STARTERS.length) used.clear()\n const name = [...pool, ...rest].find((candidate) => !used.has(candidate)) ?? pool[0]\n used.add(name)\n const photo = AI_LAYOUT_STARTER_PHOTOS[name]\n return { src: photo.src, width: photo.width, height: photo.height }\n })\n}\n\n/**\n * The page with each slot filled by its photo: the `image` given its source,\n * its intrinsic size and how it loads (the hero eagerly, as the starter home\n * page loads its own), and the placeholder icon taken out of the frame. The\n * alt text is kept as the model wrote it. A slot given no photo is left as\n * the compiler wrote it.\n */\nexport function aiLayoutPlacePictures(\n nodes: NodesMap,\n slots: readonly AiLayoutPictureSlot[],\n photos: ReadonlyArray<AiLayoutPicturePhoto | null>,\n): NodesMap {\n const next = { ...(nodes as unknown as Record<string, Record<string, unknown>>) }\n slots.forEach((slot, index) => {\n const photo = photos[index]\n const node = next[slot.imageId]\n if (!photo || !node) return\n next[slot.imageId] = {\n ...node,\n props: {\n ...((node['props'] as Record<string, unknown> | undefined) ?? {}),\n src: photo.src,\n intrinsicWidth: photo.width,\n intrinsicHeight: photo.height,\n loading: slot.role === 'hero' ? 'eager' : 'lazy',\n },\n }\n if (slot.iconId && slot.frameId && next[slot.frameId]) {\n const frame = next[slot.frameId]\n next[slot.frameId] = {\n ...frame,\n nodes: ((frame['nodes'] as string[] | undefined) ?? []).filter((child) => child !== slot.iconId),\n }\n delete next[slot.iconId]\n }\n })\n return next as unknown as NodesMap\n}\n\n/**\n * A source of photos found for the slots themselves — a stock search copied\n * into the site's library — answering one photo or `null` per slot, in order.\n * It may throw; a slot it leaves unfilled takes a starter photo.\n */\nexport type AiLayoutPictureSource = (\n slots: readonly AiLayoutPictureSlot[],\n) => Promise<ReadonlyArray<AiLayoutPicturePhoto | null>>\n\n/**\n * Fills a stored page's empty picture slots (AGL-3660): from `source` where\n * one is given and it answers, else from the starter photos. Never throws,\n * and never leaves a page worse than it came: a picture is decoration, and a\n * job never fails over one.\n */\nexport async function aiResolveLayoutPictures(\n nodes: NodesMap,\n input: {\n rootId: string\n sectionIds: readonly string[]\n sectionNames: readonly string[]\n /** A per-job word, so two sites do not open with the same photo. */\n seed: string\n source?: AiLayoutPictureSource | null\n },\n): Promise<NodesMap> {\n try {\n const slots = aiLayoutPictureSlots(nodes, input.rootId, {\n ids: input.sectionIds,\n names: input.sectionNames,\n })\n if (!slots.length) return nodes\n let found: ReadonlyArray<AiLayoutPicturePhoto | null> = []\n if (input.source) {\n try {\n found = await input.source(slots)\n } catch (error) {\n console.warn('ai layout pictures: the stock source failed; starter photos fill the page', { error })\n found = []\n }\n }\n const starters = aiLayoutStarterPhotos(slots, input.seed)\n return aiLayoutPlacePictures(\n nodes,\n slots,\n slots.map((_slot, index) => found[index] ?? starters[index]),\n )\n } catch (error) {\n console.error('ai layout pictures: the page keeps its empty picture slots', { error })\n return nodes\n }\n}\n"],"names":["DEFAULT_SITE_IMAGES","PEOPLE","aspectOf","value","match","exec","String","ratio","Number","isFinite","aiLayoutPictureRole","input","sectionIndex","test","sectionName","alt","aiLayoutPictureSlots","nodes","rootId","sections","map","slots","seen","Set","visit","id","parentId","node","has","add","index","ids","indexOf","here","componentId","src","props","trim","frame","undefined","iconId","find","child","push","imageId","frameId","aspect","sx","role","names","AI_LAYOUT_STARTER_PHOTOS","STARTER_POOLS","hero","about","gallery","ALL_STARTERS","Object","keys","aiLayoutSeedNumber","seed","hash","length","charCodeAt","Math","imul","rotate","list","by","start","slice","aiLayoutStarterPhotos","turn","used","slot","pool","rest","filter","name","includes","size","clear","candidate","photo","width","height","aiLayoutPlacePictures","photos","next","forEach","intrinsicWidth","intrinsicHeight","loading","aiResolveLayoutPictures","sectionIds","sectionNames","found","source","error","console","warn","starters","_slot"],"mappings":"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"}