@aglyn/plugins-ai 1.0.0-beta.261 → 1.0.0-beta.263

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 (90) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-brief-dialog.component.d.ts +21 -58
  3. package/src/lib/components/ai-brief-dialog.component.js +8 -132
  4. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  5. package/src/lib/components/ai-job-follow.component.js +2 -35
  6. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  7. package/src/lib/components/ai-job-plan.component.d.ts +2 -8
  8. package/src/lib/components/ai-job-plan.component.js +13 -74
  9. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  10. package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
  11. package/src/lib/components/ai-template-collection-field.component.js +13 -10
  12. package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
  13. package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
  14. package/src/lib/components/ai-upsell-dialog.component.js +9 -66
  15. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  16. package/src/lib/components/use-ai-job-run.d.ts +3 -8
  17. package/src/lib/components/use-ai-job-run.js +4 -15
  18. package/src/lib/components/use-ai-job-run.js.map +1 -1
  19. package/src/lib/insights/ai-figure-readers.js +15 -5
  20. package/src/lib/insights/ai-figure-readers.js.map +1 -1
  21. package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
  22. package/src/lib/insights/ai-figure-summaries.js +96 -0
  23. package/src/lib/insights/ai-figure-summaries.js.map +1 -0
  24. package/src/lib/insights/ai-insight-digest.d.ts +2 -0
  25. package/src/lib/insights/ai-insight-digest.js +7 -4
  26. package/src/lib/insights/ai-insight-digest.js.map +1 -1
  27. package/src/lib/insights/ai-insight-readers.d.ts +2 -1
  28. package/src/lib/insights/ai-insight-readers.js +11 -26
  29. package/src/lib/insights/ai-insight-readers.js.map +1 -1
  30. package/src/lib/jobs/ai-job-insight-step.d.ts +1 -1
  31. package/src/lib/jobs/ai-job-insight-step.js +5 -3
  32. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
  34. package/src/lib/jobs/ai-job-page-language.js +23 -2
  35. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  36. package/src/lib/jobs/ai-job-plan-step.d.ts +14 -1
  37. package/src/lib/jobs/ai-job-plan-step.js +44 -9
  38. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  39. package/src/lib/jobs/ai-jobs-notify.d.ts +8 -1
  40. package/src/lib/jobs/ai-jobs-notify.js +44 -2
  41. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  42. package/src/lib/jobs/ai-layout-stock-photos.js +11 -1
  43. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  44. package/src/lib/layout-language/ai-layout-compiler.js +18 -7
  45. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  46. package/src/lib/layout-language/ai-layout-promised-copy.d.ts +54 -0
  47. package/src/lib/layout-language/ai-layout-promised-copy.js +104 -0
  48. package/src/lib/layout-language/ai-layout-promised-copy.js.map +1 -0
  49. package/src/lib/model/ai-brief-copy.d.ts +85 -0
  50. package/src/lib/model/ai-brief-copy.js +152 -0
  51. package/src/lib/model/ai-brief-copy.js.map +1 -0
  52. package/src/lib/model/ai-create-view.d.ts +200 -0
  53. package/src/lib/model/ai-create-view.js +263 -0
  54. package/src/lib/model/ai-create-view.js.map +1 -0
  55. package/src/lib/model/ai-insight.d.ts +10 -1
  56. package/src/lib/model/ai-insight.js +16 -39
  57. package/src/lib/model/ai-insight.js.map +1 -1
  58. package/src/lib/model/ai-job-activity.d.ts +24 -0
  59. package/src/lib/model/ai-job-activity.js +52 -1
  60. package/src/lib/model/ai-job-activity.js.map +1 -1
  61. package/src/lib/model/ai-job-notice.d.ts +7 -1
  62. package/src/lib/model/ai-job-notice.js +10 -9
  63. package/src/lib/model/ai-job-notice.js.map +1 -1
  64. package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
  65. package/src/lib/model/ai-job-plan-lines.js +70 -0
  66. package/src/lib/model/ai-job-plan-lines.js.map +1 -0
  67. package/src/lib/model/ai-plan-said-sections.d.ts +81 -0
  68. package/src/lib/model/ai-plan-said-sections.js +168 -0
  69. package/src/lib/model/ai-plan-said-sections.js.map +1 -0
  70. package/src/lib/model/ai-site-job.d.ts +8 -2
  71. package/src/lib/model/ai-site-job.js +7 -1
  72. package/src/lib/model/ai-site-job.js.map +1 -1
  73. package/src/lib/model/ai-template-collections.d.ts +28 -0
  74. package/src/lib/model/ai-template-collections.js +28 -0
  75. package/src/lib/model/ai-template-collections.js.map +1 -0
  76. package/src/lib/model/ai-upsell-copy.d.ts +41 -0
  77. package/src/lib/model/ai-upsell-copy.js +85 -0
  78. package/src/lib/model/ai-upsell-copy.js.map +1 -0
  79. package/src/lib/runtime/ai-doctrine-validators.d.ts +15 -1
  80. package/src/lib/runtime/ai-doctrine-validators.js +96 -3
  81. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  82. package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
  83. package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
  84. package/src/lib/runtime/ai-palette.generated.js +11 -3
  85. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  86. package/src/lib/server/ai-create-route.d.ts +32 -0
  87. package/src/lib/server/ai-create-route.js +295 -0
  88. package/src/lib/server/ai-create-route.js.map +1 -0
  89. package/src/lib/server.js +14 -0
  90. package/src/lib/server.js.map +1 -1
