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

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 (281) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.d.ts +4 -3
  3. package/src/lib/activity/ai-activity-actions.js +4 -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-build-page.component.js +18 -0
  36. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  37. package/src/lib/components/ai-site-import-job.component.d.ts +41 -0
  38. package/src/lib/components/ai-site-import-job.component.js +707 -0
  39. package/src/lib/components/ai-site-import-job.component.js.map +1 -0
  40. package/src/lib/components/ai-site-import-start.component.d.ts +64 -0
  41. package/src/lib/components/ai-site-import-start.component.js +335 -0
  42. package/src/lib/components/ai-site-import-start.component.js.map +1 -0
  43. package/src/lib/components/ai-site-ownership-proof.component.d.ts +42 -0
  44. package/src/lib/components/ai-site-ownership-proof.component.js +353 -0
  45. package/src/lib/components/ai-site-ownership-proof.component.js.map +1 -0
  46. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  47. package/src/lib/components/ai-site-start-card.component.js +261 -119
  48. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  49. package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
  50. package/src/lib/components/ai-site-start-steps.component.js +651 -15
  51. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  52. package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
  53. package/src/lib/components/ai-site-start-tracking.js +14 -0
  54. package/src/lib/components/ai-site-start-tracking.js.map +1 -1
  55. package/src/lib/components/ai-site-style-previews.js +26 -22
  56. package/src/lib/components/ai-site-style-previews.js.map +1 -1
  57. package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
  58. package/src/lib/components/ai-upsell-dialog.component.js +8 -6
  59. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  60. package/src/lib/components/assist-build-card.component.js +11 -1
  61. package/src/lib/components/assist-build-card.component.js.map +1 -1
  62. package/src/lib/components/assist-jobs-drawer.component.js +9 -1
  63. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  64. package/src/lib/components/assist-panel.component.js +8 -4
  65. package/src/lib/components/assist-panel.component.js.map +1 -1
  66. package/src/lib/components/staff-org-ai-card.component.js +4 -7
  67. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  68. package/src/lib/declarations.d.ts +2 -2
  69. package/src/lib/declarations.js +9 -5
  70. package/src/lib/declarations.js.map +1 -1
  71. package/src/lib/jobs/ai-build-capabilities.js +3 -1
  72. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  73. package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
  74. package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
  75. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
  76. package/src/lib/jobs/ai-job-build-step.js +5 -3
  77. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  78. package/src/lib/jobs/ai-job-drafts.d.ts +14 -0
  79. package/src/lib/jobs/ai-job-drafts.js +24 -0
  80. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  81. package/src/lib/jobs/ai-job-form-step.d.ts +15 -1
  82. package/src/lib/jobs/ai-job-form-step.js +75 -8
  83. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  84. package/src/lib/jobs/ai-job-import-step.d.ts +136 -0
  85. package/src/lib/jobs/ai-job-import-step.js +809 -0
  86. package/src/lib/jobs/ai-job-import-step.js.map +1 -0
  87. package/src/lib/jobs/ai-job-page-language.d.ts +52 -1
  88. package/src/lib/jobs/ai-job-page-language.js +99 -5
  89. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  90. package/src/lib/jobs/ai-job-page-step.d.ts +19 -2
  91. package/src/lib/jobs/ai-job-page-step.js +42 -18
  92. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  93. package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
  94. package/src/lib/jobs/ai-job-plan-step.js +103 -29
  95. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-settings-step.js +4 -3
  97. package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
  98. package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
  99. package/src/lib/jobs/ai-job-site-datasets.js +140 -14
  100. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  101. package/src/lib/jobs/ai-job-site-look.js +18 -5
  102. package/src/lib/jobs/ai-job-site-look.js.map +1 -1
  103. package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
  104. package/src/lib/jobs/ai-job-site-step.js +95 -22
  105. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  106. package/src/lib/jobs/ai-job-text-step.d.ts +15 -1
  107. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  108. package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
  109. package/src/lib/jobs/ai-job-tracking-step.js +28 -5
  110. package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
  111. package/src/lib/jobs/ai-jobs.d.ts +13 -1
  112. package/src/lib/jobs/ai-jobs.js +77 -10
  113. package/src/lib/jobs/ai-jobs.js.map +1 -1
  114. package/src/lib/jobs/ai-layout-owner-photos.d.ts +16 -1
  115. package/src/lib/jobs/ai-layout-owner-photos.js +47 -0
  116. package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
  117. package/src/lib/jobs/ai-layout-stock-photos.d.ts +27 -2
  118. package/src/lib/jobs/ai-layout-stock-photos.js +190 -19
  119. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  120. package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
  121. package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
  122. package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
  123. package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
  124. package/src/lib/jobs/ai-site-inspiration.js +346 -0
  125. package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
  126. package/src/lib/jobs/ai-site-publish.d.ts +26 -1
  127. package/src/lib/jobs/ai-site-publish.js +162 -13
  128. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  129. package/src/lib/jobs/ai-theme-brand-inputs.d.ts +77 -3
  130. package/src/lib/jobs/ai-theme-brand-inputs.js +135 -31
  131. package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
  132. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
  133. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
  134. package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
  135. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
  136. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
  137. package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
  138. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
  139. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
  140. package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
  141. package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
  142. package/src/lib/layout-language/ai-layout-compiler.js +438 -25
  143. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  144. package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
  145. package/src/lib/layout-language/ai-layout-contact.js +137 -0
  146. package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
  147. package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
  148. package/src/lib/layout-language/ai-layout-copy.js +34 -2
  149. package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
  150. package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
  151. package/src/lib/layout-language/ai-layout-design.js +411 -97
  152. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  153. package/src/lib/layout-language/ai-layout-frame.js +23 -0
  154. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  155. package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
  156. package/src/lib/layout-language/ai-layout-labels.js +12 -6
  157. package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
  158. package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
  159. package/src/lib/layout-language/ai-layout-language.js +4 -3
  160. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  161. package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
  162. package/src/lib/layout-language/ai-layout-links.js +30 -0
  163. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  164. package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
  165. package/src/lib/layout-language/ai-layout-pictures.js +145 -22
  166. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  167. package/src/lib/model/ai-build-job.d.ts +16 -1
  168. package/src/lib/model/ai-build-job.js +40 -0
  169. package/src/lib/model/ai-build-job.js.map +1 -1
  170. package/src/lib/model/ai-credit-estimate.d.ts +20 -0
  171. package/src/lib/model/ai-credit-estimate.js +13 -0
  172. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  173. package/src/lib/model/ai-job-activity.d.ts +2 -2
  174. package/src/lib/model/ai-job-activity.js +6 -4
  175. package/src/lib/model/ai-job-activity.js.map +1 -1
  176. package/src/lib/model/ai-job-input.d.ts +33 -0
  177. package/src/lib/model/ai-job-input.js +59 -0
  178. package/src/lib/model/ai-job-input.js.map +1 -0
  179. package/src/lib/model/ai-job-notice.d.ts +4 -0
  180. package/src/lib/model/ai-job-notice.js +28 -4
  181. package/src/lib/model/ai-job-notice.js.map +1 -1
  182. package/src/lib/model/ai-jobs.types.d.ts +69 -3
  183. package/src/lib/model/ai-jobs.types.js +2 -1
  184. package/src/lib/model/ai-jobs.types.js.map +1 -1
  185. package/src/lib/model/ai-site-details.d.ts +122 -0
  186. package/src/lib/model/ai-site-details.js +300 -0
  187. package/src/lib/model/ai-site-details.js.map +1 -0
  188. package/src/lib/model/ai-site-entity.d.ts +181 -0
  189. package/src/lib/model/ai-site-entity.js +390 -0
  190. package/src/lib/model/ai-site-entity.js.map +1 -0
  191. package/src/lib/model/ai-site-import-view.d.ts +130 -0
  192. package/src/lib/model/ai-site-import-view.js +182 -0
  193. package/src/lib/model/ai-site-import-view.js.map +1 -0
  194. package/src/lib/model/ai-site-import.d.ts +302 -0
  195. package/src/lib/model/ai-site-import.js +526 -0
  196. package/src/lib/model/ai-site-import.js.map +1 -0
  197. package/src/lib/model/ai-site-inspiration.d.ts +72 -0
  198. package/src/lib/model/ai-site-inspiration.js +109 -0
  199. package/src/lib/model/ai-site-inspiration.js.map +1 -0
  200. package/src/lib/model/ai-site-job.d.ts +13 -1
  201. package/src/lib/model/ai-site-job.js +15 -2
  202. package/src/lib/model/ai-site-job.js.map +1 -1
  203. package/src/lib/model/ai-site-kinds.js +109 -21
  204. package/src/lib/model/ai-site-kinds.js.map +1 -1
  205. package/src/lib/model/ai-site-look.d.ts +10 -0
  206. package/src/lib/model/ai-site-look.js +24 -0
  207. package/src/lib/model/ai-site-look.js.map +1 -1
  208. package/src/lib/model/ai-site-start-seo.d.ts +19 -5
  209. package/src/lib/model/ai-site-start-seo.js +25 -7
  210. package/src/lib/model/ai-site-start-seo.js.map +1 -1
  211. package/src/lib/model/ai-site-start-steps.d.ts +9 -3
  212. package/src/lib/model/ai-site-start-steps.js +67 -5
  213. package/src/lib/model/ai-site-start-steps.js.map +1 -1
  214. package/src/lib/model/ai-site-start.d.ts +81 -4
  215. package/src/lib/model/ai-site-start.js +207 -13
  216. package/src/lib/model/ai-site-start.js.map +1 -1
  217. package/src/lib/model/ai-upsell-copy.js +4 -1
  218. package/src/lib/model/ai-upsell-copy.js.map +1 -1
  219. package/src/lib/plan-entitlements.d.ts +4 -3
  220. package/src/lib/plan-entitlements.js +3 -8
  221. package/src/lib/plan-entitlements.js.map +1 -1
  222. package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
  223. package/src/lib/runtime/ai-dataset-generation.js +12 -2
  224. package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
  225. package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
  226. package/src/lib/runtime/ai-doctrine-validators.js +54 -44
  227. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  228. package/src/lib/runtime/ai-doctrine.js +1 -1
  229. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  230. package/src/lib/runtime/ai-node-tree.d.ts +7 -0
  231. package/src/lib/runtime/ai-node-tree.js +9 -1
  232. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  233. package/src/lib/server/ai-create-route.d.ts +1 -1
  234. package/src/lib/server/ai-create-route.js +3 -4
  235. package/src/lib/server/ai-create-route.js.map +1 -1
  236. package/src/lib/server/ai-jobs-resume.js +27 -3
  237. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  238. package/src/lib/server/ai-jobs-route.js +18 -0
  239. package/src/lib/server/ai-jobs-route.js.map +1 -1
  240. package/src/lib/server/ai-site-import-route.d.ts +89 -0
  241. package/src/lib/server/ai-site-import-route.js +356 -0
  242. package/src/lib/server/ai-site-import-route.js.map +1 -0
  243. package/src/lib/server.js +14 -0
  244. package/src/lib/server.js.map +1 -1
  245. package/src/lib/site-import/fixtures/site-import-crawl-recordings.d.ts +45 -0
  246. package/src/lib/site-import/fixtures/site-import-crawl-recordings.js +313 -0
  247. package/src/lib/site-import/fixtures/site-import-crawl-recordings.js.map +1 -0
  248. package/src/lib/site-import/fixtures/site-import-wave-recordings.d.ts +50 -0
  249. package/src/lib/site-import/fixtures/site-import-wave-recordings.js +217 -0
  250. package/src/lib/site-import/fixtures/site-import-wave-recordings.js.map +1 -0
  251. package/src/lib/site-import/site-import-crawl.d.ts +163 -0
  252. package/src/lib/site-import/site-import-crawl.js +467 -0
  253. package/src/lib/site-import/site-import-crawl.js.map +1 -0
  254. package/src/lib/site-import/site-import-flag.d.ts +23 -0
  255. package/src/lib/site-import/site-import-flag.js +23 -0
  256. package/src/lib/site-import/site-import-flag.js.map +1 -0
  257. package/src/lib/site-import/site-import-html.d.ts +94 -0
  258. package/src/lib/site-import/site-import-html.js +462 -0
  259. package/src/lib/site-import/site-import-html.js.map +1 -0
  260. package/src/lib/site-import/site-import-images.d.ts +54 -0
  261. package/src/lib/site-import/site-import-images.js +204 -0
  262. package/src/lib/site-import/site-import-images.js.map +1 -0
  263. package/src/lib/site-import/site-import-page.d.ts +48 -0
  264. package/src/lib/site-import/site-import-page.js +150 -0
  265. package/src/lib/site-import/site-import-page.js.map +1 -0
  266. package/src/lib/site-import/site-import-review.d.ts +40 -0
  267. package/src/lib/site-import/site-import-review.js +149 -0
  268. package/src/lib/site-import/site-import-review.js.map +1 -0
  269. package/src/lib/site-import/site-import-robots.d.ts +52 -0
  270. package/src/lib/site-import/site-import-robots.js +126 -0
  271. package/src/lib/site-import/site-import-robots.js.map +1 -0
  272. package/src/lib/usage/assist-credits.d.ts +4 -4
  273. package/src/lib/usage/assist-credits.js +4 -4
  274. package/src/lib/usage/assist-credits.js.map +1 -1
  275. package/src/lib/usage/staff-org-ai-view.js +6 -3
  276. package/src/lib/usage/staff-org-ai-view.js.map +1 -1
  277. package/src/lib/usage/staff-org-ai.d.ts +4 -2
  278. package/src/lib/usage/staff-org-ai.js +7 -7
  279. package/src/lib/usage/staff-org-ai.js.map +1 -1
  280. package/src/lib/usage/staff-org-usage-ai.js +2 -2
  281. package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
