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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +8 -4
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-job-follow.component.js +1 -0
  5. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  6. package/src/lib/components/ai-job-links.js +13 -0
  7. package/src/lib/components/ai-job-links.js.map +1 -1
  8. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  9. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  10. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  11. package/src/lib/components/ai-media-create.component.js +107 -55
  12. package/src/lib/components/ai-media-create.component.js.map +1 -1
  13. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  14. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  15. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  16. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  17. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  18. package/src/lib/components/ai-site-start-card.component.js +80 -77
  19. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  20. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  21. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  22. package/src/lib/components/ai-usage-strip.component.js +1 -0
  23. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  24. package/src/lib/components/assist-panel.component.d.ts +10 -0
  25. package/src/lib/components/assist-panel.component.js +119 -9
  26. package/src/lib/components/assist-panel.component.js.map +1 -1
  27. package/src/lib/components/assist-signals-page.component.js +2 -0
  28. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  29. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  30. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  31. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  32. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  33. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  34. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  35. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  36. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  37. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  38. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  39. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  40. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  41. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  42. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  43. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  44. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  45. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  46. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  47. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  48. package/src/lib/jobs/ai-job-build-step.js +3 -1
  49. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  50. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  51. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  52. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  53. package/src/lib/jobs/ai-job-insight-step.js +21 -4
  54. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  55. package/src/lib/jobs/ai-job-layout-language.d.ts +8 -3
  56. package/src/lib/jobs/ai-job-layout-language.js +8 -2
  57. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  58. package/src/lib/jobs/ai-job-layout-step.js +7 -6
  59. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  60. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  61. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  62. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  63. package/src/lib/jobs/ai-job-page-language.d.ts +52 -7
  64. package/src/lib/jobs/ai-job-page-language.js +72 -9
  65. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  66. package/src/lib/jobs/ai-job-page-step.js +12 -2
  67. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  68. package/src/lib/jobs/ai-job-plan-step.d.ts +19 -5
  69. package/src/lib/jobs/ai-job-plan-step.js +54 -20
  70. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  71. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  72. package/src/lib/jobs/ai-job-site-content.js +443 -0
  73. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  74. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  75. package/src/lib/jobs/ai-job-site-look.js +322 -0
  76. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  77. package/src/lib/jobs/ai-job-site-step.d.ts +26 -5
  78. package/src/lib/jobs/ai-job-site-step.js +163 -24
  79. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  80. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  81. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  82. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  83. package/src/lib/layout-language/ai-layout-compiler.d.ts +9 -0
  84. package/src/lib/layout-language/ai-layout-compiler.js +120 -33
  85. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  86. package/src/lib/layout-language/ai-layout-frame.js +5 -4
  87. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  88. package/src/lib/layout-language/ai-layout-language.d.ts +12 -5
  89. package/src/lib/layout-language/ai-layout-language.js +92 -9
  90. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  91. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  92. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  93. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  94. package/src/lib/layout-language/ai-layout-store.d.ts +2 -0
  95. package/src/lib/layout-language/ai-layout-store.js +7 -1
  96. package/src/lib/layout-language/ai-layout-store.js.map +1 -1
  97. package/src/lib/model/ai-build-progress.js +4 -1
  98. package/src/lib/model/ai-build-progress.js.map +1 -1
  99. package/src/lib/model/ai-edit-job.d.ts +109 -0
  100. package/src/lib/model/ai-edit-job.js +157 -0
  101. package/src/lib/model/ai-edit-job.js.map +1 -0
  102. package/src/lib/model/ai-job-activity.js +1 -0
  103. package/src/lib/model/ai-job-activity.js.map +1 -1
  104. package/src/lib/model/ai-jobs.types.d.ts +3 -1
  105. package/src/lib/model/ai-jobs.types.js.map +1 -1
  106. package/src/lib/model/ai-logic-job.d.ts +18 -1
  107. package/src/lib/model/ai-logic-job.js +24 -3
  108. package/src/lib/model/ai-logic-job.js.map +1 -1
  109. package/src/lib/model/ai-media-credits.d.ts +8 -4
  110. package/src/lib/model/ai-media-credits.js +11 -4
  111. package/src/lib/model/ai-media-credits.js.map +1 -1
  112. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  113. package/src/lib/model/ai-media-kinds.js +98 -0
  114. package/src/lib/model/ai-media-kinds.js.map +1 -0
  115. package/src/lib/model/ai-site-build-progress.d.ts +4 -0
  116. package/src/lib/model/ai-site-build-progress.js +28 -11
  117. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  118. package/src/lib/model/ai-site-context.d.ts +111 -0
  119. package/src/lib/model/ai-site-context.js +222 -0
  120. package/src/lib/model/ai-site-context.js.map +1 -0
  121. package/src/lib/model/ai-site-job.d.ts +10 -2
  122. package/src/lib/model/ai-site-job.js +12 -5
  123. package/src/lib/model/ai-site-job.js.map +1 -1
  124. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  125. package/src/lib/model/ai-site-kinds.js +1942 -0
  126. package/src/lib/model/ai-site-kinds.js.map +1 -0
  127. package/src/lib/model/ai-site-look.d.ts +150 -0
  128. package/src/lib/model/ai-site-look.js +1236 -0
  129. package/src/lib/model/ai-site-look.js.map +1 -0
  130. package/src/lib/model/ai-site-memory.d.ts +80 -0
  131. package/src/lib/model/ai-site-memory.js +171 -0
  132. package/src/lib/model/ai-site-memory.js.map +1 -0
  133. package/src/lib/model/ai-site-start.d.ts +10 -1
  134. package/src/lib/model/ai-site-start.js +16 -2
  135. package/src/lib/model/ai-site-start.js.map +1 -1
  136. package/src/lib/model/assist-follow-up.d.ts +92 -0
  137. package/src/lib/model/assist-follow-up.js +128 -0
  138. package/src/lib/model/assist-follow-up.js.map +1 -0
  139. package/src/lib/plugin-config.js +2 -0
  140. package/src/lib/plugin-config.js.map +1 -1
  141. package/src/lib/plugin.js +20 -1
  142. package/src/lib/plugin.js.map +1 -1
  143. package/src/lib/providers/anthropic.d.ts +9 -1
  144. package/src/lib/providers/anthropic.js +143 -9
  145. package/src/lib/providers/anthropic.js.map +1 -1
  146. package/src/lib/providers/catalog.d.ts +37 -7
  147. package/src/lib/providers/catalog.js +103 -11
  148. package/src/lib/providers/catalog.js.map +1 -1
  149. package/src/lib/providers/contract.d.ts +29 -0
  150. package/src/lib/providers/contract.js.map +1 -1
  151. package/src/lib/providers/image-contract.d.ts +22 -1
  152. package/src/lib/providers/image-contract.js +40 -1
  153. package/src/lib/providers/image-contract.js.map +1 -1
  154. package/src/lib/providers/image-endpoints.d.ts +24 -0
  155. package/src/lib/providers/image-endpoints.js +28 -0
  156. package/src/lib/providers/image-endpoints.js.map +1 -0
  157. package/src/lib/providers/model-choice.js +23 -2
  158. package/src/lib/providers/model-choice.js.map +1 -1
  159. package/src/lib/providers/routing.js +18 -0
  160. package/src/lib/providers/routing.js.map +1 -1
  161. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  162. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  163. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  164. package/src/lib/providers/vertex-image.d.ts +13 -27
  165. package/src/lib/providers/vertex-image.js +15 -42
  166. package/src/lib/providers/vertex-image.js.map +1 -1
  167. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  168. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  169. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  170. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  171. package/src/lib/runtime/ai-dev-env.js +72 -0
  172. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  173. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  174. package/src/lib/runtime/ai-dev-replay.js +313 -0
  175. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  176. package/src/lib/runtime/ai-doctrine.js +5 -1
  177. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  178. package/src/lib/runtime/ai-eval-live.js +4 -0
  179. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  180. package/src/lib/runtime/ai-eval.d.ts +13 -1
  181. package/src/lib/runtime/ai-eval.js +84 -1
  182. package/src/lib/runtime/ai-eval.js.map +1 -1
  183. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  184. package/src/lib/runtime/ai-live-batch.js +89 -0
  185. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  186. package/src/lib/runtime/ai-runtime.js +46 -2
  187. package/src/lib/runtime/ai-runtime.js.map +1 -1
  188. package/src/lib/runtime/site-context.d.ts +58 -0
  189. package/src/lib/runtime/site-context.js +127 -0
  190. package/src/lib/runtime/site-context.js.map +1 -0
  191. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  192. package/src/lib/server/ai-admin-conversations.js +197 -0
  193. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  194. package/src/lib/server/ai-media-image.d.ts +5 -5
  195. package/src/lib/server/ai-media-image.js +25 -11
  196. package/src/lib/server/ai-media-image.js.map +1 -1
  197. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  198. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  199. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  200. package/src/lib/server/assist-chat.d.ts +11 -0
  201. package/src/lib/server/assist-chat.js +57 -18
  202. package/src/lib/server/assist-chat.js.map +1 -1
  203. package/src/lib/server/assist-edit-applied.js +16 -3
  204. package/src/lib/server/assist-edit-applied.js.map +1 -1
  205. package/src/lib/server/assist-view-context.d.ts +19 -1
  206. package/src/lib/server/assist-view-context.js +38 -1
  207. package/src/lib/server/assist-view-context.js.map +1 -1
  208. package/src/lib/server.js +10 -0
  209. package/src/lib/server.js.map +1 -1
  210. package/src/lib/subprocessors.js +17 -1
  211. package/src/lib/subprocessors.js.map +1 -1
  212. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  213. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  214. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  215. package/src/lib/usage/assist-usage.d.ts +3 -0
  216. package/src/lib/usage/assist-usage.js +4 -2
  217. package/src/lib/usage/assist-usage.js.map +1 -1
  218. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  219. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  220. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-look.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 contrastRatio,\n validateThemeForPublish,\n} from '@aglyn/aglyn/app-utils/site-theme'\nimport { DEFAULT_SITE_THEME } from '@aglyn/aglyn/app-utils/default-site'\nimport type {\n HostTheme,\n HostThemeComponentOverride,\n HostThemeSchemeColors,\n HostThemeTypographyVariant,\n HostThemeTypographyVariantKey,\n} from '@aglyn/shared-data-types'\nimport { writeThemeFonts } from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport type { AiTool } from '../runtime/ai-runtime'\nimport {\n AI_SITE_BASES,\n AI_SITE_CORNERS,\n AI_SITE_FONT_PAIRINGS,\n AI_SITE_GROUNDS,\n AI_SITE_RHYTHMS,\n aiSiteFontPairing,\n type AiSiteBase,\n type AiSiteButtons,\n type AiSiteChroma,\n type AiSiteCorners,\n type AiSiteDensity,\n type AiSiteGround,\n type AiSiteKind,\n type AiSiteRhythm,\n} from './ai-site-kinds'\n\n/**\n * A site's look (AGL-3660): one of the platform's base themes, and this\n * site's own customizations layered over it as the site theme's override —\n * the same base-and-edits shape a person's pick on Setup → Theme is stored\n * as, so the Theme page names the base it came from and every edit there\n * starts from what the AI chose.\n *\n * Everything the look decides lives in the THEME, never on a node: palette,\n * fonts, type scale, corners, spacing, and the component defaults and styles\n * of buttons, cards, fields, eyebrows, the header bar and the FAQ. So a Card\n * or a Button a person drags in later looks like the rest of the site, and\n * the layout compiler emits plain components.\n *\n * Three inputs decide it:\n * - the KIND (`ai-site-kinds.ts`): the family of looks that suit the site;\n * - the MODEL's choices for this business (`submit_site_look`), any of\n * which may be missing;\n * - the per-job SEED, which fills what the model left out from the kind's\n * options and nudges every hue, chroma and size, so two jobs with the same\n * brief and the same answers still produce two different themes.\n *\n * Colors are built in OKLCH and held to WCAG: body text 4.5:1 on its ground\n * and on paper, every button label 4.5:1 on its fill, the brand color 3:1 on\n * the page — then the whole theme passes `validateThemeForPublish`, or its\n * colors fall back to the starter theme's.\n */\n\n// ── The tokens ───────────────────────────────────────────────────────────\n\n/** How cards are drawn. */\nexport const AI_SITE_CARDS = ['flat', 'outlined', 'elevated', 'tinted', 'rule'] as const\nexport type AiSiteCards = (typeof AI_SITE_CARDS)[number]\n/** The form field style. */\nexport const AI_SITE_FIELDS = ['outlined', 'filled', 'standard'] as const\nexport type AiSiteFields = (typeof AI_SITE_FIELDS)[number]\n/** How an eyebrow above a heading is set. */\nexport const AI_SITE_EYEBROWS = ['caps', 'small', 'rule'] as const\nexport type AiSiteEyebrow = (typeof AI_SITE_EYEBROWS)[number]\n/** What sets a plain header bar off the page. */\nexport const AI_SITE_HEADERS = ['line', 'shadow', 'flat'] as const\nexport type AiSiteHeader = (typeof AI_SITE_HEADERS)[number]\n/** Button shapes and casing; `link` draws quiet buttons as underlined links. */\nexport const AI_SITE_BUTTONS: readonly AiSiteButtons[] = ['rounded', 'pill', 'square', 'caps', 'link']\nconst DENSITIES: readonly AiSiteDensity[] = ['compact', 'regular', 'airy']\n\n/**\n * A site's style tokens: everything its theme was built from. Stored with the\n * site (`hosts/{id}.siteStyle`) so a later AI edit keeps the look.\n */\nexport interface AiSiteStyle {\n v: 1\n kind: string\n base: AiSiteBase\n seed: number\n hue: number\n accent: number\n chroma: AiSiteChroma\n ground: AiSiteGround\n fonts: string\n corners: AiSiteCorners\n buttons: AiSiteButtons\n cards: AiSiteCards\n fields: AiSiteFields\n eyebrow: AiSiteEyebrow\n header: AiSiteHeader\n /** The header's arrangement: brand at the start and links after it, or both centered. */\n headerAlign: 'start' | 'center'\n /** How the pages alternate their bands. */\n rhythm: AiSiteRhythm\n headingScale: number\n density: AiSiteDensity\n /** A brand color the brief gave, used as the primary color. */\n brand: string | null\n}\n\n/** What the model may choose, every field optional. */\nexport type AiSiteLookAnswer = Partial<\n Pick<\n AiSiteStyle,\n 'base' | 'hue' | 'accent' | 'ground' | 'fonts' | 'corners' | 'buttons' | 'cards' | 'fields' | 'eyebrow' | 'header' | 'brand'\n >\n>\n\n// ── The seed ─────────────────────────────────────────────────────────────\n\n/** A job's seed: FNV-1a over its id. */\nexport function aiSiteSeed(jobId: string): number {\n let hash = 0x811c9dc5\n for (let index = 0; index < jobId.length; index += 1) {\n hash ^= jobId.charCodeAt(index)\n hash = Math.imul(hash, 0x01000193)\n }\n return hash >>> 0\n}\n\n/** A small deterministic generator over a seed (mulberry32). */\nfunction random(seed: number): () => number {\n let state = seed >>> 0\n return () => {\n state = (state + 0x6d2b79f5) >>> 0\n let t = state\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nconst pick = <T,>(next: () => number, list: readonly T[]): T => list[Math.floor(next() * list.length) % list.length] as T\n\n// ── The model's answer ───────────────────────────────────────────────────\n\nexport const AI_SITE_LOOK_TOOL_NAME = 'submit_site_look'\n\nconst enumOf = (values: readonly string[], description: string) => ({ type: 'string', enum: [...values], description })\n\n/** The strict tool the look is answered with; every field is required and the reader is lenient. */\nexport const AI_SITE_LOOK_TOOL: AiTool = {\n name: AI_SITE_LOOK_TOOL_NAME,\n description: \"Submit this website's look: the base theme it starts from and the choices that make it this business's own.\",\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['base', 'hue', 'accent', 'ground', 'fonts', 'corners', 'buttons', 'cards', 'fields', 'eyebrow', 'header', 'brand'],\n properties: {\n base: enumOf(AI_SITE_BASES, 'The base theme to start from, from the list in the request.'),\n hue: { type: 'integer', description: 'The brand color as a hue from 0 to 359 (0 red, 30 orange, 55 gold, 140 green, 190 teal, 220 blue, 270 violet, 330 pink).' },\n accent: { type: 'integer', description: 'The accent color as a hue from 0 to 359, chosen to sit well beside the brand hue.' },\n ground: enumOf(AI_SITE_GROUNDS, 'The page background: white, warm (cream), cool (gray-blue) or tinted (a whisper of the brand color).'),\n fonts: enumOf(AI_SITE_FONT_PAIRINGS.map((pairing) => pairing.id), 'A font pairing from the list in the request.'),\n corners: enumOf(AI_SITE_CORNERS, 'Corner rounding for cards and fields.'),\n buttons: enumOf(AI_SITE_BUTTONS, 'Buttons: rounded, pill, square, caps (capitals) or link (quiet buttons drawn as underlined links).'),\n cards: enumOf(AI_SITE_CARDS, 'Cards: flat, outlined, elevated (a soft shadow), tinted (a wash of the brand color) or rule (a colored line on top).'),\n fields: enumOf(AI_SITE_FIELDS, 'Form fields: outlined, filled or standard (underlined).'),\n eyebrow: enumOf(AI_SITE_EYEBROWS, 'The small label above headings: caps (spaced capitals), small (quiet small text) or rule (capitals after a short line).'),\n header: enumOf(AI_SITE_HEADERS, 'What sets the header bar off the page: a line, a shadow, or flat.'),\n brand: { anyOf: [{ type: 'string' }, { type: 'null' }], description: 'A hex brand color the brief gives, exactly as written, or null.' },\n },\n },\n}\n\nconst oneOf = <T extends string>(value: unknown, values: readonly T[]): T | undefined =>\n typeof value === 'string' && (values as readonly string[]).includes(value) ? (value as T) : undefined\n\nconst hueOf = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? ((Math.round(value) % 360) + 360) % 360 : undefined\n\n/** A brand color as a six-digit hex, or `null`. */\nexport function aiSiteBrandHex(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const match = /#?([0-9a-f]{6}|[0-9a-f]{3})\\b/i.exec(value.trim())\n if (!match) return null\n const hex = match[1] as string\n const full = hex.length === 3 ? [...hex].map((digit) => digit + digit).join('') : hex\n return `#${full.toLowerCase()}`\n}\n\n/** The model's answer, read leniently: a value outside its vocabulary is left for the seed. */\nexport function aiReadSiteLook(answer: unknown): AiSiteLookAnswer {\n const raw = (answer && typeof answer === 'object' ? answer : {}) as Record<string, unknown>\n const look: AiSiteLookAnswer = {}\n const base = oneOf(raw['base'], AI_SITE_BASES)\n if (base) look.base = base\n const hue = hueOf(raw['hue'])\n if (hue !== undefined) look.hue = hue\n const accent = hueOf(raw['accent'])\n if (accent !== undefined) look.accent = accent\n const ground = oneOf(raw['ground'], AI_SITE_GROUNDS)\n if (ground) look.ground = ground\n if (aiSiteFontPairing(raw['fonts'])) look.fonts = raw['fonts'] as string\n const corners = oneOf(raw['corners'], AI_SITE_CORNERS)\n if (corners) look.corners = corners\n const buttons = oneOf(raw['buttons'], AI_SITE_BUTTONS)\n if (buttons) look.buttons = buttons\n const cards = oneOf(raw['cards'], AI_SITE_CARDS)\n if (cards) look.cards = cards\n const fields = oneOf(raw['fields'], AI_SITE_FIELDS)\n if (fields) look.fields = fields\n const eyebrow = oneOf(raw['eyebrow'], AI_SITE_EYEBROWS)\n if (eyebrow) look.eyebrow = eyebrow\n const header = oneOf(raw['header'], AI_SITE_HEADERS)\n if (header) look.header = header\n const brand = aiSiteBrandHex(raw['brand'])\n if (brand) look.brand = brand\n return look\n}\n\n// ── Kind, model and seed into tokens ─────────────────────────────────────\n\n/** The building-block styles a kind leans to, before the model or the seed choose. */\nconst BLOCK_BIAS: Partial<\n Record<string, { cards: readonly AiSiteCards[]; eyebrow: readonly AiSiteEyebrow[]; header: readonly AiSiteHeader[]; fields: readonly AiSiteFields[] }>\n> = {\n portfolio: { cards: ['rule', 'flat', 'outlined'], eyebrow: ['small', 'rule', 'caps'], header: ['flat', 'line', 'shadow'], fields: ['standard', 'outlined', 'filled'] },\n studio: { cards: ['flat', 'rule', 'outlined'], eyebrow: ['rule', 'small', 'caps'], header: ['flat', 'line', 'shadow'], fields: ['standard', 'outlined', 'filled'] },\n photography: { cards: ['flat', 'rule', 'outlined'], eyebrow: ['small', 'rule', 'caps'], header: ['flat', 'line', 'shadow'], fields: ['standard', 'outlined', 'filled'] },\n blog: { cards: ['rule', 'outlined', 'flat', 'tinted'], eyebrow: ['small', 'caps', 'rule'], header: ['line', 'flat', 'shadow'], fields: ['outlined', 'standard', 'filled'] },\n trades: { cards: ['elevated', 'outlined', 'rule', 'flat', 'tinted'], eyebrow: ['caps', 'rule', 'small'], header: ['shadow', 'line', 'flat'], fields: ['outlined', 'filled', 'standard'] },\n fitness: { cards: ['elevated', 'flat', 'rule', 'outlined'], eyebrow: ['caps', 'rule', 'small'], header: ['shadow', 'flat', 'line'], fields: ['filled', 'outlined', 'standard'] },\n professional: { cards: ['outlined', 'rule', 'flat', 'elevated'], eyebrow: ['caps', 'rule', 'small'], header: ['line', 'flat', 'shadow'], fields: ['outlined', 'standard', 'filled'] },\n wellness: { cards: ['tinted', 'flat', 'elevated', 'outlined'], eyebrow: ['small', 'caps', 'rule'], header: ['flat', 'line', 'shadow'], fields: ['filled', 'outlined', 'standard'] },\n yoga: { cards: ['tinted', 'flat', 'outlined'], eyebrow: ['small', 'rule', 'caps'], header: ['flat', 'line', 'shadow'], fields: ['filled', 'standard', 'outlined'] },\n restaurant: { cards: ['flat', 'elevated', 'tinted', 'rule'], eyebrow: ['caps', 'rule', 'small'], header: ['flat', 'shadow', 'line'], fields: ['outlined', 'filled', 'standard'] },\n beauty: { cards: ['tinted', 'flat', 'outlined', 'rule'], eyebrow: ['rule', 'small', 'caps'], header: ['flat', 'line', 'shadow'], fields: ['standard', 'filled', 'outlined'] },\n music: { cards: ['flat', 'rule', 'elevated'], eyebrow: ['caps', 'rule', 'small'], header: ['flat', 'shadow', 'line'], fields: ['filled', 'standard', 'outlined'] },\n}\nconst ANY_BLOCKS = { cards: AI_SITE_CARDS, eyebrow: AI_SITE_EYEBROWS, header: AI_SITE_HEADERS, fields: AI_SITE_FIELDS }\n\n/** The hue family a hue is in: twelve families of thirty degrees, 0 the reds. */\nexport function aiSiteHueFamily(hue: number): number {\n return Math.floor((((hue + 15) % 360) + 360) % 360 / 30)\n}\n\n/** Which of a kind's hue ranges a hue falls in, or -1. */\nfunction rangeOf(hue: number, kind: AiSiteKind): number {\n return kind.look.hues.findIndex(([from, to]) => (from <= to ? hue >= from && hue <= to : hue >= from || hue <= to))\n}\n\n/** The look dimensions two sites are compared by (`ai-site-look.spec.ts`). */\nexport const AI_SITE_LOOK_DIMENSIONS = [\n 'base',\n 'hueFamily',\n 'fonts',\n 'buttons',\n 'cards',\n 'corners',\n 'ground',\n 'eyebrow',\n 'header',\n 'fields',\n 'headerAlign',\n 'rhythm',\n] as const\n\n/** A style's value on each tracked dimension. */\nexport function aiSiteLookSignature(style: AiSiteStyle): Record<(typeof AI_SITE_LOOK_DIMENSIONS)[number], string> {\n return {\n base: style.base,\n hueFamily: String(aiSiteHueFamily(style.hue)),\n fonts: style.fonts,\n buttons: style.buttons,\n cards: style.cards,\n corners: style.corners,\n ground: style.ground,\n eyebrow: style.eyebrow,\n header: style.header,\n fields: style.fields,\n headerAlign: style.headerAlign,\n rhythm: style.rhythm,\n }\n}\n\n/**\n * The style tokens for a site of this kind (AGL-3660). Every dimension is\n * the SEED's choice among what suits the kind, with the model's choice for\n * this business as a favored candidate — kept at a given chance, not always\n * — so the brief steers the look and two jobs with the same brief and the\n * same answer still come out as two sites: another base, another hue family,\n * another pairing, other buttons. A brand color the brief gives is always\n * kept.\n */\nexport function aiSiteStyleFor(input: {\n kind: AiSiteKind\n answer: AiSiteLookAnswer\n seed: number\n brand?: string | null\n /**\n * The looks of the workspace's other sites (AGL-3660): a look that shares\n * one's base, hue family, heading font and buttons, or differs from one in\n * fewer than three tracked dimensions, is drawn again from the next seed.\n */\n avoid?: readonly AiSiteStyle[]\n}): AiSiteStyle {\n const avoid = input.avoid ?? []\n if (!avoid.length) return styleOnce(input, input.seed)\n const signatures = avoid.map(aiSiteLookSignature)\n let best: { style: AiSiteStyle; least: number } | null = null\n for (let attempt = 0; attempt < AI_SITE_LOOK_DRAWS; attempt += 1) {\n const style = styleOnce(input, attempt === 0 ? input.seed : (Math.imul(input.seed ^ 0x5bd1e995, attempt + 1) >>> 0))\n const own = aiSiteLookSignature(style)\n const least = Math.min(\n ...signatures.map((other) =>\n TUPLE.every((dimension) => own[dimension] === other[dimension])\n ? -1\n : AI_SITE_LOOK_DIMENSIONS.filter((dimension) => own[dimension] !== other[dimension]).length,\n ),\n )\n if (least >= AI_SITE_LOOK_LEAST_DIFFERENCES) return style\n if (!best || least > best.least) best = { style, least }\n }\n return (best as { style: AiSiteStyle }).style\n}\n\n/** How many seeds a look draws before it settles for the most different. */\nexport const AI_SITE_LOOK_DRAWS = 32\n/** The fewest tracked dimensions in which two of a workspace's sites differ. */\nexport const AI_SITE_LOOK_LEAST_DIFFERENCES = 3\n/** The dimensions no two of a workspace's sites share all of. */\nconst TUPLE = ['base', 'hueFamily', 'fonts', 'buttons'] as const\n\nfunction styleOnce(\n input: { kind: AiSiteKind; answer: AiSiteLookAnswer; brand?: string | null },\n seed: number,\n): AiSiteStyle {\n const { kind, answer } = input\n const next = random(seed)\n const family = kind.look\n const blocks = BLOCK_BIAS[kind.id] ?? ANY_BLOCKS\n /** The model's choice at a chance of `keep`, else the seed's among `options`. */\n const choose = <T,>(model: T | undefined, options: readonly T[], keep: number): T =>\n model !== undefined && next() < keep ? model : pick(next, options)\n\n // The hue: a family among the kind's (the brief's industry), the model's\n // own family favored, and a hue within it.\n const modelRange = answer.hue === undefined ? -1 : rangeOf(answer.hue, kind)\n const keepHue = modelRange !== -1 && next() < 0.3\n const range = keepHue ? modelRange : Math.floor(next() * family.hues.length) % family.hues.length\n const [from, to] = family.hues[range] as readonly [number, number]\n const span = from <= to ? to - from : 360 - from + to\n const hue = keepHue\n ? ((answer.hue as number) + (next() < 0.5 ? -1 : 1) * Math.round(4 + next() * 8) + 360) % 360\n : Math.round(from + next() * span) % 360\n const accentFrom = answer.accent !== undefined && next() < 0.4 ? answer.accent : (hue + pick(next, [30, 150, 180, 210, 330])) % 360\n const accent = (accentFrom + (next() < 0.5 ? -1 : 1) * Math.round(8 + next() * 16) + 360) % 360\n\n const bases = [...new Set([...(answer.base ? [answer.base] : []), ...family.bases])]\n return {\n v: 1,\n kind: kind.id,\n base: choose(answer.base, bases, 0.3),\n seed,\n hue,\n accent,\n chroma: family.chroma,\n ground: choose(answer.ground, family.grounds, 0.35),\n fonts: choose(answer.fonts, family.fonts, 0.3),\n corners: choose(answer.corners, family.corners, 0.35),\n buttons: choose(answer.buttons, [family.buttons, ...AI_SITE_BUTTONS], 0.3),\n cards: choose(answer.cards, blocks.cards, 0.35),\n fields: choose(answer.fields, blocks.fields, 0.35),\n eyebrow: choose(answer.eyebrow, blocks.eyebrow, 0.35),\n header: choose(answer.header, blocks.header, 0.35),\n headerAlign: pick(next, family.headerAligns ?? ['start', 'start', 'center']),\n rhythm: pick(next, family.rhythms ?? AI_SITE_RHYTHMS),\n headingScale: Math.round(family.headingScale * (0.94 + next() * 0.12) * 100) / 100,\n density: family.density === 'regular' && next() < 0.25 ? pick(next, DENSITIES) : family.density,\n brand: aiSiteBrandHex(input.brand) ?? answer.brand ?? null,\n }\n}\n\n// ── Color ────────────────────────────────────────────────────────────────\n\ntype Rgb = [number, number, number]\n\nfunction oklabToLinear(L: number, a: number, b: number): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ]\n}\n\nconst toSrgb = (channel: number) =>\n channel <= 0.0031308 ? 12.92 * channel : 1.055 * channel ** (1 / 2.4) - 0.055\nconst fromSrgb = (channel: number) =>\n channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4\n\n/** An OKLCH color as hex, its chroma reduced until it fits sRGB. */\nexport function aiOklchHex(L: number, C: number, h: number): string {\n const lightness = Math.min(1, Math.max(0, L))\n let chroma = Math.max(0, C)\n for (;;) {\n const radians = (h * Math.PI) / 180\n const linear = oklabToLinear(lightness, chroma * Math.cos(radians), chroma * Math.sin(radians))\n const fits = linear.every((channel) => channel >= -0.0005 && channel <= 1.0005)\n if (fits || chroma <= 0) {\n return `#${linear\n .map((channel) => Math.round(Math.min(1, Math.max(0, toSrgb(Math.min(1, Math.max(0, channel))))) * 255))\n .map((value) => value.toString(16).padStart(2, '0'))\n .join('')}`\n }\n chroma = Math.max(0, chroma - 0.004)\n }\n}\n\n/** A hex color in OKLCH. */\nexport function aiHexOklch(hex: string): { L: number; C: number; h: number } {\n const value = hex.replace('#', '')\n const [r, g, b] = [0, 2, 4].map((at) => fromSrgb(parseInt(value.slice(at, at + 2), 16) / 255)) as Rgb\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)\n const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s\n const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s\n const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s\n return { L, C: Math.hypot(A, B), h: ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360 }\n}\n\nconst ratio = (a: string, b: string) => contrastRatio(a, b) ?? 1\n\n/** The lightness, stepped from `L` toward `toward`, at which `hex(L)` first reaches `min` against `against`. */\nfunction untilContrast(L: number, C: number, h: number, against: string, min: number, toward: 0 | 1): number {\n let lightness = L\n for (let step = 0; step < 60; step += 1) {\n if (ratio(aiOklchHex(lightness, C, h), against) >= min) return lightness\n lightness += toward ? 0.012 : -0.012\n if (lightness <= 0.05 || lightness >= 0.98) break\n }\n return lightness\n}\n\n/** The label color of a fill: white or a deep ink of its hue, whichever reads at 4.5:1, or better. */\nfunction labelOn(fill: string, h: number): string {\n const white = '#ffffff'\n const ink = aiOklchHex(0.2, 0.04, h)\n return ratio(white, fill) >= 4.5 ? white : ratio(ink, fill) >= 4.5 ? ink : ratio(white, fill) > ratio(ink, fill) ? white : '#000000'\n}\n\nconst CHROMA: Record<AiSiteChroma, number> = { neutral: 0.15, muted: 0.085, balanced: 0.135, vivid: 0.185 }\nconst START_L: Record<AiSiteChroma, number> = { neutral: 0.52, muted: 0.48, balanced: 0.52, vivid: 0.56 }\n\n/** Both schemes' colors for a style. */\nexport function aiSiteColors(style: AiSiteStyle): { light: HostThemeSchemeColors; dark: HostThemeSchemeColors } {\n const next = random(style.seed ^ 0x9e3779b9)\n const neutral = style.chroma === 'neutral'\n const brand = style.brand ? aiHexOklch(style.brand) : null\n const h = brand?.h ?? style.hue\n const chroma = brand?.C ?? CHROMA[style.chroma] * (0.9 + next() * 0.2)\n const groundChroma = neutral ? 0.3 : 1\n const white = '#ffffff'\n\n // The page grounds.\n const grounds: Record<AiSiteGround, { L: number; C: number; h: number; paper: [number, number] }> = {\n white: { L: 0.985, C: 0.004, h, paper: [1, 0] },\n warm: { L: 0.972, C: 0.016, h: 78, paper: [0.993, 0.007] },\n cool: { L: 0.974, C: 0.009, h: 250, paper: [1, 0] },\n tinted: { L: 0.968, C: 0.02, h, paper: [0.994, 0.006] },\n }\n const ground = grounds[style.ground]\n const background = aiOklchHex(ground.L, ground.C * groundChroma, ground.h)\n const paper = aiOklchHex(ground.paper[0], ground.paper[1] * groundChroma, ground.h)\n\n // The brand color: a button label reads on it, so it reads on the page too.\n let primaryMain: string\n let primaryL: number\n if (brand && ratio(white, style.brand as string) >= 4.5) {\n primaryMain = style.brand as string\n primaryL = brand.L\n } else {\n primaryL = untilContrast(brand ? Math.min(brand.L, START_L[style.chroma]) : START_L[style.chroma] + (next() - 0.5) * 0.06, chroma, h, white, 4.6, 0)\n primaryMain = aiOklchHex(primaryL, chroma, h)\n }\n const accentChroma = neutral ? 0.17 : Math.max(0.07, chroma * 0.95)\n\n // The secondary color fills a button ON a brand band, so it stands apart from the brand color.\n const secondary = neutral\n ? { main: aiOklchHex(0.24, 0.012, h), h }\n : (() => {\n // A light wash of the accent rather than the accent at full strength:\n // it reads on the brand color without fighting it.\n const washChroma = Math.min(accentChroma, 0.08)\n let L = 0.9\n let main = aiOklchHex(L, washChroma, style.accent)\n while (ratio(main, primaryMain) < 3 && L < 0.96) {\n L += 0.02\n main = aiOklchHex(L, washChroma, style.accent)\n }\n return { main, h: style.accent }\n })()\n const tertiaryHue = (style.accent + 60) % 360\n const tertiaryL = untilContrast(0.52, 0.12, tertiaryHue, white, 4.6, 0)\n const tertiary = aiOklchHex(tertiaryL, 0.12, tertiaryHue)\n const textChroma = neutral ? 0.006 : 0.02\n\n const light: HostThemeSchemeColors = {\n primary: {\n main: primaryMain,\n light: aiOklchHex(Math.min(0.92, primaryL + 0.12), chroma, h),\n dark: aiOklchHex(Math.max(0.15, primaryL - 0.1), chroma, h),\n contrastText: labelOn(primaryMain, h),\n },\n secondary: {\n main: secondary.main,\n light: aiOklchHex(0.93, accentChroma * 0.6, secondary.h),\n dark: aiOklchHex(0.42, accentChroma, secondary.h),\n contrastText: labelOn(secondary.main, secondary.h),\n },\n tertiary: {\n main: tertiary,\n light: aiOklchHex(Math.min(0.9, tertiaryL + 0.14), 0.12, tertiaryHue),\n dark: aiOklchHex(Math.max(0.15, tertiaryL - 0.1), 0.12, tertiaryHue),\n contrastText: labelOn(tertiary, tertiaryHue),\n },\n surface: { main: aiOklchHex(0.955, 0.012 * groundChroma, h), contrastText: aiOklchHex(0.22, textChroma, h) },\n background: { default: background, paper },\n text: {\n primary: aiOklchHex(0.22, textChroma, h),\n secondary: aiOklchHex(0.45, textChroma + 0.005, h),\n disabled: aiOklchHex(0.68, 0.01, h),\n },\n tint: {\n primary: aiOklchHex(0.955, neutral ? 0.03 : 0.035, neutral ? style.accent : h),\n secondary: aiOklchHex(0.955, 0.035, style.accent),\n tertiary: aiOklchHex(0.955, 0.03, tertiaryHue),\n },\n divider: aiOklchHex(0.9, 0.012 * groundChroma, h),\n }\n\n const darkGround = style.ground === 'tinted' ? 0.024 : style.ground === 'warm' ? 0.014 : 0.012\n const darkBackground = aiOklchHex(0.17, darkGround * groundChroma, style.ground === 'warm' ? 60 : h)\n const darkPaper = aiOklchHex(0.215, darkGround * groundChroma, style.ground === 'warm' ? 60 : h)\n const darkChroma = Math.min(chroma, 0.14)\n const darkPrimaryL = untilContrast(0.78, darkChroma, h, darkBackground, 4.5, 1)\n const darkPrimary = aiOklchHex(darkPrimaryL, darkChroma, h)\n const darkSecondary = neutral ? aiOklchHex(0.93, 0.01, h) : aiOklchHex(0.45, accentChroma * 0.85, style.accent)\n const darkTertiary = aiOklchHex(0.8, 0.1, tertiaryHue)\n const dark: HostThemeSchemeColors = {\n primary: {\n main: darkPrimary,\n light: aiOklchHex(Math.min(0.94, darkPrimaryL + 0.08), darkChroma, h),\n dark: aiOklchHex(darkPrimaryL - 0.1, darkChroma, h),\n contrastText: labelOn(darkPrimary, h),\n },\n secondary: {\n main: darkSecondary,\n light: aiOklchHex(0.6, accentChroma * 0.7, secondary.h),\n dark: aiOklchHex(0.35, accentChroma * 0.7, secondary.h),\n contrastText: labelOn(darkSecondary, secondary.h),\n },\n tertiary: {\n main: darkTertiary,\n light: aiOklchHex(0.88, 0.08, tertiaryHue),\n dark: aiOklchHex(0.7, 0.1, tertiaryHue),\n contrastText: labelOn(darkTertiary, tertiaryHue),\n },\n surface: { main: aiOklchHex(0.25, 0.014 * groundChroma, h), contrastText: aiOklchHex(0.95, 0.01, h) },\n background: { default: darkBackground, paper: darkPaper },\n text: {\n primary: aiOklchHex(0.95, 0.008, h),\n secondary: aiOklchHex(0.77, 0.014, h),\n disabled: aiOklchHex(0.52, 0.01, h),\n },\n tint: {\n primary: aiOklchHex(0.27, 0.05, neutral ? style.accent : h),\n secondary: aiOklchHex(0.27, 0.05, style.accent),\n tertiary: aiOklchHex(0.27, 0.04, tertiaryHue),\n },\n divider: aiOklchHex(0.32, 0.015 * groundChroma, h),\n }\n return { light, dark }\n}\n\n// ── The theme ────────────────────────────────────────────────────────────\n\nconst RADIUS: Record<AiSiteCorners, number> = { sharp: 2, soft: 6, round: 12, pill: 18 }\nconst SPACING: Record<AiSiteDensity, number> = { compact: 7, regular: 8, airy: 9 }\n\n/** How much of the heading scale each style takes: a display line grows most, small headings not at all. */\nconst SCALE_SHARE: Partial<Record<HostThemeTypographyVariantKey, { size: number; share: number }>> = {\n displayXl: { size: 4, share: 1.2 },\n h1: { size: 3.25, share: 1 },\n h2: { size: 2.25, share: 0.8 },\n h3: { size: 1.75, share: 0.5 },\n h4: { size: 1.5, share: 0.3 },\n h5: { size: 1.25, share: 0 },\n h6: { size: 1.125, share: 0 },\n}\n\nconst rem = (value: number) => `${Math.round(value * 1000) / 1000}rem`\n\n/** The type a style sets: headings at its scale, weight and tracking, and the eyebrow and button styles. */\nfunction typographyFor(style: AiSiteStyle): HostTheme['typography'] {\n const pairing = aiSiteFontPairing(style.fonts) ?? (AI_SITE_FONT_PAIRINGS[0] as NonNullable<ReturnType<typeof aiSiteFontPairing>>)\n const variants: Partial<Record<HostThemeTypographyVariantKey, HostThemeTypographyVariant>> = {\n ...(DEFAULT_SITE_THEME.typography?.variants ?? {}),\n }\n for (const [key, { size, share }] of Object.entries(SCALE_SHARE) as Array<[HostThemeTypographyVariantKey, { size: number; share: number }]>) {\n const big = key === 'displayXl' || key === 'h1' || key === 'h2'\n const variant: HostThemeTypographyVariant = {\n fontSize: rem(size * (1 + (style.headingScale - 1) * share) * (pairing.caps && big ? 0.92 : 1)),\n fontWeight: big ? pairing.heading.weight : pairing.heading.sub,\n lineHeight: big ? (pairing.caps ? 1.05 : 1.1) : 1.25,\n }\n if (pairing.tracking !== undefined && (big || key === 'h3')) variant.letterSpacing = `${pairing.tracking}em`\n if (pairing.caps && (big || key === 'h3')) variant.textTransform = 'uppercase'\n variants[key] = variant\n }\n const bodyBold = pairing.body.weights.includes(600) ? 600 : 700\n variants.overline =\n style.eyebrow === 'small'\n ? { fontSize: '0.875rem', fontWeight: bodyBold, letterSpacing: '0.01em', textTransform: 'none' }\n : { fontSize: '0.78rem', fontWeight: 700, letterSpacing: style.eyebrow === 'rule' ? '0.16em' : '0.12em', textTransform: 'uppercase' }\n variants.button = {\n fontWeight: style.buttons === 'caps' ? 700 : bodyBold,\n textTransform: style.buttons === 'caps' ? 'uppercase' : 'none',\n ...(style.buttons === 'caps' ? { letterSpacing: '0.06em' } : {}),\n }\n return { variants }\n}\n\n/** The card's look, in theme terms only: palette paths, shadow indexes and border widths. */\nfunction cardSx(cards: AiSiteCards): Record<string, unknown> {\n switch (cards) {\n case 'flat':\n return { boxShadow: 0, border: 0, bgcolor: 'surface.main' }\n case 'outlined':\n return { boxShadow: 0, border: 1, borderColor: 'divider', bgcolor: 'background.paper' }\n case 'elevated':\n return { boxShadow: 3, border: 0, bgcolor: 'background.paper' }\n case 'tinted':\n return { boxShadow: 0, border: 0, bgcolor: 'tint.primary' }\n case 'rule':\n return { boxShadow: 0, border: 0, borderTop: 3, borderColor: 'primary.main', borderRadius: 0, bgcolor: 'background.paper' }\n }\n}\n\n/** The component defaults and styles a style sets: buttons, cards, fields, eyebrows, the header bar and the FAQ. */\nfunction componentsFor(style: AiSiteStyle): Record<string, HostThemeComponentOverride> {\n const radius = RADIUS[style.corners]\n const buttonRadius = style.buttons === 'pill' ? 999 : style.buttons === 'square' ? 2 : radius\n const card = cardSx(style.cards)\n return {\n MuiButton: {\n defaultProps: { disableElevation: true },\n // 44px: the touch target a visitor's thumb needs.\n styleOverrides: { root: { borderRadius: buttonRadius, minHeight: 44, boxShadow: 'none' } },\n ...(style.buttons === 'link'\n ? {\n variants: [\n {\n props: { variant: 'text' },\n style: { textDecoration: 'underline', textUnderlineOffset: '0.3em', textDecorationThickness: '2px' },\n },\n ],\n }\n : {}),\n },\n MuiCard: {\n defaultProps:\n style.cards === 'outlined'\n ? { variant: 'outlined' }\n : { variant: 'elevation', elevation: style.cards === 'elevated' ? 3 : 0 },\n styleOverrides: { root: { borderRadius: style.cards === 'rule' ? 0 : Math.max(radius, 4) } },\n sx: { root: card },\n },\n MuiAccordion: {\n defaultProps: { disableGutters: true, elevation: 0 },\n sx: {\n root: {\n ...card,\n borderTop: style.cards === 'rule' ? 1 : card['borderTop'] ?? card['border'],\n '&::before': { display: 'none' },\n '&:not(:last-of-type)': { mb: 1 },\n },\n },\n },\n MuiTextField: { defaultProps: { variant: style.fields } },\n MuiOutlinedInput: { styleOverrides: { root: { borderRadius: radius } } },\n MuiFilledInput: { styleOverrides: { root: { borderTopLeftRadius: radius, borderTopRightRadius: radius } } },\n MuiChip: { styleOverrides: { root: { borderRadius: style.corners === 'sharp' ? 2 : 999 } } },\n MuiAppBar: {\n defaultProps: { elevation: 0 },\n variants: [\n {\n props: { color: 'inherit' },\n sx:\n style.header === 'line'\n ? { boxShadow: 0, borderBottom: 1, borderColor: 'divider' }\n : style.header === 'shadow'\n ? { boxShadow: 2, borderBottom: 0 }\n : { boxShadow: 0, borderBottom: 0 },\n },\n ],\n },\n ...(style.eyebrow === 'rule'\n ? {\n MuiTypography: {\n variants: [\n {\n props: { variant: 'overline' },\n // A short line in the eyebrow's own color, so it reads on any band.\n style: {\n '&::before': {\n content: '\"\"',\n display: 'inline-block',\n width: '1.75em',\n height: '2px',\n marginRight: '0.75em',\n verticalAlign: 'middle',\n backgroundColor: 'currentColor',\n },\n },\n },\n ],\n },\n }\n : {}),\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** `over` merged into `under`, objects key by key, anything else replaced. */\nfunction merged<T>(under: T, over: unknown): T {\n if (!isRecord(under) || !isRecord(over)) return (over === undefined ? under : over) as T\n const out: Record<string, unknown> = { ...under }\n for (const [key, value] of Object.entries(over)) out[key] = key in out ? merged(out[key], value) : value\n return out as T\n}\n\n/** Whether a theme passes the publish check and the brand-on-page contrast the doctrine asks. */\nfunction themeReads(theme: HostTheme): boolean {\n if (!validateThemeForPublish(theme).ok) return false\n for (const scheme of ['light', 'dark'] as const) {\n const colors = theme.colorSchemes?.[scheme]\n const main = colors?.primary?.main\n const page = colors?.background?.default\n if (!main || !page || ratio(main, page) < 3) return false\n for (const key of ['primary', 'secondary', 'tertiary'] as const) {\n const fill = colors?.[key]\n if (fill?.main && fill.contrastText && ratio(fill.contrastText, fill.main) < 4.5) return false\n }\n }\n return true\n}\n\n/**\n * The site's theme: the base theme with this style layered over it. Colors,\n * type, corners, spacing and the building blocks' component styles are the\n * site's own; whatever the base sets beyond them — its focus rings, its menu\n * and dialog styles, its control heights — stays the base's.\n */\nexport function aiSiteTheme(style: AiSiteStyle, base: HostTheme): HostTheme {\n const pairing = aiSiteFontPairing(style.fonts) ?? (AI_SITE_FONT_PAIRINGS[0] as NonNullable<ReturnType<typeof aiSiteFontPairing>>)\n const colors = aiSiteColors(style)\n const keep = (scheme: 'light' | 'dark') => {\n const own = base.colorSchemes?.[scheme] ?? {}\n // The status colors stay the base's: they mean the same thing on every site.\n return {\n ...(own.error ? { error: own.error } : {}),\n ...(own.warning ? { warning: own.warning } : {}),\n ...(own.info ? { info: own.info } : {}),\n ...(own.success ? { success: own.success } : {}),\n ...colors[scheme],\n }\n }\n let theme: HostTheme = {\n ...base,\n colorSchemes: { light: keep('light'), dark: keep('dark') },\n typography: merged(base.typography ?? {}, typographyFor(style)),\n shape: { ...(base.shape ?? {}), borderRadius: RADIUS[style.corners] },\n spacing: SPACING[style.density],\n components: merged(base.components ?? {}, componentsFor(style)),\n }\n delete theme.darkScheme\n const sameFamily = pairing.heading.family === pairing.body.family\n theme = writeThemeFonts(theme, {\n body: { family: pairing.body.family, category: pairing.body.category, weights: [...pairing.body.weights], source: 'google' },\n heading: sameFamily\n ? null\n : {\n family: pairing.heading.family,\n category: pairing.heading.category,\n weights: [...new Set([pairing.heading.weight, pairing.heading.sub])].sort((a, b) => a - b),\n source: 'google',\n },\n })\n // A pairing whose heading is the body family loads the heading weights too.\n if (sameFamily && theme.fonts?.[0]) {\n theme.fonts[0] = {\n ...theme.fonts[0],\n weights: [...new Set([...(theme.fonts[0].weights ?? []), pairing.heading.weight, pairing.heading.sub])].sort((a, b) => a - b),\n }\n }\n if (!themeReads(theme)) {\n // Never a theme that fails the reading checks: the starter's colors, with everything else kept.\n theme = { ...theme, colorSchemes: DEFAULT_SITE_THEME.colorSchemes }\n }\n return theme\n}\n"],"names":["contrastRatio","validateThemeForPublish","DEFAULT_SITE_THEME","writeThemeFonts","AI_SITE_BASES","AI_SITE_CORNERS","AI_SITE_FONT_PAIRINGS","AI_SITE_GROUNDS","AI_SITE_RHYTHMS","aiSiteFontPairing","AI_SITE_CARDS","AI_SITE_FIELDS","AI_SITE_EYEBROWS","AI_SITE_HEADERS","AI_SITE_BUTTONS","DENSITIES","aiSiteSeed","jobId","hash","index","length","charCodeAt","Math","imul","random","seed","state","t","pick","next","list","floor","AI_SITE_LOOK_TOOL_NAME","enumOf","values","description","type","enum","AI_SITE_LOOK_TOOL","name","strict","inputSchema","additionalProperties","required","properties","base","hue","accent","ground","fonts","map","pairing","id","corners","buttons","cards","fields","eyebrow","header","brand","anyOf","oneOf","value","includes","undefined","hueOf","Number","isFinite","round","aiSiteBrandHex","match","exec","trim","hex","full","digit","join","toLowerCase","aiReadSiteLook","answer","raw","look","BLOCK_BIAS","portfolio","studio","photography","blog","trades","fitness","professional","wellness","yoga","restaurant","beauty","music","ANY_BLOCKS","aiSiteHueFamily","rangeOf","kind","hues","findIndex","from","to","AI_SITE_LOOK_DIMENSIONS","aiSiteLookSignature","style","hueFamily","String","headerAlign","rhythm","aiSiteStyleFor","input","avoid","styleOnce","signatures","best","attempt","AI_SITE_LOOK_DRAWS","own","least","min","other","TUPLE","every","dimension","filter","AI_SITE_LOOK_LEAST_DIFFERENCES","family","blocks","choose","model","options","keep","modelRange","keepHue","range","span","accentFrom","bases","Set","v","chroma","grounds","headerAligns","rhythms","headingScale","density","oklabToLinear","L","a","b","l","m","s","toSrgb","channel","fromSrgb","aiOklchHex","C","h","lightness","max","radians","PI","linear","cos","sin","fits","toString","padStart","aiHexOklch","replace","r","g","at","parseInt","slice","cbrt","A","B","hypot","atan2","ratio","untilContrast","against","toward","step","labelOn","fill","white","ink","CHROMA","neutral","muted","balanced","vivid","START_L","aiSiteColors","groundChroma","paper","warm","cool","tinted","background","primaryMain","primaryL","accentChroma","secondary","main","washChroma","tertiaryHue","tertiaryL","tertiary","textChroma","light","primary","dark","contrastText","surface","default","text","disabled","tint","divider","darkGround","darkBackground","darkPaper","darkChroma","darkPrimaryL","darkPrimary","darkSecondary","darkTertiary","RADIUS","sharp","soft","pill","SPACING","compact","regular","airy","SCALE_SHARE","displayXl","size","share","h1","h2","h3","h4","h5","h6","rem","typographyFor","variants","typography","key","Object","entries","big","variant","fontSize","caps","fontWeight","heading","weight","sub","lineHeight","tracking","letterSpacing","textTransform","bodyBold","body","weights","overline","button","cardSx","boxShadow","border","bgcolor","borderColor","borderTop","borderRadius","componentsFor","card","radius","buttonRadius","MuiButton","defaultProps","disableElevation","styleOverrides","root","minHeight","props","textDecoration","textUnderlineOffset","textDecorationThickness","MuiCard","elevation","sx","MuiAccordion","disableGutters","display","mb","MuiTextField","MuiOutlinedInput","MuiFilledInput","borderTopLeftRadius","borderTopRightRadius","MuiChip","MuiAppBar","color","borderBottom","MuiTypography","content","width","height","marginRight","verticalAlign","backgroundColor","isRecord","Array","isArray","merged","under","over","out","themeReads","theme","ok","scheme","colors","colorSchemes","page","aiSiteTheme","error","warning","info","success","shape","spacing","components","darkScheme","sameFamily","category","source","sort"],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * What Aglyn AI remembers about a site (AGL-3661): short preferences
