@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,57 @@
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 { AiSiteContact } from '../model/ai-site-details';
18
+ import type { AiLayoutBlock, AiLayoutItem, AiLayoutSection } from './ai-layout-language';
19
+ /**
20
+ * The business's phone and email on its pages (AGL-3979), by code.
21
+ *
22
+ * Told them, a model writes them into the copy where it sees fit — a header
23
+ * button that calls, a line in an About page. Two places are not left to it:
24
+ * the footer, which is on every page, and the page that places the site's
25
+ * form, which is where a visitor goes to get in touch. Each shows the phone
26
+ * and the email, as links that call and write, wherever the design did not
27
+ * already show them. Nothing is added for a detail the person did not give,
28
+ * so a start without them builds exactly the pages it built before.
29
+ */
30
+ /** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */
31
+ export declare const AI_LAYOUT_CONTACT_LABEL_MAX = 40;
32
+ /** Which of the phone and the email some blocks already show, in their words or as links. */
33
+ export declare function aiLayoutContactShown(blocks: readonly AiLayoutBlock[], contact: AiSiteContact): {
34
+ phone: boolean;
35
+ email: boolean;
36
+ };
37
+ /**
38
+ * A contact link's words, kept whole (AGL-3979): words that carry the phone
39
+ * number or the email are a label a cut would break — "Email
40
+ * hello@harborline.co" cut to a button's 28 characters is "Email" — so they
41
+ * keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number
42
+ * or address. `''` for words that carry neither.
43
+ */
44
+ export declare function aiLayoutContactLabel(text: string | undefined, contact: AiSiteContact | null | undefined): string;
45
+ /** The lines that link what is not yet shown: the phone, then the email. */
46
+ export declare function aiLayoutContactItems(contact: AiSiteContact, shown?: {
47
+ phone: boolean;
48
+ email: boolean;
49
+ }): AiLayoutItem[];
50
+ /**
51
+ * A page's sections with the phone and email in the section that places the
52
+ * site's form (AGL-3979), where no section of the page shows them yet: a list
53
+ * of the two links, under the words beside the form — in their column, in a
54
+ * row — or, in a section of one column, just before the form. The sections
55
+ * as given where there is no such section or nothing to add.
56
+ */
57
+ export declare function aiLayoutWithContactLines(sections: readonly AiLayoutSection[], formSection: number | null, contact: AiSiteContact | null | undefined): readonly AiLayoutSection[];
@@ -0,0 +1,137 @@
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 { _ as _extends } from "@swc/helpers/_/_extends";
17
+ /**
18
+ * The business's phone and email on its pages (AGL-3979), by code.
19
+ *
20
+ * Told them, a model writes them into the copy where it sees fit — a header
21
+ * button that calls, a line in an About page. Two places are not left to it:
22
+ * the footer, which is on every page, and the page that places the site's
23
+ * form, which is where a visitor goes to get in touch. Each shows the phone
24
+ * and the email, as links that call and write, wherever the design did not
25
+ * already show them. Nothing is added for a detail the person did not give,
26
+ * so a start without them builds exactly the pages it built before.
27
+ */ /** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */ export const AI_LAYOUT_CONTACT_LABEL_MAX = 40;
28
+ const digitsOf = (value)=>value.replace(/\D/g, '');
29
+ /** Whether words carry the phone number (by its digits) or the email address the job was given. */ function says(text, contact) {
30
+ const value = text != null ? text : '';
31
+ const digits = digitsOf(contact.phone);
32
+ return {
33
+ phone: Boolean(contact.phone) && digits.length >= 7 && digitsOf(value).includes(digits),
34
+ email: Boolean(contact.email) && value.toLowerCase().includes(contact.email.toLowerCase())
35
+ };
36
+ }
37
+ /** Whether a link names the phone or the email (`phone`, `tel:…`, `email`, `mailto:…`). */ function links(to) {
38
+ const lower = (to != null ? to : '').trim().toLowerCase();
39
+ return {
40
+ phone: lower === 'phone' || lower === 'call' || lower.startsWith('tel:'),
41
+ email: lower === 'email' || lower === 'mail' || lower.startsWith('mailto:')
42
+ };
43
+ }
44
+ /** Which of the phone and the email some blocks already show, in their words or as links. */ export function aiLayoutContactShown(blocks, contact) {
45
+ const shown = {
46
+ phone: false,
47
+ email: false
48
+ };
49
+ const note = (found)=>{
50
+ var _shown, _shown1;
51
+ (_shown = shown).phone || (_shown.phone = found.phone);
52
+ (_shown1 = shown).email || (_shown1.email = found.email);
53
+ };
54
+ for (const block of blocks){
55
+ var _block_items;
56
+ note(says(block.text, contact));
57
+ note(links(block.to));
58
+ for (const item of (_block_items = block.items) != null ? _block_items : []){
59
+ note(says(`${item.title} ${item.text}`, contact));
60
+ note(links(item.to));
61
+ }
62
+ }
63
+ return shown;
64
+ }
65
+ /**
66
+ * A contact link's words, kept whole (AGL-3979): words that carry the phone
67
+ * number or the email are a label a cut would break — "Email
68
+ * hello@harborline.co" cut to a button's 28 characters is "Email" — so they
69
+ * keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number
70
+ * or address. `''` for words that carry neither.
71
+ */ export function aiLayoutContactLabel(text, contact) {
72
+ if (!contact) return '';
73
+ const value = (text != null ? text : '').replace(/\s+/g, ' ').trim();
74
+ const found = says(value, contact);
75
+ if (!found.phone && !found.email) return '';
76
+ if (value.length <= AI_LAYOUT_CONTACT_LABEL_MAX) return value;
77
+ return (found.email ? contact.email : contact.phone).slice(0, AI_LAYOUT_CONTACT_LABEL_MAX);
78
+ }
79
+ /** The lines that link what is not yet shown: the phone, then the email. */ export function aiLayoutContactItems(contact, shown = {
80
+ phone: false,
81
+ email: false
82
+ }) {
83
+ return [
84
+ ...contact.phoneHref && !shown.phone ? [
85
+ {
86
+ title: contact.phone,
87
+ text: '',
88
+ to: 'phone'
89
+ }
90
+ ] : [],
91
+ ...contact.emailHref && !shown.email ? [
92
+ {
93
+ title: contact.email,
94
+ text: '',
95
+ to: 'email'
96
+ }
97
+ ] : []
98
+ ];
99
+ }
100
+ /**
101
+ * A page's sections with the phone and email in the section that places the
102
+ * site's form (AGL-3979), where no section of the page shows them yet: a list
103
+ * of the two links, under the words beside the form — in their column, in a
104
+ * row — or, in a section of one column, just before the form. The sections
105
+ * as given where there is no such section or nothing to add.
106
+ */ export function aiLayoutWithContactLines(sections, formSection, contact) {
107
+ if (!contact || formSection === null || !sections[formSection]) return sections;
108
+ const shown = aiLayoutContactShown(sections.flatMap((section)=>section.blocks), contact);
109
+ const items = aiLayoutContactItems(contact, shown);
110
+ if (!items.length) return sections;
111
+ const section = sections[formSection];
112
+ const blocks = [
113
+ ...section.blocks
114
+ ];
115
+ const formAt = blocks.findIndex((block)=>block.kind === 'form');
116
+ const form = formAt === -1 ? null : blocks[formAt];
117
+ const cols = section.cols && section.cols.length >= 2 ? section.cols.length : 0;
118
+ const besideForm = cols ? [
119
+ ...blocks
120
+ ].reverse().find((block)=>block.kind !== 'form' && block.col !== undefined && block.col !== (form == null ? void 0 : form.col)) : undefined;
121
+ const list = _extends({
122
+ kind: 'list',
123
+ items
124
+ }, (besideForm == null ? void 0 : besideForm.col) !== undefined ? {
125
+ col: besideForm.col
126
+ } : (form == null ? void 0 : form.col) !== undefined ? {
127
+ col: form.col
128
+ } : {});
129
+ if (besideForm) blocks.splice(blocks.lastIndexOf(besideForm) + 1, 0, list);
130
+ else if (formAt !== -1) blocks.splice(formAt, 0, list);
131
+ else blocks.push(list);
132
+ return sections.map((each, index)=>index === formSection ? _extends({}, section, {
133
+ blocks
134
+ }) : each);
135
+ }
136
+
137
+ //# sourceMappingURL=ai-layout-contact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-contact.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiSiteContact } from '../model/ai-site-details'\nimport type { AiLayoutBlock, AiLayoutItem, AiLayoutSection } from './ai-layout-language'\n\n/**\n * The business's phone and email on its pages (AGL-3979), by code.\n *\n * Told them, a model writes them into the copy where it sees fit — a header\n * button that calls, a line in an About page. Two places are not left to it:\n * the footer, which is on every page, and the page that places the site's\n * form, which is where a visitor goes to get in touch. Each shows the phone\n * and the email, as links that call and write, wherever the design did not\n * already show them. Nothing is added for a detail the person did not give,\n * so a start without them builds exactly the pages it built before.\n */\n\n/** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */\nexport const AI_LAYOUT_CONTACT_LABEL_MAX = 40\n\nconst digitsOf = (value: string) => value.replace(/\\D/g, '')\n\n/** Whether words carry the phone number (by its digits) or the email address the job was given. */\nfunction says(text: string | undefined, contact: AiSiteContact): { phone: boolean; email: boolean } {\n const value = text ?? ''\n const digits = digitsOf(contact.phone)\n return {\n phone: Boolean(contact.phone) && digits.length >= 7 && digitsOf(value).includes(digits),\n email: Boolean(contact.email) && value.toLowerCase().includes(contact.email.toLowerCase()),\n }\n}\n\n/** Whether a link names the phone or the email (`phone`, `tel:…`, `email`, `mailto:…`). */\nfunction links(to: string | undefined): { phone: boolean; email: boolean } {\n const lower = (to ?? '').trim().toLowerCase()\n return {\n phone: lower === 'phone' || lower === 'call' || lower.startsWith('tel:'),\n email: lower === 'email' || lower === 'mail' || lower.startsWith('mailto:'),\n }\n}\n\n/** Which of the phone and the email some blocks already show, in their words or as links. */\nexport function aiLayoutContactShown(\n blocks: readonly AiLayoutBlock[],\n contact: AiSiteContact,\n): { phone: boolean; email: boolean } {\n const shown = { phone: false, email: false }\n const note = (found: { phone: boolean; email: boolean }) => {\n shown.phone ||= found.phone\n shown.email ||= found.email\n }\n for (const block of blocks) {\n note(says(block.text, contact))\n note(links(block.to))\n for (const item of block.items ?? []) {\n note(says(`${item.title} ${item.text}`, contact))\n note(links(item.to))\n }\n }\n return shown\n}\n\n/**\n * A contact link's words, kept whole (AGL-3979): words that carry the phone\n * number or the email are a label a cut would break — \"Email\n * hello@harborline.co\" cut to a button's 28 characters is \"Email\" — so they\n * keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number\n * or address. `''` for words that carry neither.\n */\nexport function aiLayoutContactLabel(text: string | undefined, contact: AiSiteContact | null | undefined): string {\n if (!contact) return ''\n const value = (text ?? '').replace(/\\s+/g, ' ').trim()\n const found = says(value, contact)\n if (!found.phone && !found.email) return ''\n if (value.length <= AI_LAYOUT_CONTACT_LABEL_MAX) return value\n return (found.email ? contact.email : contact.phone).slice(0, AI_LAYOUT_CONTACT_LABEL_MAX)\n}\n\n/** The lines that link what is not yet shown: the phone, then the email. */\nexport function aiLayoutContactItems(\n contact: AiSiteContact,\n shown: { phone: boolean; email: boolean } = { phone: false, email: false },\n): AiLayoutItem[] {\n return [\n ...(contact.phoneHref && !shown.phone ? [{ title: contact.phone, text: '', to: 'phone' }] : []),\n ...(contact.emailHref && !shown.email ? [{ title: contact.email, text: '', to: 'email' }] : []),\n ]\n}\n\n/**\n * A page's sections with the phone and email in the section that places the\n * site's form (AGL-3979), where no section of the page shows them yet: a list\n * of the two links, under the words beside the form — in their column, in a\n * row — or, in a section of one column, just before the form. The sections\n * as given where there is no such section or nothing to add.\n */\nexport function aiLayoutWithContactLines(\n sections: readonly AiLayoutSection[],\n formSection: number | null,\n contact: AiSiteContact | null | undefined,\n): readonly AiLayoutSection[] {\n if (!contact || formSection === null || !sections[formSection]) return sections\n const shown = aiLayoutContactShown(sections.flatMap((section) => section.blocks), contact)\n const items = aiLayoutContactItems(contact, shown)\n if (!items.length) return sections\n const section = sections[formSection]\n const blocks = [...section.blocks]\n const formAt = blocks.findIndex((block) => block.kind === 'form')\n const form = formAt === -1 ? null : blocks[formAt]\n const cols = section.cols && section.cols.length >= 2 ? section.cols.length : 0\n const besideForm = cols\n ? [...blocks].reverse().find((block) => block.kind !== 'form' && block.col !== undefined && block.col !== form?.col)\n : undefined\n const list: AiLayoutBlock = {\n kind: 'list',\n items,\n ...(besideForm?.col !== undefined ? { col: besideForm.col } : form?.col !== undefined ? { col: form.col } : {}),\n }\n if (besideForm) blocks.splice(blocks.lastIndexOf(besideForm) + 1, 0, list)\n else if (formAt !== -1) blocks.splice(formAt, 0, list)\n else blocks.push(list)\n return sections.map((each, index) => (index === formSection ? { ...section, blocks } : each))\n}\n"],"names":["AI_LAYOUT_CONTACT_LABEL_MAX","digitsOf","value","replace","says","text","contact","digits","phone","Boolean","length","includes","email","toLowerCase","links","to","lower","trim","startsWith","aiLayoutContactShown","blocks","shown","note","found","block","item","items","title","aiLayoutContactLabel","slice","aiLayoutContactItems","phoneHref","emailHref","aiLayoutWithContactLines","sections","formSection","flatMap","section","formAt","findIndex","kind","form","cols","besideForm","reverse","find","col","undefined","list","splice","lastIndexOf","push","map","each","index"],"mappings":"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"}
@@ -50,6 +50,18 @@ export declare const AI_LAYOUT_TEXT_LIMITS: {
50
50
  export type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS;
51
51
  /** Collapses whitespace and takes out markup a page would print. */
52
52
  export declare function aiLayoutCleanText(value: unknown): string;
53
+ /**
54
+ * "a" and "an" made to agree with the word after them, in English copy
55
+ * (AGL-3988): the beta.270 Sugar Hollow packages said "A easygoing session"
56
+ * twice, in their records and on the home page's cards. Only where the
57
+ * letters settle it: "a" before a word opening a, e, i or o becomes "an" —
58
+ * never before "one" or "once", or "eu" ("a European"); "an" before a word
59
+ * opening a consonant other than h becomes "a". A word opening u or h, or
60
+ * one in capitals ("an FAQ"), is said as the model wrote it, and so is a
61
+ * capital "A" inside a sentence, a letter rather than an article ("Plan A
62
+ * is"). Any other language's text comes back as it is.
63
+ */
64
+ export declare function aiLayoutArticles(text: string, language?: string | null): string;
53
65
  /**
54
66
  * The line with every phone number and email address the brief did not give
55
67
  * written as the gap (rule 14): a site's contact details are facts, and a
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ import { aiDanglingWord, detectOffVoiceCopy } from "../runtime/ai-doctrine-validators.js";
17
+ import { aiLayoutSpeaksOtherThanEnglish } from "./ai-layout-labels.js";
17
18
  /**
18
19
  * The words of a layout language document, made to fit where they are shown
19
20
  * (AGL-3660). Every word is the model's; the compiler only cleans and fits
@@ -49,6 +50,32 @@
49
50
  return value.replace(MARKUP, '')// A JSON escape a model wrote out as text ("\\u2014") is the character it names.
50
51
  .replace(/\\u([0-9a-fA-F]{4})/g, (_, hex)=>String.fromCharCode(parseInt(hex, 16))).replace(/\s+/g, ' ').trim();
51
52
  }
53
+ /**
54
+ * "a" and "an" made to agree with the word after them, in English copy
55
+ * (AGL-3988): the beta.270 Sugar Hollow packages said "A easygoing session"
56
+ * twice, in their records and on the home page's cards. Only where the
57
+ * letters settle it: "a" before a word opening a, e, i or o becomes "an" —
58
+ * never before "one" or "once", or "eu" ("a European"); "an" before a word
59
+ * opening a consonant other than h becomes "a". A word opening u or h, or
60
+ * one in capitals ("an FAQ"), is said as the model wrote it, and so is a
61
+ * capital "A" inside a sentence, a letter rather than an article ("Plan A
62
+ * is"). Any other language's text comes back as it is.
63
+ */ export function aiLayoutArticles(text, language) {
64
+ if (!text || aiLayoutSpeaksOtherThanEnglish(language)) return text;
65
+ return text.replace(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}'’-])(a|an|A|An)(\\s+)([A-Za-z][\\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}'’-]*)", "gu"), (whole, article, space, word, at)=>{
66
+ // A capital opens a sentence; anywhere else "A" is a letter, never an article.
67
+ if (/^A/.test(article) && !/(?:^|[.!?:]["”’)]?\s*)$/.test(text.slice(0, at))) return whole;
68
+ // An initialism is said by its letters, which the letters alone do not settle.
69
+ if (/^[A-Z]{2,}/.test(word)) return whole;
70
+ const lower = word.toLowerCase();
71
+ const vowel = /^[aeio]/.test(lower) && !/^(?:one(?![a-z])|once|eu|ewe)/.test(lower);
72
+ const consonant = /^[bcdfgjklmnpqrstvwxyz]/.test(lower);
73
+ const an = article.toLowerCase() === 'an';
74
+ if (!an && vowel) return `${article === 'a' ? 'an' : 'An'}${space}${word}`;
75
+ if (an && consonant) return `${article === 'an' ? 'a' : 'A'}${space}${word}`;
76
+ return whole;
77
+ });
78
+ }
52
79
  /**
53
80
  * The line with every trailing dangling word taken off, as rule 14 reads one,
54
81
  * in the site's language (AGL-3828): a Spanish line cut to "…para la" loses
@@ -68,7 +95,7 @@
68
95
  * room, else the last clause boundary that does, else the last whole word. A
69
96
  * line that fits is returned clean, with any dangling tail taken off.
70
97
  */ function fitText(value, place, language) {
71
- const text = aiLayoutCleanText(value);
98
+ const text = aiLayoutArticles(aiLayoutCleanText(value), language);
72
99
  const limit = AI_LAYOUT_TEXT_LIMITS[place];
73
100
  if (text.length <= limit) return withoutDanglingTail(text, language);
74
101
  const room = text.slice(0, limit + 1);
@@ -107,7 +134,12 @@ const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
107
134
  const digits = digitsOf(phone);
108
135
  // A year, a price or a count is not a phone number.
109
136
  if (digits.length < 7) return phone;
110
- return knownDigits.has(digits) ? phone : '[phone number]';
137
+ // One number with and without its country code is one number (AGL-3979):
138
+ // "+1 512 555 0142" given admits "(512) 555-0142" written.
139
+ const known = [
140
+ ...knownDigits
141
+ ].some((given)=>given === digits || Math.min(given.length, digits.length) >= 7 && (given.endsWith(digits) || digits.endsWith(given)) && Math.abs(given.length - digits.length) <= 3);
142
+ return known ? phone : '[phone number]';
111
143
  });