@@ -0,0 +1,585 @@
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 { AI_SITE_CORNERS, AI_SITE_GROUNDS } from "../model/ai-site-kinds.js";
18
+ import { AI_SITE_BUTTONS, AI_SITE_CARDS, AI_SITE_HEADERS, aiHexOklch } from "../model/ai-site-look.js";
19
+ import { aiSiteMotionStyle } from "../layout-language/ai-layout-motion.js";
20
+ import { normalizeThemeHex } from "../tools/ai-theme-tool.js";
21
+ import { AI_THEME_BRAND_COLORS_PER_SOURCE, referencePageColors } from "./ai-theme-brand-inputs.js";
22
+ /**
23
+ * What a site the person likes says about how it is built (AGL-3975), read by
24
+ * code into the site job's own vocabularies. SIGNALS ONLY: every value here
25
+ * is a member of a closed set this module or the look's modules define, or a
26
+ * hex color. Nothing the page SAYS — no sentence, heading, name, picture
27
+ * address or logo — is kept, so nothing of it can reach a prompt or a page.
28
+ * The page's words are read only to tell one kind of section from another,
29
+ * and the answer is the kind, never the words.
30
+ *
31
+ * One page and at most two of its stylesheets (`readReferencePage`), never a
32
+ * crawl. Every reader is best effort: a signal the page does not show is
33
+ * `null`, and the site job chooses that dimension as it always has.
34
+ */ /** The kinds of section a page is read into, in the site plan's own words. */ export const AI_SITE_INSPIRATION_SECTIONS = [
35
+ 'hero',
36
+ 'about',
37
+ 'features',
38
+ 'steps',
39
+ 'stats',
40
+ 'gallery',
41
+ 'menu',
42
+ 'products',
43
+ 'pricing',
44
+ 'testimonials',
45
+ 'logos',
46
+ 'team',
47
+ 'events',
48
+ 'blog',
49
+ 'faq',
50
+ 'newsletter',
51
+ 'booking',
52
+ 'contact',
53
+ 'cta'
54
+ ];
55
+ /** What a site offers its visitors, which a paid start maps onto Aglyn's own parts. */ export const AI_SITE_INSPIRATION_FEATURES = [
56
+ 'booking',
57
+ 'store',
58
+ 'blog',
59
+ 'forms',
60
+ 'gallery',
61
+ 'events',
62
+ 'menu',
63
+ 'testimonials',
64
+ 'newsletter'
65
+ ];
66
+ /** The mood of a site's headings' type. */ export const AI_SITE_INSPIRATION_FONT_MOODS = [
67
+ 'serif',
68
+ 'sans',
69
+ 'display'
70
+ ];
71
+ /** How a site's main buttons are drawn: a fill, or only an outline. */ export const AI_SITE_INSPIRATION_BUTTON_FILLS = [
72
+ 'filled',
73
+ 'outline'
74
+ ];
75
+ const DENSITIES = [
76
+ 'compact',
77
+ 'regular',
78
+ 'airy'
79
+ ];
80
+ const ALIGNS = [
81
+ 'start',
82
+ 'center'
83
+ ];
84
+ /** The most sections of one page that are kept, in order. */ export const AI_SITE_INSPIRATION_MAX_SECTIONS = 10;
85
+ // ── The page, reduced ────────────────────────────────────────────────────
86
+ const COMMENTS = /<!--[\s\S]*?-->/g;
87
+ const INERT_BLOCKS = /<(script|style|noscript|template|svg|iframe)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
88
+ const STYLE_BLOCK = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi;
89
+ const STYLE_ATTRIBUTE = /\bstyle\s*=\s*"([^"]*)"|\bstyle\s*=\s*'([^']*)'/gi;
90
+ const URL_ATTRIBUTE = /\b(?:href|src|action|data-src)\s*=\s*["']([^"']+)["']/gi;
91
+ const CLASS_ATTRIBUTE = /\b(?:class|id|data-section|data-block|aria-labelledby)\s*=\s*["']([^"']*)["']/gi;
92
+ const TAG = /<[^>]+>/g;
93
+ /** A page's visible words, for telling sections apart; never kept. */ function wordsOf(html) {
94
+ return html.replace(INERT_BLOCKS, ' ').replace(TAG, ' ').replace(/&nbsp;|&#160;/g, ' ').replace(/&amp;/g, '&').replace(/&#0?39;|&apos;|&rsquo;/g, "'").replace(/\s+/g, ' ').trim().toLowerCase();
95
+ }
96
+ /** Every class, id and section name in a stretch of markup, lowercased. */ function tokensOf(html) {
97
+ return [
98
+ ...html.matchAll(CLASS_ATTRIBUTE)
99
+ ].map((match)=>{
100
+ var _match_;
101
+ return (_match_ = match[1]) != null ? _match_ : '';
102
+ }).join(' ').toLowerCase();
103
+ }
104
+ /** Every address a stretch of markup links or loads. */ function urlsOf(html) {
105
+ return [
106
+ ...html.matchAll(URL_ATTRIBUTE)
107
+ ].map((match)=>{
108
+ var _match_;
109
+ return ((_match_ = match[1]) != null ? _match_ : '').toLowerCase();
110
+ });
111
+ }
112
+ const count = (html, pattern)=>{
113
+ var _html_match;
114
+ return ((_html_match = html.match(pattern)) != null ? _html_match : []).length;
115
+ };
116
+ const CSS_RULE = /([^{}]+)\{([^{}]*)\}/g;
117
+ function declarationsOf(body) {
118
+ const decls = new Map();
119
+ for (const part of body.split(';')){
120
+ const colon = part.indexOf(':');
121
+ if (colon <= 0) continue;
122
+ const property = part.slice(0, colon).trim().toLowerCase();
123
+ const value = part.slice(colon + 1).replace(/!important/i, '').trim().toLowerCase();
124
+ if (property && value) decls.set(property, value);
125
+ }
126
+ return decls;
127
+ }
128
+ /** Each rule of a stylesheet, its selector and declarations; nested at-rules are read by their inner rules. */ export function cssRulesOf(css) {
129
+ const rules = [];
130
+ for (const match of css.replace(/\/\*[\s\S]*?\*\//g, ' ').matchAll(CSS_RULE)){
131
+ var _match_, _match_1;
132
+ const selector = ((_match_ = match[1]) != null ? _match_ : '').trim().toLowerCase();
133
+ if (!selector || selector.startsWith('@')) continue;
134
+ rules.push({
135
+ selector,
136
+ decls: declarationsOf((_match_1 = match[2]) != null ? _match_1 : '')
137
+ });
138
+ }
139
+ return rules;
140
+ }
141
+ /** A CSS length in px: rem and em at 16px, a percentage as itself. `null` for anything else. */ function px(value) {
142
+ if (!value) return null;
143
+ const match = /(-?\d*\.?\d+)\s*(px|rem|em|%)?/.exec(value);
144
+ if (!match) return null;
145
+ const amount = Number(match[1]);
146
+ if (!Number.isFinite(amount)) return null;
147
+ return match[2] === 'rem' || match[2] === 'em' ? amount * 16 : amount;
148
+ }
149
+ const median = (values)=>{
150
+ if (!values.length) return null;
151
+ const sorted = [
152
+ ...values
153
+ ].sort((a, b)=>a - b);
154
+ return sorted[Math.floor(sorted.length / 2)];
155
+ };
156
+ /** The first color a declaration value names, as hex. */ function colorOf(value) {
157
+ if (!value) return null;
158
+ const hex = /#(?:[0-9a-f]{6}|[0-9a-f]{3})\b/i.exec(value);
159
+ if (hex) return normalizeThemeHex(hex[0]);
160
+ const rgb = /rgba?\(\s*(\d{1,3})\s*[,\s]\s*(\d{1,3})\s*[,\s]\s*(\d{1,3})(?:\s*[,/]\s*([\d.]+%?))?/i.exec(value);
161
+ if (rgb) {
162
+ const alpha = rgb[4] === undefined ? 1 : rgb[4].endsWith('%') ? Number(rgb[4].slice(0, -1)) / 100 : Number(rgb[4]);
163
+ if (alpha < 0.5) return null;
164
+ return `#${[
165
+ rgb[1],
166
+ rgb[2],
167
+ rgb[3]
168
+ ].map((channel)=>Math.min(255, Number(channel)).toString(16).padStart(2, '0')).join('')}`;
169
+ }
170
+ if (/\bwhite\b/.test(value)) return '#ffffff';
171
+ if (/\bblack\b/.test(value)) return '#000000';
172
+ return null;
173
+ }
174
+ const NONE = /^(none|0|0px|transparent|initial|unset)$/;
175
+ /** Whether a declaration draws something: a shadow, a border, a fill. */ const draws = (value)=>Boolean(value && !NONE.test(value.trim()) && !/^0(px)?\s+0(px)?\s+0(px)?\b/.test(value));
176
+ // ── Sections ─────────────────────────────────────────────────────────────
177
+ const BOOKING_HOSTS = /calendly\.com|acuityscheduling\.com|squareup\.com\/appointments|square\.site\/book|opentable\.|resy\.com|exploretock\.com|sevenrooms\.com|booksy\.com|vagaro\.com|fresha\.com|mindbodyonline\.com|setmore\.com|simplybook\.|schedulicity\.com|zocdoc\.com|janeapp\.com/;
178
+ /** The page's body with its header and footer cut away. */ function bodyOf(html) {
179
+ var _ref, _ref1;
180
+ var _exec;
181
+ const open = /<body\b[^>]*>/i.exec(html);
182
+ let body = open ? html.slice(open.index + open[0].length) : html;
183
+ body = body.replace(/<\/body\s*>[\s\S]*$/i, '');
184
+ const headerMatch = /<header\b[\s\S]*?<\/header\s*>/i.exec(body);
185
+ const header = (_ref = headerMatch == null ? void 0 : headerMatch[0]) != null ? _ref : (_ref1 = (_exec = /<nav\b[\s\S]*?<\/nav\s*>/i.exec(body)) == null ? void 0 : _exec[0]) != null ? _ref1 : '';
186
+ if (headerMatch) body = body.slice(0, headerMatch.index) + body.slice(headerMatch.index + headerMatch[0].length);
187
+ const footer = body.search(/<footer\b/i);
188
+ if (footer >= 0) body = body.slice(0, footer);
189
+ return {
190
+ body,
191
+ header
192
+ };
193
+ }
194
+ /** The page's sections: its `<section>`s where it has them, else the stretches its main headings open. */ function segmentsOf(body) {
195
+ const clean = body.replace(COMMENTS, ' ');
196
+ const starts = (pattern)=>[
197
+ ...clean.matchAll(pattern)
198
+ ].map((match)=>{
199
+ var _match_index;
200
+ return (_match_index = match.index) != null ? _match_index : 0;
201
+ });
202
+ let cuts = starts(/<section\b/gi);
203
+ if (cuts.length < 2) cuts = starts(/<h[12]\b/gi);
204
+ if (!cuts.length) return [];
205
+ return cuts.map((at, index)=>{
206
+ var _cuts_, _heading_;
207
+ const html = clean.slice(at, (_cuts_ = cuts[index + 1]) != null ? _cuts_ : clean.length);
208
+ const heading = /<h[1-3]\b[^>]*>([\s\S]*?)<\/h[1-3]\s*>/i.exec(html);
209
+ return {
210
+ index,
211
+ html,
212
+ tokens: tokensOf(html),
213
+ heading: heading ? wordsOf((_heading_ = heading[1]) != null ? _heading_ : '') : '',
214
+ words: wordsOf(html)
215
+ };
216
+ });
217
+ }
218
+ /** The kind of one section, or `null` for a stretch that says too little to tell. */ function sectionKind(segment) {
219
+ const { html, tokens, heading, words, index } = segment;
220
+ const has = (pattern)=>pattern.test(tokens) || pattern.test(heading);
221
+ const images = count(html, /<img\b|<picture\b/gi);
222
+ const emails = count(html, /<input\b[^>]*type\s*=\s*["']?email/gi);
223
+ const areas = count(html, /<textarea\b/gi);
224
+ const prices = count(words, /[$€£]\s?\d/g);
225
+ const wordCount = words ? words.split(' ').length : 0;
226
+ if (index === 0 && /<h1\b/i.test(html) || /\bhero\b|banner|masthead|jumbotron|splash/.test(tokens)) return 'hero';
227
+ if (has(/pricing|\bplans?\b|price-table|packages|\btiers?\b/) || prices >= 2 && /\/\s?(mo|month|yr|year)\b|per month|billed/.test(words)) return 'pricing';
228
+ if (/\bmenu\b|our dishes|food (&|and) drink/.test(heading) || /(food|dinner|lunch|brunch|drinks?|cocktail)[-_]?menu|menu[-_](section|list|card|category)/.test(tokens)) return 'menu';
229
+ if (count(html, /<details\b/gi) >= 2 || has(/\bfaq|accordion|frequently asked|questions/)) return 'faq';
230
+ if (/<blockquote\b/i.test(html) || has(/testimonial|\breviews?\b|kind words|what (our )?(clients|customers|guests|people) say/)) return 'testimonials';
231
+ if (has(/\blogos?\b|clients|\bbrands\b|partners|trusted by|as seen|featured in/)) return 'logos';
232
+ if (BOOKING_HOSTS.test(urlsOf(html).join(' ')) || has(/booking|reserv|appointment|\bbook (a|an|your|now)\b|schedule a/)) return 'booking';
233
+ if (emails === 1 && !areas && has(/newsletter|subscribe|sign-?up|stay in (the )?loop/)) return 'newsletter';
234
+ if (/<form\b/i.test(html) && (emails || areas)) return 'contact';
235
+ if (has(/contact|get in touch|reach us|say hello|visit us|find us/)) return 'contact';
236
+ if (has(/\bevents?\b|calendar|upcoming|\bshows\b|tour dates|what's on/) || count(html, /<time\b/gi) >= 2) return 'events';
237
+ if (has(/\bblog\b|\bposts?\b|articles?|journal|\bnews\b|latest stories/) || count(html, /<article\b/gi) >= 2) return 'blog';
238
+ if (has(/product|\bshop\b|bestsellers|new arrivals/) || /add to (cart|bag)/.test(words)) return 'products';
239
+ if (has(/\bteam\b|\bstaff\b|meet (the|our)|our people|founders/)) return 'team';
240
+ if (has(/\bstats\b|numbers|counter|metrics/) || count(words, /\b\d+(\.\d+)?\s?(k|m|\+|%)(?=\s|$)/g) >= 3) return 'stats';
241
+ if (has(/\bsteps\b|process|how-it-works|how it works|timeline|how we work/)) return 'steps';
242
+ if (has(/gallery|portfolio|selected work|our work|\bworks\b|projects|masonry|lookbook/) || images >= 4 && wordCount < 60) return 'gallery';
243
+ if (has(/\babout\b|our story|\bstory\b|mission|who we are/)) return 'about';
244
+ if (has(/features|services|benefits|what we (do|offer)|why choose|\boffer/) || count(html, /<h3\b/gi) >= 3) return 'features';
245
+ if (has(/\bcta\b|callout|call-to-action/) || wordCount < 40 && /<(a|button)\b/i.test(html)) return 'cta';
246
+ return wordCount >= 20 ? 'about' : null;
247
+ }
248
+ /** The page's sections in order, a run of one kind read as one section. */ export function inspirationSectionsOf(html) {
249
+ const sections = [];
250
+ for (const segment of segmentsOf(bodyOf(html).body)){
251
+ const kind = sectionKind(segment);
252
+ if (kind && sections[sections.length - 1] !== kind) sections.push(kind);
253
+ if (sections.length === AI_SITE_INSPIRATION_MAX_SECTIONS) break;
254
+ }
255
+ return sections;
256
+ }
257
+ // ── Header ───────────────────────────────────────────────────────────────
258
+ const HEADER_SELECTOR = /(^|[\s,>])(header|\.header|\.site-header|\.navbar|\.nav-bar|\.topbar|\.masthead|#header|nav)([\s.:#,>[]|$)/;
259
+ /** The header's arrangement and what sets it off the page. */ function headerSignals(html, rules) {
260
+ const { header } = bodyOf(html);
261
+ const headerRules = rules.filter((rule)=>HEADER_SELECTOR.test(rule.selector));
262
+ let headerAlign = null;
263
+ if (header) {
264
+ const tokens = tokensOf(header);
265
+ const logo = header.search(/<[^>]+\b(class|id)\s*=\s*["'][^"']*\b(logo|brand)[^"']*["']|<img\b/i);
266
+ const before = logo > 0 ? count(header.slice(0, logo), /<a\b/gi) : 0;
267
+ const after = logo >= 0 ? count(header.slice(logo), /<a\b/gi) : 0;
268
+ const centered = /(^|\s)(text-center|justify-center|centered|center|header--center|logo-center|is-centered)(\s|$)/.test(tokens) || headerRules.some((rule)=>rule.decls.get('text-align') === 'center' || rule.decls.get('justify-content') === 'center') || before >= 2 && after >= 3;
269
+ headerAlign = centered ? 'center' : 'start';
270
+ }
271
+ let style = null;
272
+ if (headerRules.length) {
273
+ if (headerRules.some((rule)=>draws(rule.decls.get('box-shadow')))) style = 'shadow';
274
+ else if (headerRules.some((rule)=>draws(rule.decls.get('border-bottom')) || draws(rule.decls.get('border-bottom-width')))) style = 'line';
275
+ else style = 'flat';
276
+ }
277
+ return {
278
+ headerAlign,
279
+ header: style
280
+ };
281
+ }
282
+ // ── Spacing ──────────────────────────────────────────────────────────────
283
+ const SECTION_SELECTOR = /(^|[\s,>.#])(section|hero|band|block|\.container|\.wrapper|main|\.content|\.row)\b/;
284
+ /** How much room the sections take: their vertical padding, from the stylesheet or a utility class. */ function densityOf(html, rules) {
285
+ const paddings = [];
286
+ for (const rule of rules){
287
+ var _ref, _rule_decls_get;
288
+ if (!SECTION_SELECTOR.test(rule.selector)) continue;
289
+ const value = (_ref = (_rule_decls_get = rule.decls.get('padding-top')) != null ? _rule_decls_get : rule.decls.get('padding-block')) != null ? _ref : rule.decls.get('padding');
290
+ const amount = px(value);
291
+ if (amount !== null && amount > 0 && !(value == null ? void 0 : value.includes('%'))) paddings.push(amount);
292
+ }
293
+ for (const match of tokensOf(bodyOf(html).body).matchAll(/(?:^|\s)(?:py|pt)-(\d{1,2})(?=\s|$)/g)){
294
+ paddings.push(Number(match[1]) * 4);
295
+ }
296
+ const middle = median(paddings);
297
+ if (middle === null) return null;
298
+ return middle >= 96 ? 'airy' : middle <= 40 ? 'compact' : 'regular';
299
+ }
300
+ // ── Color and ground ─────────────────────────────────────────────────────
301
+ /** The page ground the body sits on, among the light grounds a site's look offers; `null` for a dark one. */ function groundOf(rules) {
302
+ var _ref;
303
+ const body = rules.find((rule)=>/(^|,)\s*(body|html|:root)\s*(,|$)/.test(rule.selector) && (rule.decls.has('background') || rule.decls.has('background-color')));
304
+ const hex = colorOf((_ref = body == null ? void 0 : body.decls.get('background-color')) != null ? _ref : body == null ? void 0 : body.decls.get('background'));
305
+ if (!hex) return null;
306
+ const { L, C, h } = aiHexOklch(hex);
307
+ if (L < 0.85) return null;
308
+ if (C < 0.012) return 'white';
309
+ if (C < 0.045) return h >= 40 && h <= 115 ? 'warm' : h >= 180 && h <= 280 ? 'cool' : 'white';
310
+ return 'tinted';
311
+ }
312
+ // ── Type ─────────────────────────────────────────────────────────────────
313
+ const DISPLAY_FAMILIES = /anton|oswald|bebas|archivo black|syne|abril|alfa slab|league gothic|impact|barlow condensed|fjalla|teko|righteous|bungee|passion one|staatliches|six caps|big shoulders/;
314
+ const SERIF_FAMILIES = /playfair|georgia|times|garamond|merriweather|lora\b|baskerville|cormorant|dm serif|fraunces|newsreader|source serif|crimson|caslon|bodoni|didot|instrument serif|spectral|cardo|prata|marcellus|noto serif|pt serif|zilla|domine|libre serif|eb garamond|cinzel/;
315
+ /** One font family's mood. */ export function inspirationFontMood(family) {
316
+ const name = family.replace(/["']/g, '').trim().toLowerCase();
317
+ if (!name) return null;
318
+ if (DISPLAY_FAMILIES.test(name)) return 'display';
319
+ if (/\bsans\b/.test(name) || name === 'sans-serif') return 'sans';
320
+ if (SERIF_FAMILIES.test(name) || name === 'serif' || /\bserif\b/.test(name)) return 'serif';
321
+ return 'sans';
322
+ }
323
+ const HEADING_SELECTOR = /(^|[\s,>])(h1|h2|\.h1|\.h2|\.heading|\.title|\.display|\.headline)\b/;
324
+ const BODY_SELECTOR = /(^|,)\s*(body|html|:root)\s*(,|$)/;
325
+ /** The mood of the headings' type: their font family, else the page's, else the first family it loads. */ function fontMoodOf(html, rules) {
326
+ const familyOf = (rule)=>{
327
+ var _ref, _ref1;
328
+ var _value_split_;
329
+ const value = (_ref = rule == null ? void 0 : rule.decls.get('font-family')) != null ? _ref : ((_ref1 = rule == null ? void 0 : rule.decls.get('font')) != null ? _ref1 : '').replace(/^[^"'a-z]*\d[^\s]*\s+/, '');
330
+ const first = (_value_split_ = value.split(',')[0]) == null ? void 0 : _value_split_.trim();
331
+ return first && !first.startsWith('var(') ? first : null;
332
+ };
333
+ const heading = familyOf(rules.find((rule)=>HEADING_SELECTOR.test(rule.selector) && (rule.decls.has('font-family') || rule.decls.has('font'))));
334
+ if (heading) return inspirationFontMood(heading);
335
+ const loaded = /fonts\.googleapis\.com\/css2?\?family=([^&"':]+)/i.exec(html);
336
+ if (loaded == null ? void 0 : loaded[1]) return inspirationFontMood(decodeURIComponent(loaded[1].replace(/\+/g, ' ')));
337
+ const body = familyOf(rules.find((rule)=>BODY_SELECTOR.test(rule.selector) && (rule.decls.has('font-family') || rule.decls.has('font'))));
338
+ return body ? inspirationFontMood(body) : null;
339
+ }
340
+ // ── Buttons, cards, corners ──────────────────────────────────────────────
341
+ const BUTTON_SELECTOR = /(^|[\s,>.])(btn|button|\.cta|\[type=.?submit)/;
342
+ const CARD_SELECTOR = /\.[\w-]*(card|tile|panel|box|feature|service|item|plan|post)s?\b/;
343
+ /** A radius as the look's corners. */ function cornersOf(radius) {
344
+ return radius <= 2 ? 'sharp' : radius <= 10 ? 'soft' : radius <= 20 ? 'round' : 'pill';
345
+ }
346
+ /** How the main buttons are drawn: their shape and casing, and whether they are filled. */ function buttonSignals(html, rules) {
347
+ const buttonRules = rules.filter((rule)=>BUTTON_SELECTOR.test(rule.selector) && !/:hover|:focus|:active/.test(rule.selector));
348
+ const classes = [
349
+ ...html.matchAll(/<(?:a|button)\b[^>]*\bclass\s*=\s*["']([^"']*\b(?:btn|button|cta)\b[^"']*)["']/gi)
350
+ ].map((match)=>{
351
+ var _match_;
352
+ return ((_match_ = match[1]) != null ? _match_ : '').toLowerCase();
353
+ }).join(' ');
354
+ const radii = buttonRules.flatMap((rule)=>{
355
+ const value = rule.decls.get('border-radius');
356
+ if (!value) return [];
357
+ const amount = px(value);
358
+ return amount === null ? [] : [
359
+ value.includes('%') ? 999 : amount
360
+ ];
361
+ });
362
+ if (/\brounded-full\b/.test(classes)) radii.push(999);
363
+ if (/\brounded-none\b/.test(classes)) radii.push(0);
364
+ const caps = buttonRules.some((rule)=>rule.decls.get('text-transform') === 'uppercase') || /\buppercase\b/.test(classes);
365
+ const underlined = buttonRules.length > 0 && buttonRules.every((rule)=>{
366
+ var _rule_decls_get, _rule_decls_get1;
367
+ return /underline/.test((_rule_decls_get = rule.decls.get('text-decoration')) != null ? _rule_decls_get : '') && !draws((_rule_decls_get1 = rule.decls.get('background-color')) != null ? _rule_decls_get1 : rule.decls.get('background'));
368
+ });
369
+ const radius = median(radii);
370
+ let buttons = null;
371
+ if (underlined) buttons = 'link';
372
+ else if (radius !== null && radius >= 24) buttons = 'pill';
373
+ else if (caps) buttons = 'caps';
374
+ else if (radius !== null) buttons = radius <= 2 ? 'square' : 'rounded';
375
+ const filled = buttonRules.some((rule)=>{
376
+ var _rule_decls_get;
377
+ const fill = (_rule_decls_get = rule.decls.get('background-color')) != null ? _rule_decls_get : rule.decls.get('background');
378
+ return draws(fill) && colorOf(fill) !== null;
379
+ });
380
+ const outlined = buttonRules.some((rule)=>draws(rule.decls.get('border')) || draws(rule.decls.get('border-width')));
381
+ const buttonFill = filled ? 'filled' : outlined ? 'outline' : null;
382
+ return {
383
+ buttons,
384
+ buttonFill,
385
+ radius
386
+ };
387
+ }
388
+ /** How cards are drawn, and the corners they and the page's fields round to. */ function cardSignals(rules) {
389
+ const cardRules = rules.filter((rule)=>CARD_SELECTOR.test(rule.selector) && !BUTTON_SELECTOR.test(rule.selector) && !/:hover|:focus/.test(rule.selector));
390
+ if (!cardRules.length) return {
391
+ cards: null,
392
+ corners: null
393
+ };
394
+ const radius = median(cardRules.flatMap((rule)=>{
395
+ const amount = px(rule.decls.get('border-radius'));
396
+ return amount === null ? [] : [
397
+ amount
398
+ ];
399
+ }));
400
+ const any = (property)=>cardRules.some((rule)=>draws(rule.decls.get(property)));
401
+ let cards = 'flat';
402
+ if (any('box-shadow')) cards = 'elevated';
403
+ else if (any('border-top') && !any('border')) cards = 'rule';
404
+ else if (any('border')) cards = 'outlined';
405
+ else {
406
+ const fill = cardRules.map((rule)=>{
407
+ var _rule_decls_get;
408
+ return colorOf((_rule_decls_get = rule.decls.get('background-color')) != null ? _rule_decls_get : rule.decls.get('background'));
409
+ }).find(Boolean);
410
+ if (fill && aiHexOklch(fill).C >= 0.02 && aiHexOklch(fill).L > 0.8) cards = 'tinted';
411
+ }
412
+ return {
413
+ cards,
414
+ corners: radius === null ? null : cornersOf(radius)
415
+ };
416
+ }
417
+ // ── Motion ───────────────────────────────────────────────────────────────
418
+ /** How the page moves as it is scrolled: the scroll-animation names in its markup and the keyframes in its styles. */ function motionOf(html, css, rules, styled) {
419
+ const score = {
420
+ sides: 0,
421
+ rise: 0,
422
+ scale: 0
423
+ };
424
+ const named = [
425
+ ...[
426
+ ...html.matchAll(/\bdata-(?:aos|sal|animate)\s*=\s*["']([^"']+)["']/gi)
427
+ ].map((match)=>{
428
+ var _match_;
429
+ return (_match_ = match[1]) != null ? _match_ : '';
430
+ }),
431
+ ...[
432
+ ...html.matchAll(/\banimate__(\w+)|\bwow\s+(\w+)/gi)
433
+ ].map((match)=>{
434
+ var _ref, _match_;
435
+ return (_ref = (_match_ = match[1]) != null ? _match_ : match[2]) != null ? _ref : '';
436
+ })
437
+ ].map((name)=>name.toLowerCase());
438
+ for (const name of named){
439
+ if (/left|right/.test(name)) score.sides += 1;
440
+ else if (/zoom|scale/.test(name)) score.scale += 1;
441
+ else if (/up|down|fade|slide/.test(name)) score.rise += 1;
442
+ }
443
+ // Server-rendered scroll animations start hidden and displaced, in the markup's own styles.
444
+ for (const match of html.matchAll(STYLE_ATTRIBUTE)){
445
+ var _ref, _match_;
446
+ const style = ((_ref = (_match_ = match[1]) != null ? _match_ : match[2]) != null ? _ref : '').toLowerCase();
447
+ if (!/opacity\s*:\s*0(\.0+)?\s*(;|$)/.test(style)) continue;
448
+ if (/translatex|translate3d\(\s*-?\d/.test(style)) score.sides += 1;
449
+ else if (/scale/.test(style)) score.scale += 1;
450
+ else if (/translatey/.test(style)) score.rise += 1;
451
+ }
452
+ for (const frames of css.matchAll(/@keyframes\s+[\w-]+\s*\{((?:[^{}]*\{[^{}]*\})*)[^{}]*\}/gi)){
453
+ var _frames_;
454
+ const body = ((_frames_ = frames[1]) != null ? _frames_ : '').toLowerCase();
455
+ if (!/opacity/.test(body) && !/translate|scale/.test(body)) continue;
456
+ if (/translatex|translate3d\(\s*-?\d+(px|%)\s*,\s*0/.test(body)) score.sides += 1;
457
+ else if (/scale/.test(body)) score.scale += 1;
458
+ else if (/translatey|translate3d\(\s*0\s*,/.test(body)) score.rise += 1;
459
+ }
460
+ const delays = new Set([
461
+ ...[
462
+ ...html.matchAll(/\bdata-(?:aos|sal)-delay\s*=\s*["'](\d+)["']/gi)
463
+ ].map((match)=>match[1]),
464
+ ...rules.flatMap((rule)=>[
465
+ rule.decls.get('animation-delay'),
466
+ rule.decls.get('transition-delay')
467
+ ]).filter((value)=>Boolean(value && !NONE.test(value)))
468
+ ]);
469
+ const moving = score.sides + score.rise + score.scale;
470
+ if (moving > 0 && delays.size >= 3) return 'cascade';
471
+ if (moving > 0) {
472
+ const best = Object.entries(score).sort((a, b)=>b[1] - a[1])[0];
473
+ return best[0];
474
+ }
475
+ // A page whose styles were read and that animates nothing is still.
476
+ return styled ? 'still' : null;
477
+ }
478
+ // ── Features ─────────────────────────────────────────────────────────────
479
+ /** What the page offers its visitors, from its sections, its links and what its forms ask. */ function featuresOf(html, sections) {
480
+ const urls = urlsOf(html).join(' ');
481
+ const words = wordsOf(html);
482
+ const has = (section)=>sections.includes(section);
483
+ const forms = [
484
+ ...html.matchAll(/<form\b[\s\S]*?<\/form\s*>/gi)
485
+ ].map((match)=>match[0]);
486
+ const asks = forms.some((form)=>/<textarea\b/i.test(form) || count(form, /<input\b(?![^>]*type\s*=\s*["']?(hidden|submit|search|checkbox))/gi) >= 2);
487
+ const found = {
488
+ booking: has('booking') || BOOKING_HOSTS.test(urls) || /\bbook (now|online|an appointment|a table|a session|a call|a visit)\b|reserve a table|make a reservation/.test(words),
489
+ store: has('products') || /add to (cart|bag)|view cart/.test(words) || /\/cart\b|\/products?\/|\/collections\/|cdn\.shopify|woocommerce/.test(urls),
490
+ blog: has('blog') || /\/(blog|journal|articles|posts|news)(\/|\b)|application\/rss\+xml/.test(`${urls} ${html.toLowerCase()}`),
491
+ forms: has('contact') || asks,
492
+ gallery: has('gallery') || count(html, /<img\b/gi) >= 8,
493
+ events: has('events') || /\/events?(\/|\b)|eventbrite|ticketmaster|dice\.fm|bandsintown/.test(urls),
494
+ menu: has('menu') || /\/menus?(\/|\b)/.test(urls) || count(words, /\b(appetizers|entr[eé]es|starters|mains|desserts|small plates)\b/g) >= 2,
495
+ testimonials: has('testimonials') || /yelp\.com|trustpilot\.com|g\.page|google\.com\/maps.*reviews/.test(urls),
496
+ newsletter: has('newsletter') || /list-manage\.com|klaviyo|convertkit|mailerlite|substack\.com/.test(urls)
497
+ };
498
+ return AI_SITE_INSPIRATION_FEATURES.filter((feature)=>found[feature]);
499
+ }
500
+ // ── One page ─────────────────────────────────────────────────────────────
501
+ /**
502
+ * A read page's signals (AGL-3975). Pure over the page and its sheets, so the
503
+ * specs read recorded pages; nothing of the page but these values leaves.
504
+ */ export function aiSiteInspirationSignals(page) {
505
+ var _card_corners;
506
+ const html = page.html.replace(COMMENTS, ' ');
507
+ const inline = [
508
+ ...html.matchAll(STYLE_BLOCK)
509
+ ].map((match)=>{
510
+ var _match_;
511
+ return (_match_ = match[1]) != null ? _match_ : '';
512
+ });
513
+ const css = [
514
+ ...inline,
515
+ ...page.stylesheets
516
+ ].join('\n');
517
+ const rules = cssRulesOf(css);
518
+ const sections = inspirationSectionsOf(html);
519
+ const button = buttonSignals(html, rules);
520
+ const card = cardSignals(rules);
521
+ return _extends({
522
+ sections,
523
+ density: densityOf(html, rules)
524
+ }, headerSignals(html, rules), {
525
+ colors: referencePageColors({
526
+ html,
527
+ stylesheets: page.stylesheets
528
+ }).slice(0, AI_THEME_BRAND_COLORS_PER_SOURCE),
529
+ ground: groundOf(rules),
530
+ fontMood: fontMoodOf(html, rules),
531
+ buttons: button.buttons,
532
+ buttonFill: button.buttonFill,
533
+ cards: card.cards,
534
+ // A page with no cards rounds its corners as its buttons do, unless they are pills.
535
+ corners: (_card_corners = card.corners) != null ? _card_corners : button.radius !== null && button.radius < 24 ? cornersOf(button.radius) : null,
536
+ motion: motionOf(html, css, rules, css.trim().length > 0),
537
+ features: featuresOf(html, sections)
538
+ });
539
+ }
540
+ // ── Read back ────────────────────────────────────────────────────────────
541
+ const oneOf = (value, values)=>typeof value === 'string' && values.includes(value) ? value : null;
542
+ const listOf = (value, values, max)=>Array.isArray(value) ? value.flatMap((entry)=>oneOf(entry, values) ? [
543
+ entry
544
+ ] : []).slice(0, max) : [];
545
+ /**
546
+ * Stored signals, read back defensively: a plan travels through the confirm
547
+ * door and a unit's inputs, so every value is held to its closed set again
548
+ * and anything else is dropped. `null` for nothing usable.
549
+ */ export function aiReadSiteInspiration(value) {
550
+ if (!value || typeof value !== 'object') return null;
551
+ const raw = value;
552
+ const sites = (Array.isArray(raw['sites']) ? raw['sites'] : []).slice(0, 3).flatMap((entry)=>{
553
+ if (!entry || typeof entry !== 'object') return [];
554
+ const site = entry;
555
+ const colors = Array.isArray(site['colors']) ? site['colors'].flatMap((hex)=>typeof hex === 'string' && normalizeThemeHex(hex) ? [
556
+ normalizeThemeHex(hex)
557
+ ] : []).slice(0, AI_THEME_BRAND_COLORS_PER_SOURCE) : [];
558
+ return [
559
+ {
560
+ sections: listOf(site['sections'], AI_SITE_INSPIRATION_SECTIONS, AI_SITE_INSPIRATION_MAX_SECTIONS),
561
+ density: oneOf(site['density'], DENSITIES),
562
+ headerAlign: oneOf(site['headerAlign'], ALIGNS),
563
+ header: oneOf(site['header'], AI_SITE_HEADERS),
564
+ colors,
565
+ ground: oneOf(site['ground'], AI_SITE_GROUNDS),
566
+ fontMood: oneOf(site['fontMood'], AI_SITE_INSPIRATION_FONT_MOODS),
567
+ buttons: oneOf(site['buttons'], AI_SITE_BUTTONS),
568
+ buttonFill: oneOf(site['buttonFill'], AI_SITE_INSPIRATION_BUTTON_FILLS),
569
+ corners: oneOf(site['corners'], AI_SITE_CORNERS),
570
+ cards: oneOf(site['cards'], AI_SITE_CARDS),
571
+ motion: aiSiteMotionStyle(site['motion']),
572
+ features: listOf(site['features'], AI_SITE_INSPIRATION_FEATURES, AI_SITE_INSPIRATION_FEATURES.length)
573
+ }
574
+ ];
575
+ });
576
+ if (!sites.length) return null;
577
+ const unread = typeof raw['unread'] === 'number' && Number.isInteger(raw['unread']) && raw['unread'] >= 0 ? Math.min(3, raw['unread']) : 0;
578
+ return {
579
+ v: 1,
580
+ sites,
581
+ unread
582
+ };
583
+ }
584
+
585
+ //# sourceMappingURL=ai-site-inspiration-signals.js.map