19
+ * distilled from the edits its owner applied, read into every later AI job
20
+ * for the site, and shown — and clearable — on Setup → Business profile.
21
+ *
22
+ * ── Rules, not a model ────────────────────────────────────────────────────
23
+ *
24
+ * Each preference is recognized by a fixed rule over what the owner asked for
25
+ * and what the applied edit did. No model call: memory has to cost nothing on
26
+ * Free, and a rule says exactly why a line was remembered, so an owner who
27
+ * sees it can recognize the edit it came from. A rule fires only on an edit
28
+ * the owner APPLIED, never on one they were merely shown.
29
+ *
30
+ * ── One per group ─────────────────────────────────────────────────────────
31
+ *
32
+ * A preference belongs to a group, and a group holds one at a time: an owner
33
+ * who asked for a casual tone and later for a formal one prefers the formal
34
+ * one now, so the casual line is forgotten rather than contradicted.
35
+ *
36
+ * Stored at `hosts/{hostId}/aiMemory/{id}`, one document per preference, so
37
+ * clearing one is one client delete (see the rules).
38
+ */
39
+ /** The host subcollection the preferences live in. */
40
+ export declare const AI_SITE_MEMORY_SUBCOLLECTION = "aiMemory";
41
+ /** At most this many are kept per site; the oldest unrepeated go first. */
42
+ export declare const AI_SITE_MEMORY_MAX = 12;
43
+ /** At most this many are told to a model. */
44
+ export declare const AI_SITE_MEMORY_LISTED = 6;
45
+ /** A preference a rule recognized in one applied edit. */
46
+ export interface AiSitePreferenceMatch {
47
+ /** Stable per preference: the document id. */
48
+ id: string;
49
+ /** Preferences in one group replace each other. */
50
+ group: string;
51
+ /** What a model and the owner read. */
52
+ text: string;
53
+ }
54
+ /** A stored preference. */
55
+ export interface AiSitePreference extends AiSitePreferenceMatch {
56
+ /** How many applied edits it was recognized in. */
57
+ count: number;
58
+ lastSeenAtMs: number;
59
+ /** What it was learned from; only applied Assist edits today. */
60
+ source: 'assist-edit';
61
+ }
62
+ /** What an applied edit tells the rules. */
63
+ export interface AiAppliedEditSignal {
64
+ /** What the owner asked the assistant, in their words. */
65
+ question: string;
66
+ /** The applied operations counted by word (`set`, `remove`, `insert`, …). */
67
+ opCounts: Readonly<Record<string, number>>;
68
+ }
69
+ /**
70
+ * The preferences one applied edit shows (AGL-3661): each rule that fires, at
71
+ * most one per group — the first, as the rules are ordered.
72
+ */
73
+ export declare function aiPreferencesFromEdit(signal: AiAppliedEditSignal): AiSitePreferenceMatch[];
74
+ /**
75
+ * The preferences a model is told, strongest first: the most often repeated,
76
+ * then the most recent. Capped at {@link AI_SITE_MEMORY_LISTED}.
77
+ */
78
+ export declare function aiListedPreferences(preferences: readonly AiSitePreference[]): string[];
79
+ /** A stored row as a preference; `null` for one this version cannot read. */
80
+ export declare function aiSitePreferenceOf(id: string, raw: Record<string, unknown> | undefined): AiSitePreference | null;
@@ -0,0 +1,171 @@
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
+ * What Aglyn AI remembers about a site (AGL-3661): short preferences
18
+ * distilled from the edits its owner applied, read into every later AI job
19
+ * for the site, and shown — and clearable — on Setup → Business profile.
20
+ *
21
+ * ── Rules, not a model ────────────────────────────────────────────────────
22
+ *
23
+ * Each preference is recognized by a fixed rule over what the owner asked for
24
+ * and what the applied edit did. No model call: memory has to cost nothing on
25
+ * Free, and a rule says exactly why a line was remembered, so an owner who
26
+ * sees it can recognize the edit it came from. A rule fires only on an edit
27
+ * the owner APPLIED, never on one they were merely shown.
28
+ *
29
+ * ── One per group ─────────────────────────────────────────────────────────
30
+ *
31
+ * A preference belongs to a group, and a group holds one at a time: an owner
32
+ * who asked for a casual tone and later for a formal one prefers the formal
33
+ * one now, so the casual line is forgotten rather than contradicted.
34
+ *
35
+ * Stored at `hosts/{hostId}/aiMemory/{id}`, one document per preference, so
36
+ * clearing one is one client delete (see the rules).
37
+ */ /** The host subcollection the preferences live in. */ export const AI_SITE_MEMORY_SUBCOLLECTION = 'aiMemory';
38
+ /** At most this many are kept per site; the oldest unrepeated go first. */ export const AI_SITE_MEMORY_MAX = 12;
39
+ /** At most this many are told to a model. */ export const AI_SITE_MEMORY_LISTED = 6;
40
+ const has = (pattern)=>(question)=>pattern.test(question);
41
+ /** A section kind an owner removes, by the words they ask for it with. */ const REMOVABLE_SECTIONS = [
42
+ {
43
+ id: 'testimonials',
44
+ words: 'testimonials?|reviews?|quotes?',
45
+ label: 'testimonial and review sections'
46
+ },
47
+ {
48
+ id: 'faq',
49
+ words: 'faqs?|questions section',
50
+ label: 'FAQ sections'
51
+ },
52
+ {
53
+ id: 'pricing',
54
+ words: 'pric(?:es|ing)|plans section',
55
+ label: 'pricing sections'
56
+ },
57
+ {
58
+ id: 'stats',
59
+ words: 'stats?|statistics|numbers section|counters?',
60
+ label: 'stats and number sections'
61
+ },
62
+ {
63
+ id: 'logos',
64
+ words: 'logos?|partners?|clients section|trusted by',
65
+ label: 'logo and partner strips'
66
+ },
67
+ {
68
+ id: 'newsletter',
69
+ words: 'newsletter|sign-?up section|subscribe',
70
+ label: 'newsletter sign-up sections'
71
+ },
72
+ {
73
+ id: 'team',
74
+ words: 'team section|our team|staff section',
75
+ label: 'team sections'
76
+ },
77
+ {
78
+ id: 'gallery',
79
+ words: 'gallery|galleries',
80
+ label: 'gallery sections'
81
+ }
82
+ ];
83
+ const REMOVE_VERBS = '(?:remove|delete|drop|get rid of|take out|cut|lose|hide)';
84
+ const RULES = [
85
+ {
86
+ id: 'tone-casual',
87
+ group: 'tone',
88
+ text: 'Prefers a casual, friendly tone',
89
+ test: has(/\b(casual|friendl(?:y|ier)|relaxed|less formal|warmer|conversational|chill)\b/i)
90
+ },
91
+ {
92
+ id: 'tone-formal',
93
+ group: 'tone',
94
+ text: 'Prefers a formal, professional tone',
95
+ test: has(/\b(more (?:formal|professional|serious)|less casual|formal tone|professional tone)\b/i)
96
+ },
97
+ {
98
+ id: 'copy-short',
99
+ group: 'length',
100
+ text: 'Prefers short, concise copy',
101
+ test: has(/\b(shorter|concise|less (?:text|copy|wordy)|trim|tighten|too long|fewer words|brief(?:er)?)\b/i)
102
+ },
103
+ {
104
+ id: 'copy-detailed',
105
+ group: 'length',
106
+ text: 'Prefers detailed copy',
107
+ test: has(/\b(more detail(?:ed)?|longer|expand|elaborate|too short|say more)\b/i)
108
+ },
109
+ {
110
+ id: 'no-emoji',
111
+ group: 'emoji',
112
+ text: 'Does not want emoji in copy',
113
+ test: has(/\b(no|remove|without|drop|delete) (?:the )?emojis?\b/i)
114
+ },
115
+ ...REMOVABLE_SECTIONS.map((section)=>({
116
+ id: `removes-${section.id}`,
117
+ group: `removes-${section.id}`,
118
+ text: `Removes ${section.label}`,
119
+ test: (question, ops)=>{
120
+ var _ops_remove;
121
+ return ((_ops_remove = ops['remove']) != null ? _ops_remove : 0) > 0 && new RegExp(`\\b${REMOVE_VERBS}\\b[^.?!]{0,40}\\b(?:${section.words})\\b`, 'i').test(question);
122
+ }
123
+ }))
124
+ ];
125
+ /**
126
+ * The preferences one applied edit shows (AGL-3661): each rule that fires, at
127
+ * most one per group — the first, as the rules are ordered.
128
+ */ export function aiPreferencesFromEdit(signal) {
129
+ var _signal_question, _signal_opCounts;
130
+ const question = ((_signal_question = signal.question) != null ? _signal_question : '').slice(0, 2000);
131
+ if (!question.trim()) return [];
132
+ const ops = (_signal_opCounts = signal.opCounts) != null ? _signal_opCounts : {};
133
+ const applied = Object.values(ops).some((count)=>count > 0);
134
+ if (!applied) return [];
135
+ const seen = new Set();
136
+ const matches = [];
137
+ for (const rule of RULES){
138
+ if (seen.has(rule.group) || !rule.test(question, ops)) continue;
139
+ seen.add(rule.group);
140
+ matches.push({
141
+ id: rule.id,
142
+ group: rule.group,
143
+ text: rule.text
144
+ });
145
+ }
146
+ return matches;
147
+ }
148
+ /**
149
+ * The preferences a model is told, strongest first: the most often repeated,
150
+ * then the most recent. Capped at {@link AI_SITE_MEMORY_LISTED}.
151
+ */ export function aiListedPreferences(preferences) {
152
+ return [
153
+ ...preferences
154
+ ].sort((a, b)=>b.count - a.count || b.lastSeenAtMs - a.lastSeenAtMs).slice(0, AI_SITE_MEMORY_LISTED).map((preference)=>preference.text);
155
+ }
156
+ /** A stored row as a preference; `null` for one this version cannot read. */ export function aiSitePreferenceOf(id, raw) {
157
+ const text = typeof (raw == null ? void 0 : raw['text']) === 'string' ? raw['text'].trim().slice(0, 120) : '';
158
+ if (!text) return null;
159
+ const count = Number(raw == null ? void 0 : raw['count']);
160
+ const last = Number(raw == null ? void 0 : raw['lastSeenAtMs']);
161
+ return {
162
+ id,
163
+ group: typeof (raw == null ? void 0 : raw['group']) === 'string' ? raw['group'] : id,
164
+ text,
165
+ count: Number.isFinite(count) && count > 0 ? Math.floor(count) : 1,
166
+ lastSeenAtMs: Number.isFinite(last) ? last : 0,
167
+ source: 'assist-edit'
168
+ };
169
+ }
170
+
171
+ //# sourceMappingURL=ai-site-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-memory.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 * What Aglyn AI remembers about a site (AGL-3661): short preferences\n * distilled from the edits its owner applied, read into every later AI job\n * for the site, and shown — and clearable — on Setup → Business profile.\n *\n * ── Rules, not a model ────────────────────────────────────────────────────\n *\n * Each preference is recognized by a fixed rule over what the owner asked for\n * and what the applied edit did. No model call: memory has to cost nothing on\n * Free, and a rule says exactly why a line was remembered, so an owner who\n * sees it can recognize the edit it came from. A rule fires only on an edit\n * the owner APPLIED, never on one they were merely shown.\n *\n * ── One per group ─────────────────────────────────────────────────────────\n *\n * A preference belongs to a group, and a group holds one at a time: an owner\n * who asked for a casual tone and later for a formal one prefers the formal\n * one now, so the casual line is forgotten rather than contradicted.\n *\n * Stored at `hosts/{hostId}/aiMemory/{id}`, one document per preference, so\n * clearing one is one client delete (see the rules).\n */\n\n/** The host subcollection the preferences live in. */\nexport const AI_SITE_MEMORY_SUBCOLLECTION = 'aiMemory'\n\n/** At most this many are kept per site; the oldest unrepeated go first. */\nexport const AI_SITE_MEMORY_MAX = 12\n\n/** At most this many are told to a model. */\nexport const AI_SITE_MEMORY_LISTED = 6\n\n/** A preference a rule recognized in one applied edit. */\nexport interface AiSitePreferenceMatch {\n /** Stable per preference: the document id. */\n id: string\n /** Preferences in one group replace each other. */\n group: string\n /** What a model and the owner read. */\n text: string\n}\n\n/** A stored preference. */\nexport interface AiSitePreference extends AiSitePreferenceMatch {\n /** How many applied edits it was recognized in. */\n count: number\n lastSeenAtMs: number\n /** What it was learned from; only applied Assist edits today. */\n source: 'assist-edit'\n}\n\n/** What an applied edit tells the rules. */\nexport interface AiAppliedEditSignal {\n /** What the owner asked the assistant, in their words. */\n question: string\n /** The applied operations counted by word (`set`, `remove`, `insert`, …). */\n opCounts: Readonly<Record<string, number>>\n}\n\ninterface PreferenceRule extends AiSitePreferenceMatch {\n test: (question: string, ops: Readonly<Record<string, number>>) => boolean\n}\n\nconst has = (pattern: RegExp) => (question: string) => pattern.test(question)\n\n/** A section kind an owner removes, by the words they ask for it with. */\nconst REMOVABLE_SECTIONS: ReadonlyArray<{ id: string; words: string; label: string }> = [\n { id: 'testimonials', words: 'testimonials?|reviews?|quotes?', label: 'testimonial and review sections' },\n { id: 'faq', words: 'faqs?|questions section', label: 'FAQ sections' },\n { id: 'pricing', words: 'pric(?:es|ing)|plans section', label: 'pricing sections' },\n { id: 'stats', words: 'stats?|statistics|numbers section|counters?', label: 'stats and number sections' },\n { id: 'logos', words: 'logos?|partners?|clients section|trusted by', label: 'logo and partner strips' },\n { id: 'newsletter', words: 'newsletter|sign-?up section|subscribe', label: 'newsletter sign-up sections' },\n { id: 'team', words: 'team section|our team|staff section', label: 'team sections' },\n { id: 'gallery', words: 'gallery|galleries', label: 'gallery sections' },\n]\n\nconst REMOVE_VERBS = '(?:remove|delete|drop|get rid of|take out|cut|lose|hide)'\n\nconst RULES: readonly PreferenceRule[] = [\n {\n id: 'tone-casual',\n group: 'tone',\n text: 'Prefers a casual, friendly tone',\n test: has(/\\b(casual|friendl(?:y|ier)|relaxed|less formal|warmer|conversational|chill)\\b/i),\n },\n {\n id: 'tone-formal',\n group: 'tone',\n text: 'Prefers a formal, professional tone',\n test: has(/\\b(more (?:formal|professional|serious)|less casual|formal tone|professional tone)\\b/i),\n },\n {\n id: 'copy-short',\n group: 'length',\n text: 'Prefers short, concise copy',\n test: has(/\\b(shorter|concise|less (?:text|copy|wordy)|trim|tighten|too long|fewer words|brief(?:er)?)\\b/i),\n },\n {\n id: 'copy-detailed',\n group: 'length',\n text: 'Prefers detailed copy',\n test: has(/\\b(more detail(?:ed)?|longer|expand|elaborate|too short|say more)\\b/i),\n },\n {\n id: 'no-emoji',\n group: 'emoji',\n text: 'Does not want emoji in copy',\n test: has(/\\b(no|remove|without|drop|delete) (?:the )?emojis?\\b/i),\n },\n ...REMOVABLE_SECTIONS.map(\n (section): PreferenceRule => ({\n id: `removes-${section.id}`,\n group: `removes-${section.id}`,\n text: `Removes ${section.label}`,\n test: (question, ops) =>\n (ops['remove'] ?? 0) > 0 &&\n new RegExp(`\\\\b${REMOVE_VERBS}\\\\b[^.?!]{0,40}\\\\b(?:${section.words})\\\\b`, 'i').test(question),\n }),\n ),\n]\n\n/**\n * The preferences one applied edit shows (AGL-3661): each rule that fires, at\n * most one per group — the first, as the rules are ordered.\n */\nexport function aiPreferencesFromEdit(signal: AiAppliedEditSignal): AiSitePreferenceMatch[] {\n const question = (signal.question ?? '').slice(0, 2_000)\n if (!question.trim()) return []\n const ops = signal.opCounts ?? {}\n const applied = Object.values(ops).some((count) => count > 0)\n if (!applied) return []\n const seen = new Set<string>()\n const matches: AiSitePreferenceMatch[] = []\n for (const rule of RULES) {\n if (seen.has(rule.group) || !rule.test(question, ops)) continue\n seen.add(rule.group)\n matches.push({ id: rule.id, group: rule.group, text: rule.text })\n }\n return matches\n}\n\n/**\n * The preferences a model is told, strongest first: the most often repeated,\n * then the most recent. Capped at {@link AI_SITE_MEMORY_LISTED}.\n */\nexport function aiListedPreferences(preferences: readonly AiSitePreference[]): string[] {\n return [...preferences]\n .sort((a, b) => b.count - a.count || b.lastSeenAtMs - a.lastSeenAtMs)\n .slice(0, AI_SITE_MEMORY_LISTED)\n .map((preference) => preference.text)\n}\n\n/** A stored row as a preference; `null` for one this version cannot read. */\nexport function aiSitePreferenceOf(id: string, raw: Record<string, unknown> | undefined): AiSitePreference | null {\n const text = typeof raw?.['text'] === 'string' ? raw['text'].trim().slice(0, 120) : ''\n if (!text) return null\n const count = Number(raw?.['count'])\n const last = Number(raw?.['lastSeenAtMs'])\n return {\n id,\n group: typeof raw?.['group'] === 'string' ? raw['group'] : id,\n text,\n count: Number.isFinite(count) && count > 0 ? Math.floor(count) : 1,\n lastSeenAtMs: Number.isFinite(last) ? last : 0,\n source: 'assist-edit',\n }\n}\n"],"names":["AI_SITE_MEMORY_SUBCOLLECTION","AI_SITE_MEMORY_MAX","AI_SITE_MEMORY_LISTED","has","pattern","question","test","REMOVABLE_SECTIONS","id","words","label","REMOVE_VERBS","RULES","group","text","map","section","ops","RegExp","aiPreferencesFromEdit","signal","slice","trim","opCounts","applied","Object","values","some","count","seen","Set","matches","rule","add","push","aiListedPreferences","preferences","sort","a","b","lastSeenAtMs","preference","aiSitePreferenceOf","raw","Number","last","isFinite","Math","floor","source"],"mappings":"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"}
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ import { type AiSiteKind } from './ai-site-kinds';
17
18
  import { type AiSiteJobInputs, type AiSiteSubmissions } from './ai-site-job';