112
144
  }
113
145
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-copy.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 {\n aiDanglingWord,\n detectOffVoiceCopy,\n} from '../runtime/ai-doctrine-validators'\n\n/**\n * The words of a layout language document, made to fit where they are shown\n * (AGL-3660). Every word is the model's; the compiler only cleans and fits\n * them: markup a person would see is taken out, a line too long for its place\n * ends at a clean boundary instead of mid-word, a line that would end on a\n * dangling word (\"…for you and\") loses it, and a phone number or an email\n * address the brief never gave is written as the bracketed gap the doctrine\n * asks for (rule 14) rather than shown as a fact.\n */\n\n/** Characters each place holds before its line is ended. */\nexport const AI_LAYOUT_TEXT_LIMITS = {\n /** A page's h1. */\n title: 70,\n /** A section's heading. */\n heading: 80,\n /** A card's, a step's or a sub-heading's title. */\n itemTitle: 60,\n eyebrow: 40,\n /** A button's label; the palette holds a label to 40. */\n label: 28,\n lede: 240,\n text: 420,\n itemText: 240,\n listItem: 140,\n stat: 14,\n statLabel: 60,\n question: 120,\n answer: 420,\n quote: 240,\n note: 200,\n /** An image's description, which is its alt text. */\n alt: 160,\n} as const\n\nexport type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS\n\n/** Inline markup a model writes out of habit, which a page would print literally. */\nconst MARKUP = /(\\*\\*|__|`|~~|^#{1,6}\\s+|^\\s*[-*•]\\s+)/gm\n\n/** A sentence's end inside a line. */\nconst SENTENCE_END = /[.!?…](?=[\"'”’)\\]]?\\s)/g\n\n/** A clause boundary inside a line: a comma, a semicolon, a colon or a dash, with what follows. */\nconst CLAUSE_END = /\\s*(?:[,;:]|\\s[—–-])\\s/g\n\n/** Punctuation that may not end a line it was cut back to. */\nconst TRAILING_JOINERS = /[\\s,;:—–-]+$/\n\n/** Collapses whitespace and takes out markup a page would print. */\nexport function aiLayoutCleanText(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value\n .replace(MARKUP, '')\n // A JSON escape a model wrote out as text (\"\\\\u2014\") is the character it names.\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * The line with every trailing dangling word taken off, as rule 14 reads one,\n * in the site's language (AGL-3828): a Spanish line cut to \"…para la\" loses\n * both words, as an English one cut to \"…for the\" does.\n */\nfunction withoutDanglingTail(line: string, language?: string): string {\n let text = line.replace(TRAILING_JOINERS, '').trim()\n for (let guard = 0; guard < 6; guard += 1) {\n const word = aiDanglingWord(text, language)\n if (!word) break\n text = text\n .slice(0, text.length - word.length)\n .replace(TRAILING_JOINERS, '')\n .trim()\n }\n return text\n}\n\n/**\n * A line held to the characters its place holds, ended where a reader expects\n * a line to end: at the last sentence's end that leaves at least half the\n * room, else the last clause boundary that does, else the last whole word. A\n * line that fits is returned clean, with any dangling tail taken off.\n */\nfunction fitText(value: unknown, place: AiLayoutTextPlace, language?: string): string {\n const text = aiLayoutCleanText(value)\n const limit = AI_LAYOUT_TEXT_LIMITS[place]\n if (text.length <= limit) return withoutDanglingTail(text, language)\n const room = text.slice(0, limit + 1)\n const half = Math.floor(limit * 0.4)\n const lastMatch = (pattern: RegExp): number => {\n let at = -1\n for (const match of room.matchAll(pattern)) {\n const end = (match.index ?? 0) + (pattern === SENTENCE_END ? 1 : 0)\n if (end <= limit && end >= half) at = end\n }\n return at\n }\n const sentence = lastMatch(SENTENCE_END)\n if (sentence > 0) return withoutDanglingTail(room.slice(0, sentence), language)\n const clause = lastMatch(CLAUSE_END)\n if (clause > 0) return withoutDanglingTail(room.slice(0, clause), language)\n const space = room.lastIndexOf(' ', limit)\n return withoutDanglingTail(\n space > half ? room.slice(0, space) : room.slice(0, limit),\n language,\n )\n}\n\n/** A phone number as people write one: seven or more digits with the usual separators. */\nconst PHONE = /\\(?\\+?\\d[\\d\\s().-]{6,}\\d/g\nconst EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g\n\n/** The digits of a phone number, which is what two spellings of one number share. */\nfunction digitsOf(value: string): string {\n return value.replace(/\\D/g, '')\n}\n\n/**\n * The line with every phone number and email address the brief did not give\n * written as the gap (rule 14): a site's contact details are facts, and a\n * model that writes one the brief lacks is inventing it. `facts` is every\n * word the job was given — the brief, the site's answers and its profile.\n */\nexport function aiLayoutGuardContacts(line: string, facts: string): string {\n const known = facts.toLowerCase()\n const knownDigits = new Set((facts.match(PHONE) ?? []).map(digitsOf))\n return line\n .replace(EMAIL, (email) =>\n known.includes(email.toLowerCase()) ? email : '[email address]',\n )\n .replace(PHONE, (phone) => {\n const digits = digitsOf(phone)\n // A year, a price or a count is not a phone number.\n if (digits.length < 7) return phone\n return knownDigits.has(digits) ? phone : '[phone number]'\n })\n}\n\n/**\n * A line fitted to its place with invented contact details written as gaps;\n * `language` is the site's (see `aiLayoutFitText`).\n */\nexport function aiLayoutWords(\n value: unknown,\n place: AiLayoutTextPlace,\n facts: string,\n language?: string,\n): string {\n return aiLayoutFitText(\n aiLayoutGuardContacts(aiLayoutCleanText(value), facts),\n place,\n language,\n )\n}\n\n/**\n * A line held to the characters its place holds (see `fitText`), and none at\n * all where what is left is filler. `language` is the site's language code,\n * which says what a dangling word is (AGL-3828); absent, English.\n */\nexport function aiLayoutFitText(\n value: unknown,\n place: AiLayoutTextPlace,\n language?: string,\n): string {\n const text = fitText(value, place, language)\n return aiLayoutIsFiller(text) ? '' : text\n}\n\n/**\n * Whether a line is filler rule 14 refuses (\"lorem ipsum\", \"your text here\"):\n * such a line is no copy, and the compiler leaves it out rather than ship it.\n */\nexport function aiLayoutIsFiller(text: string): boolean {\n return detectOffVoiceCopy([{ at: 'line', text }]).length > 0\n}\n\n/** A quote that cannot be a real customer's, written as the gap the owner fills with one (rule 14). */\nexport function aiLayoutQuoteGap(value: unknown, language?: string): string {\n const text = aiLayoutFitText(value, 'quote', language)\n .replace(/^[\"“'‘]+|[\"”'’]+$/g, '')\n .trim()\n if (!text) return ''\n if (/^\\[.*\\]$/.test(text)) return text\n return `[${text.replace(/^\\[|\\]$/g, '')}]`\n}\n"],"names":["aiDanglingWord","detectOffVoiceCopy","AI_LAYOUT_TEXT_LIMITS","title","heading","itemTitle","eyebrow","label","lede","text","itemText","listItem","stat","statLabel","question","answer","quote","note","alt","MARKUP","SENTENCE_END","CLAUSE_END","TRAILING_JOINERS","aiLayoutCleanText","value","replace","_","hex","String","fromCharCode","parseInt","trim","withoutDanglingTail","line","language","guard","word","slice","length","fitText","place","limit","room","half","Math","floor","lastMatch","pattern","at","match","matchAll","end","index","sentence","clause","space","lastIndexOf","PHONE","EMAIL","digitsOf","aiLayoutGuardContacts","facts","known","toLowerCase","knownDigits","Set","map","email","includes","phone","digits","has","aiLayoutWords","aiLayoutFitText","aiLayoutIsFiller","aiLayoutQuoteGap","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-copy.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 {\n aiDanglingWord,\n detectOffVoiceCopy,\n} from '../runtime/ai-doctrine-validators'\nimport { aiLayoutSpeaksOtherThanEnglish } from './ai-layout-labels'\n\n/**\n * The words of a layout language document, made to fit where they are shown\n * (AGL-3660). Every word is the model's; the compiler only cleans and fits\n * them: markup a person would see is taken out, a line too long for its place\n * ends at a clean boundary instead of mid-word, a line that would end on a\n * dangling word (\"…for you and\") loses it, and a phone number or an email\n * address the brief never gave is written as the bracketed gap the doctrine\n * asks for (rule 14) rather than shown as a fact.\n */\n\n/** Characters each place holds before its line is ended. */\nexport const AI_LAYOUT_TEXT_LIMITS = {\n /** A page's h1. */\n title: 70,\n /** A section's heading. */\n heading: 80,\n /** A card's, a step's or a sub-heading's title. */\n itemTitle: 60,\n eyebrow: 40,\n /** A button's label; the palette holds a label to 40. */\n label: 28,\n lede: 240,\n text: 420,\n itemText: 240,\n listItem: 140,\n stat: 14,\n statLabel: 60,\n question: 120,\n answer: 420,\n quote: 240,\n note: 200,\n /** An image's description, which is its alt text. */\n alt: 160,\n} as const\n\nexport type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS\n\n/** Inline markup a model writes out of habit, which a page would print literally. */\nconst MARKUP = /(\\*\\*|__|`|~~|^#{1,6}\\s+|^\\s*[-*•]\\s+)/gm\n\n/** A sentence's end inside a line. */\nconst SENTENCE_END = /[.!?…](?=[\"'”’)\\]]?\\s)/g\n\n/** A clause boundary inside a line: a comma, a semicolon, a colon or a dash, with what follows. */\nconst CLAUSE_END = /\\s*(?:[,;:]|\\s[—–-])\\s/g\n\n/** Punctuation that may not end a line it was cut back to. */\nconst TRAILING_JOINERS = /[\\s,;:—–-]+$/\n\n/** Collapses whitespace and takes out markup a page would print. */\nexport function aiLayoutCleanText(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value\n .replace(MARKUP, '')\n // A JSON escape a model wrote out as text (\"\\\\u2014\") is the character it names.\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * \"a\" and \"an\" made to agree with the word after them, in English copy\n * (AGL-3988): the beta.270 Sugar Hollow packages said \"A easygoing session\"\n * twice, in their records and on the home page's cards. Only where the\n * letters settle it: \"a\" before a word opening a, e, i or o becomes \"an\" —\n * never before \"one\" or \"once\", or \"eu\" (\"a European\"); \"an\" before a word\n * opening a consonant other than h becomes \"a\". A word opening u or h, or\n * one in capitals (\"an FAQ\"), is said as the model wrote it, and so is a\n * capital \"A\" inside a sentence, a letter rather than an article (\"Plan A\n * is\"). Any other language's text comes back as it is.\n */\nexport function aiLayoutArticles(text: string, language?: string | null): string {\n if (!text || aiLayoutSpeaksOtherThanEnglish(language)) return text\n return text.replace(\n /(?<![\\p{L}\\p{N}'’-])(a|an|A|An)(\\s+)([A-Za-z][\\p{L}\\p{N}'’-]*)/gu,\n (whole, article: string, space: string, word: string, at: number) => {\n // A capital opens a sentence; anywhere else \"A\" is a letter, never an article.\n if (/^A/.test(article) && !/(?:^|[.!?:][\"”’)]?\\s*)$/.test(text.slice(0, at))) return whole\n // An initialism is said by its letters, which the letters alone do not settle.\n if (/^[A-Z]{2,}/.test(word)) return whole\n const lower = word.toLowerCase()\n const vowel = /^[aeio]/.test(lower) && !/^(?:one(?![a-z])|once|eu|ewe)/.test(lower)\n const consonant = /^[bcdfgjklmnpqrstvwxyz]/.test(lower)\n const an = article.toLowerCase() === 'an'\n if (!an && vowel) return `${article === 'a' ? 'an' : 'An'}${space}${word}`\n if (an && consonant) return `${article === 'an' ? 'a' : 'A'}${space}${word}`\n return whole\n },\n )\n}\n\n/**\n * The line with every trailing dangling word taken off, as rule 14 reads one,\n * in the site's language (AGL-3828): a Spanish line cut to \"…para la\" loses\n * both words, as an English one cut to \"…for the\" does.\n */\nfunction withoutDanglingTail(line: string, language?: string): string {\n let text = line.replace(TRAILING_JOINERS, '').trim()\n for (let guard = 0; guard < 6; guard += 1) {\n const word = aiDanglingWord(text, language)\n if (!word) break\n text = text\n .slice(0, text.length - word.length)\n .replace(TRAILING_JOINERS, '')\n .trim()\n }\n return text\n}\n\n/**\n * A line held to the characters its place holds, ended where a reader expects\n * a line to end: at the last sentence's end that leaves at least half the\n * room, else the last clause boundary that does, else the last whole word. A\n * line that fits is returned clean, with any dangling tail taken off.\n */\nfunction fitText(value: unknown, place: AiLayoutTextPlace, language?: string): string {\n const text = aiLayoutArticles(aiLayoutCleanText(value), language)\n const limit = AI_LAYOUT_TEXT_LIMITS[place]\n if (text.length <= limit) return withoutDanglingTail(text, language)\n const room = text.slice(0, limit + 1)\n const half = Math.floor(limit * 0.4)\n const lastMatch = (pattern: RegExp): number => {\n let at = -1\n for (const match of room.matchAll(pattern)) {\n const end = (match.index ?? 0) + (pattern === SENTENCE_END ? 1 : 0)\n if (end <= limit && end >= half) at = end\n }\n return at\n }\n const sentence = lastMatch(SENTENCE_END)\n if (sentence > 0) return withoutDanglingTail(room.slice(0, sentence), language)\n const clause = lastMatch(CLAUSE_END)\n if (clause > 0) return withoutDanglingTail(room.slice(0, clause), language)\n const space = room.lastIndexOf(' ', limit)\n return withoutDanglingTail(\n space > half ? room.slice(0, space) : room.slice(0, limit),\n language,\n )\n}\n\n/** A phone number as people write one: seven or more digits with the usual separators. */\nconst PHONE = /\\(?\\+?\\d[\\d\\s().-]{6,}\\d/g\nconst EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g\n\n/** The digits of a phone number, which is what two spellings of one number share. */\nfunction digitsOf(value: string): string {\n return value.replace(/\\D/g, '')\n}\n\n/**\n * The line with every phone number and email address the brief did not give\n * written as the gap (rule 14): a site's contact details are facts, and a\n * model that writes one the brief lacks is inventing it. `facts` is every\n * word the job was given — the brief, the site's answers and its profile.\n */\nexport function aiLayoutGuardContacts(line: string, facts: string): string {\n const known = facts.toLowerCase()\n const knownDigits = new Set((facts.match(PHONE) ?? []).map(digitsOf))\n return line\n .replace(EMAIL, (email) =>\n known.includes(email.toLowerCase()) ? email : '[email address]',\n )\n .replace(PHONE, (phone) => {\n const digits = digitsOf(phone)\n // A year, a price or a count is not a phone number.\n if (digits.length < 7) return phone\n // One number with and without its country code is one number (AGL-3979):\n // \"+1 512 555 0142\" given admits \"(512) 555-0142\" written.\n const known = [...knownDigits].some(\n (given) => given === digits || (Math.min(given.length, digits.length) >= 7 && (given.endsWith(digits) || digits.endsWith(given)) && Math.abs(given.length - digits.length) <= 3),\n )\n return known ? phone : '[phone number]'\n })\n}\n\n/**\n * A line fitted to its place with invented contact details written as gaps;\n * `language` is the site's (see `aiLayoutFitText`).\n */\nexport function aiLayoutWords(\n value: unknown,\n place: AiLayoutTextPlace,\n facts: string,\n language?: string,\n): string {\n return aiLayoutFitText(\n aiLayoutGuardContacts(aiLayoutCleanText(value), facts),\n place,\n language,\n )\n}\n\n/**\n * A line held to the characters its place holds (see `fitText`), and none at\n * all where what is left is filler. `language` is the site's language code,\n * which says what a dangling word is (AGL-3828); absent, English.\n */\nexport function aiLayoutFitText(\n value: unknown,\n place: AiLayoutTextPlace,\n language?: string,\n): string {\n const text = fitText(value, place, language)\n return aiLayoutIsFiller(text) ? '' : text\n}\n\n/**\n * Whether a line is filler rule 14 refuses (\"lorem ipsum\", \"your text here\"):\n * such a line is no copy, and the compiler leaves it out rather than ship it.\n */\nexport function aiLayoutIsFiller(text: string): boolean {\n return detectOffVoiceCopy([{ at: 'line', text }]).length > 0\n}\n\n/** A quote that cannot be a real customer's, written as the gap the owner fills with one (rule 14). */\nexport function aiLayoutQuoteGap(value: unknown, language?: string): string {\n const text = aiLayoutFitText(value, 'quote', language)\n .replace(/^[\"“'‘]+|[\"”'’]+$/g, '')\n .trim()\n if (!text) return ''\n if (/^\\[.*\\]$/.test(text)) return text\n return `[${text.replace(/^\\[|\\]$/g, '')}]`\n}\n"],"names":["aiDanglingWord","detectOffVoiceCopy","aiLayoutSpeaksOtherThanEnglish","AI_LAYOUT_TEXT_LIMITS","title","heading","itemTitle","eyebrow","label","lede","text","itemText","listItem","stat","statLabel","question","answer","quote","note","alt","MARKUP","SENTENCE_END","CLAUSE_END","TRAILING_JOINERS","aiLayoutCleanText","value","replace","_","hex","String","fromCharCode","parseInt","trim","aiLayoutArticles","language","whole","article","space","word","at","test","slice","lower","toLowerCase","vowel","consonant","an","withoutDanglingTail","line","guard","length","fitText","place","limit","room","half","Math","floor","lastMatch","pattern","match","matchAll","end","index","sentence","clause","lastIndexOf","PHONE","EMAIL","digitsOf","aiLayoutGuardContacts","facts","known","knownDigits","Set","map","email","includes","phone","digits","some","given","min","endsWith","abs","aiLayoutWords","aiLayoutFitText","aiLayoutIsFiller","aiLayoutQuoteGap"],"mappings":"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"}
@@ -53,12 +53,31 @@ export interface AiLayoutDesign {
53
53
  }
54
54
  /**
55
55
  * How the first section of a page is drawn:
56
- * - `cover`: a full-bleed photo with the words over it on a scrim;
56
+ * - `cover`: a full-bleed photo with the words over it on a scrim, set at
57
+ * the start of the page;
58
+ * - `poster`: a full-bleed photo, taller, the words centered over it in
59
+ * display type (AGL-3985);
57
60
  * - `split`: the words beside a tall photo, inside the page's measure;
61
+ * - `bleed`: the band in two halves, a photo filling one edge to edge and
62
+ * the words in the other (AGL-3985);
58
63
  * - `editorial`: display type across the page, the lede and the buttons in
59
- * a row under it, and a wide photo below.
64
+ * a row under it, and a wide photo below;
65
+ * - `column`: a masthead — the words centered in a narrow column, a wide
66
+ * photo under them (AGL-3985);
67
+ * - `collage`: the words beside two photos, a tall one and a smaller one
68
+ * laid over its corner (AGL-3985);
69
+ * - `panel`: a full-bleed photo, undimmed, with the words on a card of the
70
+ * page's surface over its lower corner (AGL-3985);
71
+ * - `portrait`: a round photo over the words, all centered: a person's own
72
+ * page (AGL-3985).
60
73
  */
61
- export type AiLayoutHeroVariant = 'cover' | 'split' | 'editorial';
74
+ export type AiLayoutHeroVariant = 'cover' | 'poster' | 'split' | 'bleed' | 'editorial' | 'column' | 'collage' | 'panel' | 'portrait';
75
+ /** Every hero arrangement, as {@link AiLayoutHeroVariant} lists them. */
76
+ export declare const AI_LAYOUT_HERO_VARIANTS: readonly AiLayoutHeroVariant[];
77
+ /** The heroes whose words take half the page or less, so their title is the page's title size, never display. */
78
+ export declare const AI_LAYOUT_HALF_HEROES: ReadonlySet<AiLayoutHeroVariant>;
79
+ /** The heroes that lay their words over a photo, which pins the section to the site's dark scheme. */
80
+ export declare const AI_LAYOUT_OVER_PHOTO_HEROES: ReadonlySet<AiLayoutHeroVariant>;
62
81
  /**
63
82
  * How a group of cards is drawn where no component draws it:
64
83
  * - `cards`: the theme's cards (the compiler's first design);
@@ -99,15 +118,53 @@ export interface AiLayoutDesignChoices {
99
118
  writing: 'grid' | 'ruled';
100
119
  }
101
120
  type Family = 'gallery' | 'editorial' | 'hospitality' | 'calm' | 'retail' | 'standard';
121
+ type GroupStyle = AiLayoutDesignChoices['group'];
122
+ /**
123
+ * A kind's LAYOUT SIGNATURE (AGL-3985): the arrangements its pages are drawn
124
+ * in, so a café, a law firm and a band never read as one page in three
125
+ * colors. Before it, six kinds shared one list of heroes and every kind drew
126
+ * its groups, its photo-led sections and its headings beside their items at
127
+ * the same odds; the guided start's 21 style previews, which the real
128
+ * compiler draws, read as one page — a header bar, a split or banded hero,
129
+ * one section.
130
+ *
131
+ * - `heroes`: the two first sections the kind opens with, each drawn at even
132
+ * odds (AGL-3899). The first is the one its style card shows. No two kinds
133
+ * share the same two.
134
+ * - `groups`: the group styles a section of items takes where its subject
135
+ * picks none.
136
+ * - `steps`: how its numbered groups read.
137
+ * - `features`: the chance a section of words takes a photo beside it — a
138
+ * café's or a salon's pages are photo-led, a law firm's or a writer's let
139
+ * the type carry them.
140
+ * - `coverClose`: the chance a closing call to action is drawn over a photo.
141
+ * - `splitHeads`: the chance a heading sits beside its items: an
142
+ * editorial, considered page.
143
+ * - `pictures`: how its work is laid out where it shows it as pictures.
144
+ */
145
+ export interface AiLayoutKindSignature {
146
+ heroes: readonly [AiLayoutHeroVariant, AiLayoutHeroVariant];
147
+ groups: readonly GroupStyle[];
148
+ steps: readonly AiLayoutStepsVariant[];
149
+ features: number;
150
+ coverClose: number;
151
+ splitHeads: number;
152
+ pictures: readonly AiLayoutDesignChoices['pictures'][];
153
+ }
154
+ /** Each kind's signature, by its `AI_SITE_KINDS` id. */
155
+ export declare const AI_LAYOUT_KIND_SIGNATURES: Readonly<Record<string, AiLayoutKindSignature>>;
156
+ /** The signature of a kind; an unknown kind draws as a business site. */
157
+ export declare function aiLayoutKindSignature(kind: string): AiLayoutKindSignature;
102
158
  /** The family a kind's pages are drawn in. */
103
159
  export declare function aiLayoutFamily(kind: string): Family;
104
160
  /**
105
161
  * A site's choices, the same for every page of it, each drawn at even odds
106
- * among the options that suit the kind (AGL-3899), so two sites of one kind
107
- * are drawn apart. Favoring each list's first option made the newest
108
- * production sites one design: the kind's first hero won 55% before the
109
- * pick, a business site opened split, groups were ruled two times in three
110
- * and every text section took a photo.
162
+ * among the options its kind's signature lists (AGL-3899, AGL-3985), so two
163
+ * sites of one kind are drawn apart and two kinds never read as one page.
164
+ * Favoring each list's first option made the newest production sites one
165
+ * design: the kind's first hero won 55% before the pick, a business site
166
+ * opened split, groups were ruled two times in three and every text section
167
+ * took a photo.
111
168
  */
112
169
  export declare function aiLayoutDesignChoices(design: AiLayoutDesign): AiLayoutDesignChoices;
113
170
  /**