@@ -0,0 +1,168 @@
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
+ * A plan's sections of what customers say (AGL-3887).
18
+ *
19
+ * A job never writes a review, a testimonial or a quote for people who did
20
+ * not say it (rule 14, `AI_SITE_CONTEXT_RULE`). So a section planned to show
21
+ * them, on a site that has none, has nothing true to hold, and the build
22
+ * fills it with the only honest thing left: a promise of words to come. The
23
+ * live Whitmore Lane Family Law start (beta.262, 2026-10-16) planned "Client
24
+ * feedback placeholder" on its home page and shipped "We are building this
25
+ * space for the words of families we have worked with" over a stock photo.
26
+ *
27
+ * A site start's plan is settled in code before it is kept, never re-asked
28
+ * (a re-ask costs the person credits for a plan that has one answer): such a
29
+ * section becomes a band about what working together is like, as a
30
+ * photographer's start of the same day planned its "Kind words" as "What
31
+ * working together is like" (Relaxed / Prepared / Personal). Where the page
32
+ * already has that band, the section goes, if the page keeps its fewest
33
+ * sections without it.
34
+ *
35
+ * Kept as planned where the customer words are real: a store, whose
36
+ * platform shows its shoppers' reviews; a site with a dataset of them; a
37
+ * brief that asks for them or quotes them.
38
+ *
39
+ * Every plan, whatever its kind, names no section "placeholder": a section's
40
+ * name is what its build is asked to write, and a name that says it is a
41
+ * stand-in is written as one.
42
+ *
43
+ * Pure: plans in, plans out.
44
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
45
+ /** Who a section of customer words quotes. */ const SAYERS = '(?:clients?|customers?|patients?|guests?|famil(?:y|ies)|parents?|students?|readers?|members?|couples?|homeowners?|buyers?|shoppers?|diners?|people|others)';
46
+ /**
47
+ * The words a section of what customers say is named by. "Review" alone is
48
+ * not one of them: a law firm's "Free case review" or an accountant's "Plan
49
+ * review" is a service, not a quote.
50
+ */ const SAID_SECTION = new RegExp([
51
+ '\\btestimonials?\\b',
52
+ '\\bkind words\\b',
53
+ '\\bsocial proof\\b',
54
+ '\\bcustomer love\\b',
55
+ '\\bendorsements?\\b',
56
+ '\\bin their (?:own )?words\\b',
57
+ '\\bword of mouth\\b',
58
+ '\\bsuccess stories\\b',
59
+ `\\b${SAYERS}['’]?s?['’]? (?:feedback|reviews?|stories|voices|words|praise|say(?:s|ing)?)\\b`,
60
+ `\\bwhat (?:our |my |real )?(?:\\w+ )?${SAYERS} (?:say|are saying|think|tell us)\\b`,
61
+ '\\b(?:star|5-star|five-star|google|yelp|online|verified) (?:reviews?|ratings?)\\b',
62
+ '\\bratings? (?:and|&) reviews?\\b',
63
+ '\\breviews? (?:and|&) (?:ratings?|testimonials?)\\b',
64
+ '^\\s*(?:reviews|feedback)\\s*$'
65
+ ].join('|'), 'i');
66
+ /** A dataset's name that says it holds what customers said. */ const SAID_RECORDS = /\b(?:reviews|testimonials?|kind words|endorsements?)\b/i;
67
+ /** A quotation with its speaker: “…” — Name, the way a brief gives a real testimonial. */ const ATTRIBUTED_QUOTE = RegExp('[“"][^”"]{12,}[”"]\\s*[—–-]+\\s*[\\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}]', "u");
68
+ /** A section already about working together: its approach, its process, what to expect. */ const APPROACH = /\b(?:what (?:working|it(?:['’]s| is) like)|working (?:together|with (?:us|me|our team))|how (?:we|i|it) work|our (?:approach|process)|the process|approach|process|what to expect)\b/i;
69
+ /** The word a stand-in is named by, with any brackets, parentheses or dash it is set off by. */ const PLACEHOLDER_WORD = /\s*[[(]?\s*[-–—:]?\s*\bplace ?holders?\b\s*[\])]?/gi;
70
+ /** What a section of customer words on a site that has none is planned as instead. */ export const AI_PLAN_SAID_SECTION_NAME = 'What working with us is like';
71
+ /** The points that band makes (the photographer's three: Relaxed, Prepared, Personal). */ export const AI_PLAN_SAID_SECTION_ITEMS = 3;
72
+ /** The fewest sections any page other than the home keeps after a section goes. */ const PAGE_MIN_SECTIONS = 2;
73
+ /** Whether a planned section's name says it shows what customers say. */ export function aiPlanSectionSaysCustomerWords(name) {
74
+ return SAID_SECTION.test(aiPlanSectionNameWithoutPlaceholder(name));
75
+ }
76
+ /** A section's name with the word "placeholder" taken out; the name unchanged where nothing is left. */ export function aiPlanSectionNameWithoutPlaceholder(name) {
77
+ if (!/\bplace ?holders?\b/i.test(name)) return name;
78
+ const cleaned = name.replace(PLACEHOLDER_WORD, ' ').replace(/\s{2,}/g, ' ').replace(/^[\s\-–—:,]+|[\s\-–—:,]+$/g, '').trim();
79
+ return cleaned || 'Section';
80
+ }
81
+ /**
82
+ * Whether the site, or the person asking, has customer words that are real
83
+ * (AGL-3887): a store's shoppers' reviews, which its platform shows; a
84
+ * dataset of them the site keeps; a brief that names them or quotes one with
85
+ * its speaker.
86
+ */ export function aiPlanHasCustomerWords(input) {
87
+ var _ref, _input_brief;
88
+ var _input_inventory;
89
+ if (input.siteKind === 'store') return true;
90
+ if (((_ref = (_input_inventory = input.inventory) == null ? void 0 : _input_inventory.datasets) != null ? _ref : []).some((dataset)=>SAID_RECORDS.test(dataset.name))) return true;
91
+ const brief = (_input_brief = input.brief) != null ? _input_brief : '';
92
+ return SAID_SECTION.test(brief) || ATTRIBUTED_QUOTE.test(brief);
93
+ }
94
+ /** Every section's name with the word "placeholder" taken out (AGL-3887), the plan unchanged where none had it. */ export function aiSettlePlanPlaceholderNames(plan) {
95
+ let changed = false;
96
+ const screens = plan.screens.map((screen)=>{
97
+ let touched = false;
98
+ const sections = screen.sections.map((section)=>{
99
+ const name = aiPlanSectionNameWithoutPlaceholder(section.name);
100
+ if (name === section.name) return section;
101
+ touched = true;
102
+ return _extends({}, section, {
103
+ name
104
+ });
105
+ });
106
+ if (!touched) return screen;
107
+ changed = true;
108
+ return _extends({}, screen, {
109
+ sections
110
+ });
111
+ });
112
+ return changed ? _extends({}, plan, {
113
+ screens
114
+ }) : plan;
115
+ }
116
+ /**
117
+ * A site start's plan with no section of customer words it cannot fill
118
+ * (AGL-3887). Where `hasCustomerWords` is false, each such section past a
119
+ * page's first is planned as {@link AI_PLAN_SAID_SECTION_NAME} with its
120
+ * points; where the page already has that band, it is taken out instead
121
+ * while the page keeps its fewest sections (`homeMin` on the home page, two
122
+ * on any other), and planned as the band all the same where it cannot be.
123
+ * The plan is returned unchanged where nothing moved.
124
+ */ export function aiSettlePlanSaidSections(plan, options) {
125
+ if (options.hasCustomerWords) return plan;
126
+ let changed = false;
127
+ const screens = plan.screens.map((screen)=>{
128
+ const next = settleScreen(screen, options.homeMin);
129
+ if (next !== screen) changed = true;
130
+ return next;
131
+ });
132
+ return changed ? _extends({}, plan, {
133
+ screens
134
+ }) : plan;
135
+ }
136
+ function isHome(screen) {
137
+ return screen.slug.trim().replace(/^\/+|\/+$/g, '') === '';
138
+ }
139
+ function settleScreen(screen, homeMin) {
140
+ const said = screen.sections.flatMap((section, index)=>index > 0 && aiPlanSectionSaysCustomerWords(section.name) ? [
141
+ index
142
+ ] : []);
143
+ if (!said.length) return screen;
144
+ const floor = isHome(screen) ? Math.max(homeMin, PAGE_MIN_SECTIONS) : PAGE_MIN_SECTIONS;
145
+ let approach = screen.sections.some((section, index)=>!said.includes(index) && APPROACH.test(section.name));
146
+ let count = screen.sections.length;
147
+ const sections = [];
148
+ screen.sections.forEach((section, index)=>{
149
+ if (!said.includes(index)) {
150
+ sections.push(section);
151
+ return;
152
+ }
153
+ if (approach && count - 1 >= floor) {
154
+ count -= 1;
155
+ return;
156
+ }
157
+ approach = true;
158
+ sections.push(_extends({}, section, {
159
+ name: AI_PLAN_SAID_SECTION_NAME,
160
+ items: Math.max(section.items, AI_PLAN_SAID_SECTION_ITEMS)
161
+ }));
162
+ });
163
+ return _extends({}, screen, {
164
+ sections
165
+ });
166
+ }
167
+
168
+ //# sourceMappingURL=ai-plan-said-sections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-said-sections.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A plan's sections of what customers say (AGL-3887).\n *\n * A job never writes a review, a testimonial or a quote for people who did\n * not say it (rule 14, `AI_SITE_CONTEXT_RULE`). So a section planned to show\n * them, on a site that has none, has nothing true to hold, and the build\n * fills it with the only honest thing left: a promise of words to come. The\n * live Whitmore Lane Family Law start (beta.262, 2026-10-16) planned \"Client\n * feedback placeholder\" on its home page and shipped \"We are building this\n * space for the words of families we have worked with\" over a stock photo.\n *\n * A site start's plan is settled in code before it is kept, never re-asked\n * (a re-ask costs the person credits for a plan that has one answer): such a\n * section becomes a band about what working together is like, as a\n * photographer's start of the same day planned its \"Kind words\" as \"What\n * working together is like\" (Relaxed / Prepared / Personal). Where the page\n * already has that band, the section goes, if the page keeps its fewest\n * sections without it.\n *\n * Kept as planned where the customer words are real: a store, whose\n * platform shows its shoppers' reviews; a site with a dataset of them; a\n * brief that asks for them or quotes them.\n *\n * Every plan, whatever its kind, names no section \"placeholder\": a section's\n * name is what its build is asked to write, and a name that says it is a\n * stand-in is written as one.\n *\n * Pure: plans in, plans out.\n */\n\nimport type { AiBuildPlan, AiBuildPlanScreen, AiBuildPlanSection } from './ai-build-plan'\nimport type { AiSiteInventory } from './ai-site-inventory'\n\n/** Who a section of customer words quotes. */\nconst SAYERS =\n '(?:clients?|customers?|patients?|guests?|famil(?:y|ies)|parents?|students?|readers?|members?|couples?|homeowners?|buyers?|shoppers?|diners?|people|others)'\n\n/**\n * The words a section of what customers say is named by. \"Review\" alone is\n * not one of them: a law firm's \"Free case review\" or an accountant's \"Plan\n * review\" is a service, not a quote.\n */\nconst SAID_SECTION = new RegExp(\n [\n '\\\\btestimonials?\\\\b',\n '\\\\bkind words\\\\b',\n '\\\\bsocial proof\\\\b',\n '\\\\bcustomer love\\\\b',\n '\\\\bendorsements?\\\\b',\n '\\\\bin their (?:own )?words\\\\b',\n '\\\\bword of mouth\\\\b',\n '\\\\bsuccess stories\\\\b',\n `\\\\b${SAYERS}['’]?s?['’]? (?:feedback|reviews?|stories|voices|words|praise|say(?:s|ing)?)\\\\b`,\n `\\\\bwhat (?:our |my |real )?(?:\\\\w+ )?${SAYERS} (?:say|are saying|think|tell us)\\\\b`,\n '\\\\b(?:star|5-star|five-star|google|yelp|online|verified) (?:reviews?|ratings?)\\\\b',\n '\\\\bratings? (?:and|&) reviews?\\\\b',\n '\\\\breviews? (?:and|&) (?:ratings?|testimonials?)\\\\b',\n '^\\\\s*(?:reviews|feedback)\\\\s*$',\n ].join('|'),\n 'i',\n)\n\n/** A dataset's name that says it holds what customers said. */\nconst SAID_RECORDS = /\\b(?:reviews|testimonials?|kind words|endorsements?)\\b/i\n\n/** A quotation with its speaker: “…” — Name, the way a brief gives a real testimonial. */\nconst ATTRIBUTED_QUOTE = /[“\"][^”\"]{12,}[”\"]\\s*[—–-]+\\s*\\p{L}/u\n\n/** A section already about working together: its approach, its process, what to expect. */\nconst APPROACH =\n /\\b(?:what (?:working|it(?:['’]s| is) like)|working (?:together|with (?:us|me|our team))|how (?:we|i|it) work|our (?:approach|process)|the process|approach|process|what to expect)\\b/i\n\n/** The word a stand-in is named by, with any brackets, parentheses or dash it is set off by. */\nconst PLACEHOLDER_WORD = /\\s*[[(]?\\s*[-–—:]?\\s*\\bplace ?holders?\\b\\s*[\\])]?/gi\n\n/** What a section of customer words on a site that has none is planned as instead. */\nexport const AI_PLAN_SAID_SECTION_NAME = 'What working with us is like'\n\n/** The points that band makes (the photographer's three: Relaxed, Prepared, Personal). */\nexport const AI_PLAN_SAID_SECTION_ITEMS = 3\n\n/** The fewest sections any page other than the home keeps after a section goes. */\nconst PAGE_MIN_SECTIONS = 2\n\n/** Whether a planned section's name says it shows what customers say. */\nexport function aiPlanSectionSaysCustomerWords(name: string): boolean {\n return SAID_SECTION.test(aiPlanSectionNameWithoutPlaceholder(name))\n}\n\n/** A section's name with the word \"placeholder\" taken out; the name unchanged where nothing is left. */\nexport function aiPlanSectionNameWithoutPlaceholder(name: string): string {\n if (!/\\bplace ?holders?\\b/i.test(name)) return name\n const cleaned = name\n .replace(PLACEHOLDER_WORD, ' ')\n .replace(/\\s{2,}/g, ' ')\n .replace(/^[\\s\\-–—:,]+|[\\s\\-–—:,]+$/g, '')\n .trim()\n return cleaned || 'Section'\n}\n\n/**\n * Whether the site, or the person asking, has customer words that are real\n * (AGL-3887): a store's shoppers' reviews, which its platform shows; a\n * dataset of them the site keeps; a brief that names them or quotes one with\n * its speaker.\n */\nexport function aiPlanHasCustomerWords(input: {\n brief?: string | null\n inventory?: Pick<AiSiteInventory, 'datasets'> | null\n siteKind?: string | null\n}): boolean {\n if (input.siteKind === 'store') return true\n if ((input.inventory?.datasets ?? []).some((dataset) => SAID_RECORDS.test(dataset.name))) return true\n const brief = input.brief ?? ''\n return SAID_SECTION.test(brief) || ATTRIBUTED_QUOTE.test(brief)\n}\n\n/** Every section's name with the word \"placeholder\" taken out (AGL-3887), the plan unchanged where none had it. */\nexport function aiSettlePlanPlaceholderNames<P extends Pick<AiBuildPlan, 'screens'>>(plan: P): P {\n let changed = false\n const screens = plan.screens.map((screen) => {\n let touched = false\n const sections = screen.sections.map((section) => {\n const name = aiPlanSectionNameWithoutPlaceholder(section.name)\n if (name === section.name) return section\n touched = true\n return { ...section, name }\n })\n if (!touched) return screen\n changed = true\n return { ...screen, sections }\n })\n return changed ? { ...plan, screens } : plan\n}\n\n/**\n * A site start's plan with no section of customer words it cannot fill\n * (AGL-3887). Where `hasCustomerWords` is false, each such section past a\n * page's first is planned as {@link AI_PLAN_SAID_SECTION_NAME} with its\n * points; where the page already has that band, it is taken out instead\n * while the page keeps its fewest sections (`homeMin` on the home page, two\n * on any other), and planned as the band all the same where it cannot be.\n * The plan is returned unchanged where nothing moved.\n */\nexport function aiSettlePlanSaidSections<P extends Pick<AiBuildPlan, 'screens'>>(\n plan: P,\n options: { hasCustomerWords: boolean; homeMin: number },\n): P {\n if (options.hasCustomerWords) return plan\n let changed = false\n const screens = plan.screens.map((screen) => {\n const next = settleScreen(screen, options.homeMin)\n if (next !== screen) changed = true\n return next\n })\n return changed ? { ...plan, screens } : plan\n}\n\nfunction isHome(screen: Pick<AiBuildPlanScreen, 'slug'>): boolean {\n return screen.slug.trim().replace(/^\\/+|\\/+$/g, '') === ''\n}\n\nfunction settleScreen<S extends Pick<AiBuildPlanScreen, 'slug' | 'sections'>>(screen: S, homeMin: number): S {\n const said = screen.sections.flatMap((section, index) =>\n index > 0 && aiPlanSectionSaysCustomerWords(section.name) ? [index] : [],\n )\n if (!said.length) return screen\n const floor = isHome(screen) ? Math.max(homeMin, PAGE_MIN_SECTIONS) : PAGE_MIN_SECTIONS\n let approach = screen.sections.some((section, index) => !said.includes(index) && APPROACH.test(section.name))\n let count = screen.sections.length\n const sections: AiBuildPlanSection[] = []\n screen.sections.forEach((section, index) => {\n if (!said.includes(index)) {\n sections.push(section)\n return\n }\n if (approach && count - 1 >= floor) {\n count -= 1\n return\n }\n approach = true\n sections.push({\n ...section,\n name: AI_PLAN_SAID_SECTION_NAME,\n items: Math.max(section.items, AI_PLAN_SAID_SECTION_ITEMS),\n })\n })\n return { ...screen, sections }\n}\n"],"names":["SAYERS","SAID_SECTION","RegExp","join","SAID_RECORDS","ATTRIBUTED_QUOTE","APPROACH","PLACEHOLDER_WORD","AI_PLAN_SAID_SECTION_NAME","AI_PLAN_SAID_SECTION_ITEMS","PAGE_MIN_SECTIONS","aiPlanSectionSaysCustomerWords","name","test","aiPlanSectionNameWithoutPlaceholder","cleaned","replace","trim","aiPlanHasCustomerWords","input","siteKind","inventory","datasets","some","dataset","brief","aiSettlePlanPlaceholderNames","plan","changed","screens","map","screen","touched","sections","section","aiSettlePlanSaidSections","options","hasCustomerWords","next","settleScreen","homeMin","isHome","slug","said","flatMap","index","length","floor","Math","max","approach","includes","count","forEach","push","items"],"mappings":"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"}
@@ -334,8 +334,14 @@ export declare function aiSiteEmptyGalleryViolations(plan: Pick<AiBuildPlan, 'sc
334
334
  }>;
335
335
  /** The violation a site plan whose home page is under its fewest sections is re-asked under. */
336
336
  export declare const AI_SITE_THIN_HOME_CODE = "plan-thin-home";
337
- /** What a site plan's home is composed of, in the words the plan's turn and its re-ask both use. */
338
- export declare const AI_SITE_HOME_BANDS = "a hero first, then bands such as services or the offer, about or why us, and testimonials or other social proof, and a closing call to action or contact band last";
337
+ /**
338
+ * What a site plan's home is composed of, in the words the plan's turn and its
339
+ * re-ask both use. Its trust band is what working together is like, never
340
+ * testimonials (AGL-3887): a new site has no customer words to show, and a
341
+ * home asked for them planned a "Client feedback placeholder" the build could
342
+ * fill only with a promise of words to come.
343
+ */
344
+ export declare const AI_SITE_HOME_BANDS = "a hero first, then bands such as services or the offer, about or why us, and what working together is like, and a closing call to action or contact band last";
339
345
  /**
340
346
  * The fewest sections this site start's home is held to (AGL-3660): five, or
341
347
  * 0 — no minimum — where the Free wall cannot pay for a full home beside the
@@ -339,7 +339,13 @@ export const AI_SITE_GALLERY_MAX_ITEMS = 6;
339
339
  ];
340
340
  }
341
341
  /** The violation a site plan whose home page is under its fewest sections is re-asked under. */ export const AI_SITE_THIN_HOME_CODE = 'plan-thin-home';
342
- /** What a site plan's home is composed of, in the words the plan's turn and its re-ask both use. */ export const AI_SITE_HOME_BANDS = 'a hero first, then bands such as services or the offer, about or why us, and testimonials or other social proof, and a closing call to action or contact band last';
342
+ /**
343
+ * What a site plan's home is composed of, in the words the plan's turn and its
344
+ * re-ask both use. Its trust band is what working together is like, never
345
+ * testimonials (AGL-3887): a new site has no customer words to show, and a
346
+ * home asked for them planned a "Client feedback placeholder" the build could
347
+ * fill only with a promise of words to come.
348
+ */ export const AI_SITE_HOME_BANDS = 'a hero first, then bands such as services or the offer, about or why us, and what working together is like, and a closing call to action or contact band last';
343
349
  /**
344
350
  * The fewest sections this site start's home is held to (AGL-3660): five, or
345
351
  * 0 — no minimum — where the Free wall cannot pay for a full home beside the