18
19
  /**
19
20
  * The guided start (AGL-2918): the questions a person answers on a site they
@@ -80,6 +81,12 @@ export interface AiSiteStartAnswers {
80
81
  audience: string;
81
82
  /** The example they liked, by {@link AiSiteStartExample.id}; `null` for none. */
82
83
  example: string | null;
84
+ /**
85
+ * The kind of site they picked (AGL-3660), by its `AI_SITE_KINDS` id;
86
+ * `null` until they pick one, when the kind their first answer suggests is
87
+ * the one used ({@link aiSiteStartKind}).
88
+ */
89
+ kind: string | null;
83
90
  /** How many pages to plan. */
84
91
  pages: number;
85
92
  /** Where the contact form's submissions go. */
@@ -95,6 +102,8 @@ export interface AiSiteStartAnswers {
95
102
  * thing a person should have to choose.
96
103
  */
97
104
  export declare const AI_SITE_START_ANSWERS: AiSiteStartAnswers;
105
+ /** The kind of site the answers pick, else the one what the site is for suggests. */
106
+ export declare function aiSiteStartKind(answers: Pick<AiSiteStartAnswers, 'kind' | 'siteType'>): AiSiteKind;
98
107
  /** The example an answer names, or `null` when it names none this list knows. */
99
108
  export declare function aiSiteStartExample(answers: AiSiteStartAnswers): AiSiteStartExample | null;
100
109
  /**
@@ -126,7 +135,7 @@ export declare function aiSiteStartBrief(answers: AiSiteStartAnswers): string;
126
135
  */
127
136
  export declare function aiSiteStartInputs(answers: AiSiteStartAnswers): Omit<AiSiteJobInputs, 'batchId'>;
128
137
  /** The site inputs a guided start's answers become, by name: what a summary carries back (AGL-3596). */
129
- export declare const AI_SITE_START_INPUT_KEYS: readonly ["businessType", "audience", "starter", "pages", "businessName", "city", "brand", "submissions", "welcomeEmail"];
138
+ export declare const AI_SITE_START_INPUT_KEYS: readonly ["businessType", "audience", "starter", "siteKind", "pages", "businessName", "city", "brand", "submissions", "welcomeEmail"];
130
139
  /** A site job's scalar inputs, and nothing else its inputs carry; `null` when it has none. */
131
140
  export declare function aiSiteStartInputsOf(inputs: Readonly<Record<string, unknown>> | null | undefined): Record<string, unknown> | null;
132
141
  /**
@@ -13,7 +13,8 @@
13
13
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
- */ import { AI_SITE_INPUT_MAX_CHARS, AI_SITE_SUBMISSIONS, aiSitePagesBand } from "./ai-site-job.js";
16
+ */ import { aiSiteKind, aiSiteKindFor } from "./ai-site-kinds.js";
17
+ import { AI_SITE_INPUT_MAX_CHARS, AI_SITE_SUBMISSIONS, aiSitePagesBand } from "./ai-site-job.js";
17
18
  /**
18
19
  * The examples the start offers, in the order they are shown. The platform's
19
20
  * own starter sites: a person who has no words for what they want has seen
@@ -74,10 +75,15 @@
74
75
  siteType: '',
75
76
  audience: '',
76
77
  example: null,
78
+ kind: null,
77
79
  pages: 5,
78
80
  submissions: 'inbox',
79
81
  welcomeEmail: true
80
82
  };
83
+ /** The kind of site the answers pick, else the one what the site is for suggests. */ export function aiSiteStartKind(answers) {
84
+ var _aiSiteKind;
85
+ return (_aiSiteKind = aiSiteKind(answers.kind)) != null ? _aiSiteKind : aiSiteKindFor(answers.siteType);
86
+ }
81
87
  /** The example an answer names, or `null` when it names none this list knows. */ export function aiSiteStartExample(answers) {
82
88
  var _AI_SITE_START_EXAMPLES_find;
83
89
  if (!answers.example) return null;
@@ -124,6 +130,8 @@
124
130
  if (example) {
125
131
  sentences.push(`Follow the shape of the ${example.label} starter: ${example.blurb.toLowerCase()}.`);
126
132
  }
133
+ // The kind they picked, or the one their answer suggests (AGL-3660).
134
+ sentences.push(`Make it a ${aiSiteStartKind(answers).label.toLowerCase()} site.`);
127
135
  sentences.push('Plan a page for each thing a visitor comes to do, and a contact form on the page that asks to be contacted.');
128
136
  if (audience) {
129
137
  // Said of the FORM as well as of the copy: who fills a form in is what
@@ -148,10 +156,12 @@
148
156
  */ export function aiSiteStartInputs(answers) {
149
157
  var _ref;
150
158
  const example = aiSiteStartExample(answers);
159
+ const kind = aiSiteStartKind(answers);
151
160
  return {
152
161
  businessType: answers.siteType.trim(),
153
162
  audience: answers.audience.trim(),
154
- starter: (_ref = example == null ? void 0 : example.id) != null ? _ref : '',
163
+ starter: (_ref = example == null ? void 0 : example.id) != null ? _ref : kind.starter,
164
+ siteKind: kind.id,
155
165
  pages: answers.pages,
156
166
  businessName: '',
157
167
  city: '',
@@ -164,6 +174,7 @@
164
174
  'businessType',
165
175
  'audience',
166
176
  'starter',
177
+ 'siteKind',
167
178
  'pages',
168
179
  'businessName',
169
180
  'city',
@@ -187,6 +198,8 @@
187
198
  * questions no longer offer goes back to its default — and held to the
188
199
  * workspace's page band. `null` when the inputs say no kind of site at all.
189
200
  */ export function aiSiteStartAnswersFromInputs(inputs, options = {}) {
201
+ var _ref;
202
+ var _aiSiteKind;
190
203
  const text = (key)=>typeof (inputs == null ? void 0 : inputs[key]) === 'string' ? inputs[key].trim() : '';
191
204
  const siteType = text('businessType');
192
205
  if (!siteType) return null;
@@ -198,6 +211,7 @@
198
211
  siteType,
199
212
  audience: text('audience'),
200
213
  example: AI_SITE_START_EXAMPLES.some((entry)=>entry.id === starter) ? starter : null,
214
+ kind: (_ref = (_aiSiteKind = aiSiteKind(text('siteKind'))) == null ? void 0 : _aiSiteKind.id) != null ? _ref : null,
201
215
  pages: Math.min(band.max, Math.max(band.min, pages)),
202
216
  submissions: AI_SITE_SUBMISSIONS.includes(submissions) ? submissions : AI_SITE_START_ANSWERS.submissions,
203
217
  welcomeEmail: options.freeTaste ? false : (inputs == null ? void 0 : inputs['welcomeEmail']) !== false