@aglyn/plugins-ai 1.0.0-beta.225 → 1.0.0-beta.227

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 (161) hide show
  1. package/README.md +2 -1
  2. package/package.json +17 -17
  3. package/src/lib/billing/ai-month-meter.js +5 -3
  4. package/src/lib/billing/ai-month-meter.js.map +1 -1
  5. package/src/lib/billing/ai-overage-ledger.js +3 -1
  6. package/src/lib/billing/ai-overage-ledger.js.map +1 -1
  7. package/src/lib/components/ai-brief-dialog.component.d.ts +6 -2
  8. package/src/lib/components/ai-brief-dialog.component.js +35 -17
  9. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  10. package/src/lib/components/ai-describe-automation.component.js +17 -2
  11. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  12. package/src/lib/components/ai-insight-dialog.component.js +24 -2
  13. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  14. package/src/lib/components/ai-job-events.d.ts +14 -0
  15. package/src/lib/components/ai-job-events.js +76 -0
  16. package/src/lib/components/ai-job-events.js.map +1 -0
  17. package/src/lib/components/ai-job-follow.component.d.ts +49 -0
  18. package/src/lib/components/ai-job-follow.component.js +210 -0
  19. package/src/lib/components/ai-job-follow.component.js.map +1 -0
  20. package/src/lib/components/ai-job-links.d.ts +42 -0
  21. package/src/lib/components/ai-job-links.js +140 -0
  22. package/src/lib/components/ai-job-links.js.map +1 -0
  23. package/src/lib/components/ai-job-plan.component.d.ts +13 -6
  24. package/src/lib/components/ai-job-plan.component.js +29 -3
  25. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  26. package/src/lib/components/ai-job-requests.d.ts +24 -0
  27. package/src/lib/components/ai-job-requests.js +76 -0
  28. package/src/lib/components/ai-job-requests.js.map +1 -0
  29. package/src/lib/components/ai-jobs-indicator.component.d.ts +25 -0
  30. package/src/lib/components/ai-jobs-indicator.component.js +80 -0
  31. package/src/lib/components/ai-jobs-indicator.component.js.map +1 -0
  32. package/src/lib/components/ai-jobs-store.d.ts +81 -0
  33. package/src/lib/components/ai-jobs-store.js +218 -0
  34. package/src/lib/components/ai-jobs-store.js.map +1 -0
  35. package/src/lib/components/ai-page-brief-dialog.component.d.ts +3 -3
  36. package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
  37. package/src/lib/components/ai-site-build-page.component.d.ts +23 -0
  38. package/src/lib/components/ai-site-build-page.component.js +377 -0
  39. package/src/lib/components/ai-site-build-page.component.js.map +1 -0
  40. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  41. package/src/lib/components/ai-site-start-card.component.js +522 -130
  42. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  43. package/src/lib/components/ai-site-starter-fallback.component.d.ts +24 -0
  44. package/src/lib/components/ai-site-starter-fallback.component.js +112 -0
  45. package/src/lib/components/ai-site-starter-fallback.component.js.map +1 -0
  46. package/src/lib/components/assist-jobs-drawer.component.d.ts +18 -20
  47. package/src/lib/components/assist-jobs-drawer.component.js +152 -172
  48. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  49. package/src/lib/components/assist-panel.component.js +62 -6
  50. package/src/lib/components/assist-panel.component.js.map +1 -1
  51. package/src/lib/components/staff-ai-credit-return.component.d.ts +55 -0
  52. package/src/lib/components/staff-ai-credit-return.component.js +346 -0
  53. package/src/lib/components/staff-ai-credit-return.component.js.map +1 -0
  54. package/src/lib/components/staff-org-ai-card.component.js +364 -279
  55. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  56. package/src/lib/components/staff-user-ai-usage-card.component.d.ts +4 -0
  57. package/src/lib/components/staff-user-ai-usage-card.component.js +83 -28
  58. package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
  59. package/src/lib/components/use-ai-job-run.js +5 -18
  60. package/src/lib/components/use-ai-job-run.js.map +1 -1
  61. package/src/lib/jobs/ai-job-auto-confirm.d.ts +52 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.js +62 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -0
  64. package/src/lib/jobs/ai-job-plan-step.d.ts +33 -2
  65. package/src/lib/jobs/ai-job-plan-step.js +161 -17
  66. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-site-step.d.ts +7 -0
  68. package/src/lib/jobs/ai-job-site-step.js +34 -4
  69. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  70. package/src/lib/jobs/ai-job-text-step.d.ts +7 -0
  71. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  72. package/src/lib/jobs/ai-jobs-notify.d.ts +37 -0
  73. package/src/lib/jobs/ai-jobs-notify.js +51 -0
  74. package/src/lib/jobs/ai-jobs-notify.js.map +1 -0
  75. package/src/lib/jobs/ai-jobs.d.ts +31 -4
  76. package/src/lib/jobs/ai-jobs.js +196 -32
  77. package/src/lib/jobs/ai-jobs.js.map +1 -1
  78. package/src/lib/model/ai-job-activity.d.ts +112 -0
  79. package/src/lib/model/ai-job-activity.js +144 -0
  80. package/src/lib/model/ai-job-activity.js.map +1 -0
  81. package/src/lib/model/ai-job-failure-copy.d.ts +69 -0
  82. package/src/lib/model/ai-job-failure-copy.js +78 -0
  83. package/src/lib/model/ai-job-failure-copy.js.map +1 -0
  84. package/src/lib/model/ai-job-notice.d.ts +61 -0
  85. package/src/lib/model/ai-job-notice.js +112 -0
  86. package/src/lib/model/ai-job-notice.js.map +1 -0
  87. package/src/lib/model/ai-jobs.types.d.ts +26 -0
  88. package/src/lib/model/ai-jobs.types.js +1 -0
  89. package/src/lib/model/ai-jobs.types.js.map +1 -1
  90. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  91. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  92. package/src/lib/model/ai-site-build-progress.d.ts +45 -0
  93. package/src/lib/model/ai-site-build-progress.js +76 -0
  94. package/src/lib/model/ai-site-build-progress.js.map +1 -0
  95. package/src/lib/model/ai-site-inventory.d.ts +9 -0
  96. package/src/lib/model/ai-site-inventory.js +7 -2
  97. package/src/lib/model/ai-site-inventory.js.map +1 -1
  98. package/src/lib/model/ai-site-job.d.ts +93 -2
  99. package/src/lib/model/ai-site-job.js +88 -7
  100. package/src/lib/model/ai-site-job.js.map +1 -1
  101. package/src/lib/model/ai-site-start.d.ts +3 -1
  102. package/src/lib/model/ai-site-start.js +6 -4
  103. package/src/lib/model/ai-site-start.js.map +1 -1
  104. package/src/lib/model/ai-template-subjects.d.ts +4 -3
  105. package/src/lib/model/ai-template-subjects.js +5 -4
  106. package/src/lib/model/ai-template-subjects.js.map +1 -1
  107. package/src/lib/plan-entitlements.d.ts +3 -3
  108. package/src/lib/plan-entitlements.js +3 -3
  109. package/src/lib/plan-entitlements.js.map +1 -1
  110. package/src/lib/plugin.js +28 -0
  111. package/src/lib/plugin.js.map +1 -1
  112. package/src/lib/runtime/ai-abuse-guards.d.ts +9 -6
  113. package/src/lib/runtime/ai-abuse-guards.js +10 -7
  114. package/src/lib/runtime/ai-abuse-guards.js.map +1 -1
  115. package/src/lib/runtime/ai-doctrine-validators.js +31 -4
  116. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  117. package/src/lib/runtime/ai-gate.d.ts +3 -2
  118. package/src/lib/runtime/ai-gate.js +3 -2
  119. package/src/lib/runtime/ai-gate.js.map +1 -1
  120. package/src/lib/runtime/ai-palette.generated.js +12 -4
  121. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  122. package/src/lib/runtime/site-inventory.d.ts +16 -1
  123. package/src/lib/runtime/site-inventory.js +43 -3
  124. package/src/lib/runtime/site-inventory.js.map +1 -1
  125. package/src/lib/server/ai-admin-credits.d.ts +21 -0
  126. package/src/lib/server/ai-admin-credits.js +213 -0
  127. package/src/lib/server/ai-admin-credits.js.map +1 -0
  128. package/src/lib/server/ai-admin-org.js +12 -5
  129. package/src/lib/server/ai-admin-org.js.map +1 -1
  130. package/src/lib/server/ai-admin-signals.js +3 -1
  131. package/src/lib/server/ai-admin-signals.js.map +1 -1
  132. package/src/lib/server/ai-admin-user.js +12 -1
  133. package/src/lib/server/ai-admin-user.js.map +1 -1
  134. package/src/lib/server/ai-allotments.js +3 -2
  135. package/src/lib/server/ai-allotments.js.map +1 -1
  136. package/src/lib/server/ai-jobs-route.js +20 -2
  137. package/src/lib/server/ai-jobs-route.js.map +1 -1
  138. package/src/lib/server/assist-chat.js +3 -2
  139. package/src/lib/server/assist-chat.js.map +1 -1
  140. package/src/lib/server/billing-credits.js +4 -3
  141. package/src/lib/server/billing-credits.js.map +1 -1
  142. package/src/lib/server/billing-overage.js +3 -2
  143. package/src/lib/server/billing-overage.js.map +1 -1
  144. package/src/lib/server.js +11 -0
  145. package/src/lib/server.js.map +1 -1
  146. package/src/lib/usage/assist-credit-returns-write.d.ts +75 -0
  147. package/src/lib/usage/assist-credit-returns-write.js +150 -0
  148. package/src/lib/usage/assist-credit-returns-write.js.map +1 -0
  149. package/src/lib/usage/assist-credit-returns.d.ts +68 -0
  150. package/src/lib/usage/assist-credit-returns.js +79 -0
  151. package/src/lib/usage/assist-credit-returns.js.map +1 -0
  152. package/src/lib/usage/assist-free-taste.js +6 -3
  153. package/src/lib/usage/assist-free-taste.js.map +1 -1
  154. package/src/lib/usage/assist-job-refund.d.ts +100 -0
  155. package/src/lib/usage/assist-job-refund.js +109 -0
  156. package/src/lib/usage/assist-job-refund.js.map +1 -0
  157. package/src/lib/usage/assist-usage.js +5 -2
  158. package/src/lib/usage/assist-usage.js.map +1 -1
  159. package/src/lib/usage/staff-org-ai.d.ts +31 -1
  160. package/src/lib/usage/staff-org-ai.js +19 -2
  161. package/src/lib/usage/staff-org-ai.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-start-card.component.tsx"],"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'use client'\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport type { ConsoleHostFirstRunZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n AppBar,\n Box,\n Button,\n Chip,\n Container,\n Dialog,\n DialogActions,\n DialogContent,\n Divider,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Toolbar,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_SITE_PAGES,\n AI_SITE_SUBMISSION_CHOICES,\n aiSiteCreditEstimate,\n type AiSiteSubmissions,\n} from '../model/ai-site-job'\nimport {\n AI_SITE_START_ANSWERS,\n AI_SITE_START_EXAMPLES,\n AI_SITE_START_TYPES,\n aiSiteStartBrief,\n aiSiteStartInputs,\n aiSiteStartRefusal,\n type AiSiteStartAnswers,\n} from '../model/ai-site-start'\n\n/**\n * The guided start (AGL-2918), on the `hostFirstRun` zone of the page a newly\n * created site lands on: a few questions, and the site scaffold they become.\n *\n * ── A full screen dialog, and therefore three ways out ───────────────────\n *\n * The questions take the whole screen, which is the only presentation that\n * gets them read — but a surface that takes the screen and cannot be left is\n * a funnel, so leaving is drawn first and works three ways:\n *\n * - the close control at the start of the app bar,\n * - \"Skip and start blank\" beside it, at the end of the same bar,\n * - Escape, which `Dialog` reports through `onClose`.\n *\n * All three are the zone's own `startBlank` and nothing else: no job, no\n * draft, no record of a site half begun. Dismissing is LEAVING, not a pause\n * that could be returned to — the person keeps the blank site they already\n * have, on the page under the dialog, and the site does not ask again.\n *\n * The bar carrying them is the dialog's own chrome rather than part of its\n * body, so `scroll=\"paper\"` keeps it in place while the questions scroll: the\n * way out cannot be scrolled off, and it is never disabled — least of all\n * while a request is in flight, which is the moment a person most wants it.\n *\n * Nothing is focused ahead of the dialog's own frame, so the first thing a\n * keyboard reaches is the close control rather than the first question.\n *\n * ── It is absent until the route has answered ────────────────────────────\n *\n * The dialog covers the page, so being drawn on a guess is worse here than it\n * is for a card: it would be a takeover every flag-off workspace watches\n * disappear. Nothing renders at all — no `Dialog`, no portal — until the jobs\n * route has admitted this workspace, and a 404, a 403 or an unreachable route\n * is this widget staying absent and the ordinary blank page being all there\n * is. That the widget is mounted is itself the last of a stack of gates: the\n * shell held the plan's `aiGenerative` entitlement, the site's AI switch and\n * the reader's `ai.generate` first. A lockdown is the start door's to say,\n * here, in its own words.\n *\n * ── It asks, it does not build ───────────────────────────────────────────\n *\n * Confirming starts a `site` job, which PLANS first: the pages, their\n * addresses, the navigation, the layout, the contact form and a palette, with\n * an estimated cost beside the button that confirms it. Nothing is built\n * until the person confirms that plan in AI jobs, and everything it then\n * builds is an unpublished draft.\n */\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\n/** Labels the dialog for a reader, from its own heading. */\nconst TITLE_ID = 'ai-site-start-title'\n\nexport function AiSiteStartCard({\n hostId,\n orgId,\n startBlank,\n}: ConsoleHostFirstRunZoneProps) {\n const { data: user } = useUser()\n // Held in a ref so a request reads WHO is signed in, never the identity of\n // the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [answers, setAnswers] = useState<AiSiteStartAnswers>(AI_SITE_START_ANSWERS)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const [started, setStarted] = useState(false)\n\n useEffect(() => {\n if (!orgId || !uid) return\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`,\n )\n if (active) {\n setVerdict(response.status === 404 || response.status === 403 ? 'hidden' : 'ready')\n }\n } catch {\n if (active) setVerdict('hidden')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid])\n\n const answer = useCallback(\n (patch: Partial<AiSiteStartAnswers>) =>\n setAnswers((current) => ({ ...current, ...patch })),\n [],\n )\n\n const refusal = aiSiteStartRefusal(answers)\n\n const plan = useCallback(async () => {\n if (!orgId || aiSiteStartRefusal(answers)) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'site',\n brief: aiSiteStartBrief(answers),\n inputs: aiSiteStartInputs(answers),\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'The site could not be started. Try again.'),\n )\n return\n }\n setStarted(true)\n } catch {\n setNotice('The site could not be started. Try again.')\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, answers])\n\n if (verdict !== 'ready') return null\n\n const estimate = aiSiteCreditEstimate(answers.pages, {\n welcomeEmail: answers.welcomeEmail,\n })\n\n return (\n <Dialog open fullScreen onClose={startBlank} aria-labelledby={TITLE_ID}>\n {/*\n The way out, before anything it is a way out of. On a narrow screen it\n is the HEADING that gives way — the bar's one elastic element, so the\n exit keeps its words at every width rather than collapsing into an\n icon somebody has to recognize.\n\n Shared app-bar treatment (AGL-704) — see the console's\n secondary-app-bar; enableColorOnDark is required or AppBar substitutes\n its own dark-mode colour.\n */}\n <AppBar position=\"relative\" color=\"surface\" enableColorOnDark>\n <Toolbar>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={startBlank}\n aria-label=\"Close the guided start\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n <Typography\n id={TITLE_ID}\n variant=\"h6\"\n component=\"div\"\n noWrap\n sx={{ textOverflow: 'ellipsis', ml: 2, flex: 1 }}\n >\n {'Start this site with AI'}\n </Typography>\n <Divider sx={{ height: 28, m: 0.5 }} orientation=\"vertical\" />\n <Button color=\"inherit\" onClick={startBlank}>\n {started ? 'Close' : 'Skip and start blank'}\n </Button>\n </Toolbar>\n </AppBar>\n <DialogContent>\n {/* A line of questions is read at a column's width, not a screen's. */}\n <Container maxWidth=\"md\" disableGutters sx={{ py: 2 }}>\n <Stack spacing={3}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Answer a few questions and AI plans the whole site — its pages, its ' +\n 'navigation, its layout and a contact form. Nothing is built until you ' +\n 'confirm the plan, and everything it builds is an unpublished draft.'}\n </Typography>\n {notice && <Alert severity=\"info\">{notice}</Alert>}\n {started ? (\n <Alert severity=\"success\">\n {'Your site is being planned. Open AI jobs in the Assist panel to read the plan ' +\n 'and confirm it — nothing is built, and nothing is published, until you do.'}\n </Alert>\n ) : (\n <>\n <Box>\n <TextField\n fullWidth\n label=\"What kind of site are you creating?\"\n placeholder=\"a neighborhood dog groomer that takes bookings\"\n value={answers.siteType}\n onChange={(event) => answer({ siteType: event.target.value })}\n />\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', mt: 1, rowGap: 1 }}>\n {AI_SITE_START_TYPES.map((type) => (\n <Chip\n key={type}\n label={type}\n size=\"small\"\n variant={answers.siteType === type ? 'filled' : 'outlined'}\n onClick={() => answer({ siteType: type })}\n />\n ))}\n </Stack>\n </Box>\n <TextField\n fullWidth\n label=\"Who is it for?\"\n placeholder=\"local dog owners who want a regular groom booked online\"\n value={answers.audience}\n onChange={(event) => answer({ audience: event.target.value })}\n helperText=\"Optional. It narrows who the pages are written for.\"\n />\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom>\n {'Which of these do you like?'}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {'Optional. Picking one steers the shape of the site, not its words.'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n {AI_SITE_START_EXAMPLES.map((example) => (\n <Chip\n key={example.id}\n label={`${example.label} — ${example.blurb}`}\n variant={answers.example === example.id ? 'filled' : 'outlined'}\n onClick={() =>\n answer({\n example: answers.example === example.id ? null : example.id,\n })\n }\n />\n ))}\n </Stack>\n </Box>\n {/*\n The one setting a new site owner has to make, asked where\n they are already answering: a contact form nobody routed\n files its messages somewhere the person has not looked.\n */}\n <TextField\n select\n fullWidth\n label=\"Where do form submissions go?\"\n value={answers.submissions}\n onChange={(event) =>\n answer({ submissions: event.target.value as AiSiteSubmissions })\n }\n helperText=\"You can change this on the form itself afterwards.\"\n >\n {AI_SITE_SUBMISSION_CHOICES.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {`${option.label} — ${option.blurb}`}\n </MenuItem>\n ))}\n </TextField>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n select\n label=\"Pages\"\n value={answers.pages}\n onChange={(event) => answer({ pages: Number(event.target.value) })}\n sx={{ minWidth: 160 }}\n >\n {Array.from(\n { length: AI_SITE_PAGES.max - AI_SITE_PAGES.min + 1 },\n (_, index) => AI_SITE_PAGES.min + index,\n ).map((count) => (\n <MenuItem key={count} value={count}>\n {count}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n label=\"Welcome email\"\n value={answers.welcomeEmail ? 'yes' : 'no'}\n onChange={(event) => answer({ welcomeEmail: event.target.value === 'yes' })}\n sx={{ minWidth: 160 }}\n >\n <MenuItem value=\"yes\">{'Draft one'}</MenuItem>\n <MenuItem value=\"no\">{'No'}</MenuItem>\n </TextField>\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`About ${estimate.toLocaleString('en-US')} credits, estimated. What it really ` +\n 'costs is what each step spends, and you can watch that add up while it runs.'}\n </Typography>\n </>\n )}\n </Stack>\n </Container>\n </DialogContent>\n {!started && (\n /* Wraps, because the reason and the button together are wider than a\n phone: on one line the reason would push the button off the edge. */\n <DialogActions sx={{ px: 3, py: 2, flexWrap: 'wrap', rowGap: 1 }}>\n {refusal && (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mr: 'auto' }}>\n {refusal}\n </Typography>\n )}\n <Button variant=\"contained\" disabled={busy || Boolean(refusal)} onClick={plan}>\n {busy ? 'Starting…' : 'Plan my site'}\n </Button>\n </DialogActions>\n )}\n </Dialog>\n )\n}\n\nexport default AiSiteStartCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","ICON_VARIANT_CLOSE","MdiIcon","authorizedFetch","useUser","Alert","AppBar","Box","Button","Chip","Container","Dialog","DialogActions","DialogContent","Divider","IconButton","MenuItem","Stack","TextField","Toolbar","Typography","useCallback","useEffect","useRef","useState","AI_SITE_PAGES","AI_SITE_SUBMISSION_CHOICES","aiSiteCreditEstimate","AI_SITE_START_ANSWERS","AI_SITE_START_EXAMPLES","AI_SITE_START_TYPES","aiSiteStartBrief","aiSiteStartInputs","aiSiteStartRefusal","TITLE_ID","AiSiteStartCard","hostId","orgId","startBlank","data","user","userRef","current","uid","verdict","setVerdict","answers","setAnswers","busy","setBusy","notice","setNotice","started","setStarted","active","response","encodeURIComponent","status","answer","patch","refusal","plan","method","headers","body","JSON","stringify","kind","brief","inputs","payload","json","catch","ok","locked","String","error","estimate","pages","welcomeEmail","open","fullScreen","onClose","aria-labelledby","position","color","enableColorOnDark","edge","onClick","aria-label","path","id","variant","component","noWrap","sx","textOverflow","ml","flex","height","m","orientation","maxWidth","disableGutters","py","spacing","severity","fullWidth","label","placeholder","value","siteType","onChange","event","target","direction","flexWrap","mt","rowGap","map","type","size","audience","helperText","gutterBottom","example","blurb","select","submissions","option","xs","sm","Number","minWidth","Array","from","length","max","min","_","index","count","toLocaleString","px","mr","disabled","Boolean"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-start-card.component.tsx"],"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'use client'\n\nimport { lockdownRefusalText, parseLockdownRefusal, PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport { trackEvent } from '@aglyn/aglyn/app-utils/analytics-events'\nimport type { ConsoleHostFirstRunZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport {\n mdiCheckCircle,\n mdiCreation,\n mdiInformationOutline,\n mdiPageLayoutHeaderFooter,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n AppBar,\n Box,\n Button,\n Card,\n CardActionArea,\n Chip,\n CircularProgress,\n Container,\n Dialog,\n DialogActions,\n DialogContent,\n IconButton,\n MenuItem,\n Stack,\n Step,\n StepLabel,\n Stepper,\n TextField,\n Toolbar,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { alpha, type Theme } from '@mui/material/styles'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useId, useRef, useState, type ReactNode } from 'react'\nimport {\n AI_JOB_AUTO_CONFIRM_INPUT,\n AI_SITE_FREE_PAGES,\n AI_SITE_FREE_PAGES_NOTE,\n AI_SITE_SUBMISSION_CHOICES,\n aiFreeSiteCreditEstimate,\n aiSiteCreditEstimate,\n aiSitePagesBand,\n type AiSiteSubmissions,\n} from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport {\n AI_SITE_START_ANSWERS,\n AI_SITE_START_EXAMPLES,\n AI_SITE_START_TYPES,\n aiSiteStartBrief,\n aiSiteStartInputs,\n aiSiteStartRefusal,\n type AiSiteStartAnswers,\n} from '../model/ai-site-start'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { aiSiteBuildHref } from './ai-job-links'\nimport { publishAiJob } from './ai-jobs-store'\n\n/**\n * The guided start (AGL-2918), on the `hostFirstRun` zone of the page a newly\n * created site lands on: a choice, a few questions, and the site scaffold they\n * become.\n *\n * ── Two steps (AGL-3594) ─────────────────────────────────────────────────\n *\n * Step 1 asks how to start, as two equal cards: the starter site — a\n * ready-made home page with a header and footer — or AI. The starter card is\n * the zone's `startBlank`, which gives a site born for the guided start the\n * starter every other new site is born with. The AI card moves to step 2, the\n * questions, which have a Back to step 1.\n *\n * ── A full screen dialog, and therefore three ways out ───────────────────\n *\n * The questions take the whole screen, which is the only presentation that\n * gets them read — but a surface that takes the screen and cannot be left is\n * a funnel, so leaving is drawn first and works three ways on both steps:\n *\n * - the close control at the start of the header,\n * - \"Skip\" at the end of it, named \"Skip and start blank\",\n * - Escape, which `Dialog` reports through `onClose`.\n *\n * Before a job is started all three are the zone's own `startBlank`: the\n * starter site, and no job, no draft, no record of a site half begun. After,\n * they close through `leave`, and the job builds the site.\n *\n * The header is the dialog's own chrome rather than part of its body, so the\n * way out cannot be scrolled off, and it is never disabled — least of all\n * while a request is in flight, which is the moment a person most wants it.\n * Nothing is focused ahead of the dialog's own frame, so the first thing a\n * keyboard reaches is the close control.\n *\n * ── It is absent until the route has answered ────────────────────────────\n *\n * The dialog covers the page, so being drawn on a guess is worse here than it\n * is for a card: it would be a takeover every flag-off workspace watches\n * disappear. Nothing renders at all — no `Dialog`, no portal — until the jobs\n * route has admitted this workspace, and a 404, a 403 or an unreachable route\n * is this widget staying absent and the ordinary blank page being all there\n * is. That the widget is mounted is itself the last of a stack of gates: the\n * shell held the plan's `aiGenerative` entitlement, the site's AI switch and\n * the reader's `ai.generate` first. A lockdown is the start door's to say,\n * here, in its own words.\n *\n * ── It asks, it does not build ───────────────────────────────────────────\n *\n * \"Plan my site\" starts a `site` job, which PLANS first: the pages, their\n * addresses, the navigation, the layout, the contact form and a palette, with\n * an estimated cost beside the button that confirms it. Nothing is built\n * until the person confirms that plan, and everything it then builds is an\n * unpublished draft.\n *\n * ── It stays with the job (AGL-3593) ─────────────────────────────────────\n *\n * Once the job exists the dialog follows it live — planning, the plan with\n * its Confirm, building, then the draft pages to open — so a person never has\n * to leave it to get their site built. \"Open AI jobs\" follows the job in the\n * Assist panel instead; it leaves the guided start the way every other exit\n * does, because the dialog covers the panel it opens.\n */\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\ntype Step = 'choose' | 'describe'\n\n/** Labels the dialog for a reader, from its own heading. */\nconst TITLE_ID = 'ai-site-start-title'\n\n/** The two steps, as the indicator under the header names them. */\nconst STEP_LABELS = ['Choose', 'Describe'] as const\n\n/** The soft round badge an icon sits in, in its accent's tint. */\nfunction IconBadge({ accent, children }: { accent: 'primary' | 'secondary'; children: ReactNode }) {\n return (\n <Box\n aria-hidden\n sx={(theme: Theme) => ({\n width: theme.spacing(9),\n height: theme.spacing(9),\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n bgcolor: alpha(theme.palette[accent].main, theme.palette.mode === 'dark' ? 0.24 : 0.12),\n color: `${accent}.main`,\n fontSize: theme.spacing(5.5),\n })}\n >\n {children}\n </Box>\n )\n}\n\n/**\n * One way to start, as a card the whole of which is the button. Its accessible\n * name is its title; its description describes it.\n */\nfunction ChoiceCard({\n accent,\n icon,\n iconId,\n title,\n description,\n busy = false,\n onChoose,\n children,\n}: {\n accent: 'primary' | 'secondary'\n icon: string\n /** Names the icon for a reader of the markup; the icon itself is decorative. */\n iconId: string\n title: string\n description: string\n busy?: boolean\n onChoose: () => void\n children?: ReactNode\n}) {\n const id = useId()\n return (\n <Card\n variant=\"outlined\"\n sx={(theme: Theme) => ({\n borderRadius: 2,\n height: '100%',\n transition: theme.transitions.create(['border-color', 'box-shadow', 'transform'], {\n duration: theme.transitions.duration.shorter,\n }),\n '&:hover, &:focus-within': {\n borderColor: `${accent}.main`,\n boxShadow: theme.shadows[4],\n transform: `translateY(-${theme.spacing(0.25)})`,\n },\n '@media (prefers-reduced-motion: reduce)': {\n transition: 'none',\n '&:hover, &:focus-within': { transform: 'none' },\n },\n })}\n >\n <CardActionArea\n onClick={onChoose}\n disabled={busy}\n aria-labelledby={`${id}-title`}\n aria-describedby={`${id}-description`}\n aria-busy={busy || undefined}\n sx={{ height: '100%', p: { xs: 3, sm: 4 } }}\n >\n <Stack spacing={2} sx={{ alignItems: 'center', textAlign: 'center' }}>\n <IconBadge accent={accent}>\n {busy ? (\n <CircularProgress size=\"1em\" color={accent} aria-label={`${title}…`} />\n ) : (\n <MdiIcon path={icon} data-icon={iconId} aria-hidden />\n )}\n </IconBadge>\n <Typography id={`${id}-title`} variant=\"h6\" component=\"h2\">\n {title}\n </Typography>\n <Typography id={`${id}-description`} variant=\"body2\" color=\"text.secondary\">\n {description}\n </Typography>\n {children}\n </Stack>\n </CardActionArea>\n </Card>\n )\n}\n\n/** A small label above a group of questions. */\nfunction SectionLabel({ children }: { children: ReactNode }) {\n return (\n <Typography variant=\"overline\" color=\"text.secondary\" component=\"h2\" sx={{ display: 'block' }}>\n {children}\n </Typography>\n )\n}\n\nexport function AiSiteStartCard({\n hostId,\n orgId,\n orgSlug,\n host,\n startBlank,\n leave,\n}: ConsoleHostFirstRunZoneProps) {\n const { data: user } = useUser()\n const router = useRouter()\n // Held in a ref so a request reads WHO is signed in, never the identity of\n // the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [step, setStep] = useState<Step>('choose')\n const [answers, setAnswers] = useState<AiSiteStartAnswers>(AI_SITE_START_ANSWERS)\n const [busy, setBusy] = useState(false)\n // The starter card's own busy state while `startBlank` runs (AGL-3594).\n const [startingStarter, setStartingStarter] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n /** The site job \"Plan my site\" started, as the create door answered with it. */\n const [started, setStarted] = useState<AiJobSummary | null>(null)\n // The Free taste's page band (AGL-3594), read off the verdict request.\n const [freeTaste, setFreeTaste] = useState(false)\n\n useEffect(() => {\n if (!orgId || !uid) return\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`,\n )\n const payload = response.ok ? await response.json().catch(() => null) : null\n if (active) {\n if (payload?.freeTaste === true) {\n setFreeTaste(true)\n setAnswers((current) => ({ ...current, pages: AI_SITE_FREE_PAGES.max, welcomeEmail: false }))\n }\n setVerdict(response.status === 404 || response.status === 403 ? 'hidden' : 'ready')\n }\n } catch {\n if (active) setVerdict('hidden')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid])\n\n const answer = useCallback(\n (patch: Partial<AiSiteStartAnswers>) =>\n setAnswers((current) => ({ ...current, ...patch })),\n [],\n )\n\n const refusal = aiSiteStartRefusal(answers, { freeTaste })\n const band = aiSitePagesBand(freeTaste)\n\n const plan = useCallback(async () => {\n if (!orgId || aiSiteStartRefusal(answers, { freeTaste })) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'site',\n brief: aiSiteStartBrief(answers),\n // The guided start confirms its own plan (AGL-3594): the build\n // follows the plan with no approval to make.\n inputs: { ...aiSiteStartInputs(answers), [AI_JOB_AUTO_CONFIRM_INPUT]: true },\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'The site could not be started. Try again.'),\n )\n return\n }\n const created = (payload?.job as AiJobSummary | undefined) ?? null\n if (!created) {\n setNotice('The site could not be started. Try again.')\n return\n }\n // The indicator and the launcher count it from the moment it exists.\n publishAiJob(created)\n // Where the next step is (AGL-3594): the site's \"Building your site\"\n // page for this job. The zone closes without the starter first, so the\n // setup page does not ask again behind it.\n const href = aiSiteBuildHref(orgSlug, host, created.id)\n if (href) {\n ;(leave ?? (() => undefined))()\n router.push(href)\n return\n }\n setStarted(created)\n } catch {\n setNotice('The site could not be started. Try again.')\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, answers, freeTaste, orgSlug, host, leave, router])\n\n const chooseStarter = useCallback(() => {\n setStartingStarter(true)\n trackEvent('site_start_choice', { choice: 'starter' })\n startBlank()\n }, [startBlank])\n\n const chooseAi = useCallback(() => {\n trackEvent('site_start_choice', { choice: 'ai' })\n setStep('describe')\n }, [])\n\n if (verdict !== 'ready') return null\n\n // Before a job is started every way out is the blank site, which writes the\n // starter (AGL-3594); after, it only closes — the job builds the site.\n const exit = started ? (leave ?? startBlank) : startBlank\n\n const estimate = freeTaste\n ? aiFreeSiteCreditEstimate(answers.pages)\n : aiSiteCreditEstimate(answers.pages, {\n welcomeEmail: answers.welcomeEmail,\n })\n const estimateText = freeTaste\n ? `Up to about ${estimate.toLocaleString('en-US')} of the ${FREE_AI_TASTE_CREDITS_PER_MONTH} AI credits your Free workspace has each month`\n : `About ${estimate.toLocaleString('en-US')} credits, estimated`\n\n const choosing = step === 'choose' && !started\n\n return (\n <Dialog\n open\n fullScreen\n onClose={exit}\n aria-labelledby={TITLE_ID}\n slotProps={{ paper: { sx: { bgcolor: 'background.paper' } } }}\n >\n {/*\n The way out, before anything it is a way out of: a slim header on the\n paper surface. On a narrow screen it is the TITLE that gives way, so\n the exits keep their words at every width.\n */}\n <AppBar\n position=\"relative\"\n color=\"inherit\"\n elevation={0}\n enableColorOnDark\n sx={{ bgcolor: 'background.paper', borderBottom: 1, borderColor: 'divider' }}\n >\n <Toolbar variant=\"dense\" sx={{ gap: 1 }}>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={exit}\n aria-label=\"Close the guided start\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n <Typography\n id={TITLE_ID}\n variant=\"subtitle1\"\n component=\"div\"\n noWrap\n sx={{ textOverflow: 'ellipsis', flex: 1 }}\n >\n {'Start your site'}\n </Typography>\n <Button color=\"inherit\" onClick={exit} aria-label={started ? 'Close' : 'Skip and start blank'}>\n {started ? 'Close' : 'Skip'}\n </Button>\n </Toolbar>\n </AppBar>\n <DialogContent sx={{ px: { xs: 2, sm: 3 } }}>\n <Container maxWidth={choosing ? 'md' : 'sm'} disableGutters sx={{ py: { xs: 3, sm: 5 } }}>\n <Stack spacing={4}>\n <Stepper\n activeStep={choosing ? 0 : 1}\n aria-label={choosing ? 'Step 1 of 2' : 'Step 2 of 2'}\n sx={{ maxWidth: (theme: Theme) => theme.spacing(40), width: '100%', mx: 'auto' }}\n >\n {STEP_LABELS.map((label) => (\n <Step key={label}>\n <StepLabel>{label}</StepLabel>\n </Step>\n ))}\n </Stepper>\n <Stack spacing={1} sx={{ textAlign: choosing ? 'center' : 'left' }}>\n <Typography variant=\"h4\" component=\"h1\">\n {choosing ? 'How do you want to start?' : 'Tell us about your site'}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {choosing\n ? 'You can change everything later.'\n : `A few answers and ${PLATFORM_BRAND_NAME} AI plans your pages. Nothing is published until you say so.`}\n </Typography>\n </Stack>\n {notice && <Alert severity=\"info\">{notice}</Alert>}\n {choosing ? (\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },\n gap: 3,\n }}\n >\n <ChoiceCard\n accent=\"secondary\"\n icon={mdiPageLayoutHeaderFooter.path}\n iconId=\"page-layout-header-footer\"\n title=\"Start from the starter site\"\n description=\"A ready-made home page with a header, footer and contact form you can edit.\"\n busy={startingStarter}\n onChoose={chooseStarter}\n />\n <ChoiceCard\n accent=\"primary\"\n icon={mdiCreation.path}\n iconId=\"creation\"\n title=\"Start with AI\"\n description={`Answer a few questions and ${PLATFORM_BRAND_NAME} AI plans your pages.`}\n onChoose={chooseAi}\n >\n <Chip size=\"small\" color=\"primary\" label=\"Recommended\" />\n {freeTaste && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Up to ${AI_SITE_FREE_PAGES.max} pages on the Free plan`}\n </Typography>\n )}\n </ChoiceCard>\n </Box>\n ) : started && orgId ? (\n <Card variant=\"outlined\" sx={{ borderRadius: 2, p: { xs: 2, sm: 3 } }}>\n <AiJobFollow\n job={started}\n orgId={orgId}\n orgSlug={orgSlug}\n user={user}\n intro={\n 'Your site is being planned. Review the plan here or in AI jobs, and confirm ' +\n 'it to build — nothing is built, and nothing is published, until you do.'\n }\n // Full screen: AI jobs opens in the panel this dialog covers.\n onOpenJobs={exit}\n />\n </Card>\n ) : (\n <>\n <Stack spacing={2} component=\"section\" aria-label=\"Your business\">\n <SectionLabel>{'Your business'}</SectionLabel>\n <TextField\n fullWidth\n required\n label=\"What kind of site are you creating?\"\n placeholder=\"a neighborhood dog groomer that takes bookings\"\n value={answers.siteType}\n onChange={(event) => answer({ siteType: event.target.value })}\n helperText=\"A few words is enough, or pick one below.\"\n />\n <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>\n {AI_SITE_START_TYPES.map((type) => (\n <Chip\n key={type}\n label={type}\n size=\"small\"\n color={answers.siteType === type ? 'primary' : 'default'}\n variant={answers.siteType === type ? 'filled' : 'outlined'}\n onClick={() => answer({ siteType: type })}\n />\n ))}\n </Box>\n <TextField\n fullWidth\n label=\"Who is it for?\"\n placeholder=\"local dog owners who want a regular groom booked online\"\n value={answers.audience}\n onChange={(event) => answer({ audience: event.target.value })}\n helperText=\"Optional. It narrows who the pages are written for.\"\n />\n </Stack>\n <Stack spacing={2} component=\"section\" aria-label=\"Look & layout\">\n <SectionLabel>{'Look & layout'}</SectionLabel>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Optional. Picking one steers the shape of the site, not its words.'}\n </Typography>\n <Box\n sx={{\n display: 'grid',\n gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },\n gap: 2,\n }}\n >\n {AI_SITE_START_EXAMPLES.map((example) => {\n const selected = answers.example === example.id\n return (\n <Card\n key={example.id}\n variant=\"outlined\"\n sx={(theme: Theme) => ({\n borderRadius: 2,\n borderColor: selected ? 'primary.main' : 'divider',\n bgcolor: selected ? alpha(theme.palette.primary.main, 0.08) : 'transparent',\n })}\n >\n <CardActionArea\n aria-pressed={selected}\n aria-label={`${example.label} — ${example.blurb}`}\n onClick={() => answer({ example: selected ? null : example.id })}\n sx={{ p: 2, height: '100%' }}\n >\n <Stack direction=\"row\" spacing={1.5} sx={{ alignItems: 'flex-start' }}>\n <Box sx={{ flex: 1, minWidth: 0 }}>\n <Typography variant=\"subtitle2\">{example.label}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {example.blurb}\n </Typography>\n </Box>\n {selected && (\n <Box sx={{ color: 'primary.main', display: 'flex' }} aria-hidden>\n <MdiIcon path={mdiCheckCircle.path} />\n </Box>\n )}\n </Stack>\n </CardActionArea>\n </Card>\n )\n })}\n </Box>\n </Stack>\n <Stack spacing={2} component=\"section\" aria-label=\"Details\">\n <SectionLabel>{'Details'}</SectionLabel>\n {/*\n The one setting a new site owner has to make, asked where\n they are already answering: a contact form nobody routed\n files its messages somewhere the person has not looked.\n */}\n <TextField\n select\n fullWidth\n label=\"Where do form submissions go?\"\n value={answers.submissions}\n onChange={(event) =>\n answer({ submissions: event.target.value as AiSiteSubmissions })\n }\n helperText=\"You can change this on the form itself afterwards.\"\n >\n {AI_SITE_SUBMISSION_CHOICES.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {`${option.label} — ${option.blurb}`}\n </MenuItem>\n ))}\n </TextField>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n select\n fullWidth\n label=\"Pages\"\n value={answers.pages}\n onChange={(event) => answer({ pages: Number(event.target.value) })}\n helperText={freeTaste ? AI_SITE_FREE_PAGES_NOTE : undefined}\n >\n {Array.from(\n { length: band.max - band.min + 1 },\n (_, index) => band.min + index,\n ).map((count) => (\n <MenuItem key={count} value={count}>\n {count}\n </MenuItem>\n ))}\n </TextField>\n {!freeTaste && (\n <TextField\n select\n fullWidth\n label=\"Welcome email\"\n value={answers.welcomeEmail ? 'yes' : 'no'}\n onChange={(event) => answer({ welcomeEmail: event.target.value === 'yes' })}\n >\n <MenuItem value=\"yes\">{'Draft one'}</MenuItem>\n <MenuItem value=\"no\">{'No'}</MenuItem>\n </TextField>\n )}\n </Stack>\n </Stack>\n </>\n )}\n </Stack>\n </Container>\n </DialogContent>\n {step === 'describe' && !started && (\n /* The footer stays put while the questions scroll. It wraps, because\n the estimate, the reason and the buttons are wider than a phone. */\n <DialogActions\n sx={{\n px: { xs: 2, sm: 3 },\n py: 2,\n flexWrap: 'wrap',\n rowGap: 1,\n columnGap: 2,\n borderTop: 1,\n borderColor: 'divider',\n bgcolor: 'background.paper',\n }}\n >\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center', mr: 'auto', minWidth: 0 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${estimateText}.`}\n </Typography>\n <Tooltip title=\"What it really costs is what each step spends, and you can watch that add up while it runs.\">\n <Box\n component=\"span\"\n tabIndex={0}\n aria-label=\"How the estimate works\"\n sx={{ display: 'inline-flex', color: 'text.secondary' }}\n >\n <MdiIcon path={mdiInformationOutline.path} fontSize=\"small\" />\n </Box>\n </Tooltip>\n </Stack>\n {refusal && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {refusal}\n </Typography>\n )}\n <Button onClick={() => setStep('choose')}>{'Back'}</Button>\n <Button\n variant=\"contained\"\n size=\"large\"\n disabled={busy || Boolean(refusal)}\n onClick={plan}\n startIcon={<MdiIcon path={mdiCreation.path} />}\n >\n {busy ? 'Starting…' : 'Plan my site'}\n </Button>\n </DialogActions>\n )}\n </Dialog>\n )\n}\n\nexport default AiSiteStartCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","PLATFORM_BRAND_NAME","trackEvent","ICON_VARIANT_CLOSE","mdiCheckCircle","mdiCreation","mdiInformationOutline","mdiPageLayoutHeaderFooter","MdiIcon","authorizedFetch","useUser","Alert","AppBar","Box","Button","Card","CardActionArea","Chip","CircularProgress","Container","Dialog","DialogActions","DialogContent","IconButton","MenuItem","Stack","Step","StepLabel","Stepper","TextField","Toolbar","Tooltip","Typography","alpha","useRouter","useCallback","useEffect","useId","useRef","useState","AI_JOB_AUTO_CONFIRM_INPUT","AI_SITE_FREE_PAGES","AI_SITE_FREE_PAGES_NOTE","AI_SITE_SUBMISSION_CHOICES","aiFreeSiteCreditEstimate","aiSiteCreditEstimate","aiSitePagesBand","FREE_AI_TASTE_CREDITS_PER_MONTH","AI_SITE_START_ANSWERS","AI_SITE_START_EXAMPLES","AI_SITE_START_TYPES","aiSiteStartBrief","aiSiteStartInputs","aiSiteStartRefusal","AiJobFollow","aiSiteBuildHref","publishAiJob","TITLE_ID","STEP_LABELS","IconBadge","accent","children","aria-hidden","sx","theme","width","spacing","height","borderRadius","display","alignItems","justifyContent","bgcolor","palette","main","mode","color","fontSize","ChoiceCard","icon","iconId","title","description","busy","onChoose","id","variant","transition","transitions","create","duration","shorter","borderColor","boxShadow","shadows","transform","onClick","disabled","aria-labelledby","aria-describedby","aria-busy","undefined","p","xs","sm","textAlign","size","aria-label","path","data-icon","component","SectionLabel","AiSiteStartCard","hostId","orgId","orgSlug","host","startBlank","leave","data","user","router","userRef","current","uid","verdict","setVerdict","step","setStep","answers","setAnswers","setBusy","startingStarter","setStartingStarter","notice","setNotice","started","setStarted","freeTaste","setFreeTaste","active","response","encodeURIComponent","payload","ok","json","catch","pages","max","welcomeEmail","status","answer","patch","refusal","band","plan","method","headers","body","JSON","stringify","kind","brief","inputs","locked","String","error","created","job","href","push","chooseStarter","choice","chooseAi","exit","estimate","estimateText","toLocaleString","choosing","open","fullScreen","onClose","slotProps","paper","position","elevation","enableColorOnDark","borderBottom","gap","edge","noWrap","textOverflow","flex","px","maxWidth","disableGutters","py","activeStep","mx","map","label","severity","gridTemplateColumns","intro","onOpenJobs","fullWidth","required","placeholder","value","siteType","onChange","event","target","helperText","flexWrap","type","audience","example","selected","primary","aria-pressed","blurb","direction","minWidth","select","submissions","option","Number","Array","from","length","min","_","index","count","rowGap","columnGap","borderTop","mr","tabIndex","Boolean","startIcon"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { authorizedFetch } from '@aglyn/shared-util-http/authorized-token';
2
+ import type { AiJobSummary } from '../model/ai-jobs.types';
3
+ /**
4
+ * Asks the console for the site's starter (AGL-3594): `POST
5
+ * /api/hosts/starter`, the same route leaving the guided start for a blank
6
+ * site calls. A no-op on a site that has a page already. Reports the
7
+ * starter's published Home as the site's first publish, only when one was
8
+ * written. Never throws.
9
+ */
10
+ export declare function requestAiStarterSite(user: Parameters<typeof authorizedFetch>[0], hostId: string): Promise<'provisioned' | 'unchanged' | 'failed'>;
11
+ /**
12
+ * "Use the starter site instead" (AGL-3594): the one action a guided start
13
+ * that did not work out offers — a site job that failed, was canceled, or
14
+ * stopped for review — while it has built nothing. Draws nothing for any
15
+ * other job. The AI jobs drawer places it on a failed or canceled row; the
16
+ * plan card places it beside Try again when it is handed the signed-in user.
17
+ * The user is a prop rather than read here, so a surface drawn outside the
18
+ * console's Firebase provider still renders.
19
+ */
20
+ export declare function AiSiteStarterFallback({ job, user, }: {
21
+ job: AiJobSummary;
22
+ user: Parameters<typeof authorizedFetch>[0];
23
+ }): JSX.Element | null;
24
+ export default AiSiteStarterFallback;
@@ -0,0 +1,112 @@
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
+ */ 'use client';
17
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
+ import { trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
19
+ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
20
+ import { Button, Typography } from "@mui/material";
21
+ import { useCallback, useRef, useState } from "react";
22
+ import { AI_SITE_STARTER_FALLBACK_LABEL, aiSiteStarterFallbackOffered } from "../model/ai-job-failure-copy.js";
23
+ /**
24
+ * Asks the console for the site's starter (AGL-3594): `POST
25
+ * /api/hosts/starter`, the same route leaving the guided start for a blank
26
+ * site calls. A no-op on a site that has a page already. Reports the
27
+ * starter's published Home as the site's first publish, only when one was
28
+ * written. Never throws.
29
+ */ export async function requestAiStarterSite(user, hostId) {
30
+ try {
31
+ const response = await authorizedFetch(user, '/api/hosts/starter', {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json'
35
+ },
36
+ body: JSON.stringify({
37
+ hostId
38
+ })
39
+ });
40
+ if (!response.ok) return 'failed';
41
+ const payload = await response.json().catch(()=>null);
42
+ if ((payload == null ? void 0 : payload.provisioned) !== true) return 'unchanged';
43
+ trackEvent('site_published', {
44
+ first_publish: true
45
+ });
46
+ return 'provisioned';
47
+ } catch (unused) {
48
+ return 'failed';
49
+ }
50
+ }
51
+ /**
52
+ * "Use the starter site instead" (AGL-3594): the one action a guided start
53
+ * that did not work out offers — a site job that failed, was canceled, or
54
+ * stopped for review — while it has built nothing. Draws nothing for any
55
+ * other job. The AI jobs drawer places it on a failed or canceled row; the
56
+ * plan card places it beside Try again when it is handed the signed-in user.
57
+ * The user is a prop rather than read here, so a surface drawn outside the
58
+ * console's Firebase provider still renders.
59
+ */ export function AiSiteStarterFallback({ job, user }) {
60
+ const userRef = useRef(user);
61
+ userRef.current = user;
62
+ const [state, setState] = useState('idle');
63
+ const use = useCallback(async ()=>{
64
+ if (!job.hostId) return;
65
+ setState('busy');
66
+ setState(await requestAiStarterSite(userRef.current, job.hostId));
67
+ }, [
68
+ job.hostId
69
+ ]);
70
+ if (!aiSiteStarterFallbackOffered(job)) return null;
71
+ if (state === 'provisioned') {
72
+ return /*#__PURE__*/ _jsx(Typography, {
73
+ variant: "caption",
74
+ color: "text.secondary",
75
+ component: "div",
76
+ sx: {
77
+ mt: 1
78
+ },
79
+ children: 'Your site now has the starter home page, live at its address.'
80
+ });
81
+ }
82
+ return /*#__PURE__*/ _jsxs(_Fragment, {
83
+ children: [
84
+ /*#__PURE__*/ _jsx(Button, {
85
+ size: "small",
86
+ variant: "text",
87
+ disabled: state === 'busy',
88
+ onClick: ()=>void use(),
89
+ sx: {
90
+ mt: 1,
91
+ ml: 1
92
+ },
93
+ children: AI_SITE_STARTER_FALLBACK_LABEL
94
+ }),
95
+ state === 'unchanged' && /*#__PURE__*/ _jsx(Typography, {
96
+ variant: "caption",
97
+ color: "text.secondary",
98
+ component: "div",
99
+ children: 'This site already has pages, so the starter was not added.'
100
+ }),
101
+ state === 'failed' && /*#__PURE__*/ _jsx(Typography, {
102
+ variant: "caption",
103
+ color: "error",
104
+ component: "div",
105
+ children: 'The starter site could not be added. Try again.'
106
+ })
107
+ ]
108
+ });
109
+ }
110
+ export default AiSiteStarterFallback;
111
+
112
+ //# sourceMappingURL=ai-site-starter-fallback.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-site-starter-fallback.component.tsx"],"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'use client'\n\nimport { trackEvent } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { Button, Typography } from '@mui/material'\nimport { useCallback, useRef, useState } from 'react'\nimport {\n AI_SITE_STARTER_FALLBACK_LABEL,\n aiSiteStarterFallbackOffered,\n} from '../model/ai-job-failure-copy'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\n\n/**\n * Asks the console for the site's starter (AGL-3594): `POST\n * /api/hosts/starter`, the same route leaving the guided start for a blank\n * site calls. A no-op on a site that has a page already. Reports the\n * starter's published Home as the site's first publish, only when one was\n * written. Never throws.\n */\nexport async function requestAiStarterSite(\n user: Parameters<typeof authorizedFetch>[0],\n hostId: string,\n): Promise<'provisioned' | 'unchanged' | 'failed'> {\n try {\n const response = await authorizedFetch(user, '/api/hosts/starter', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId }),\n })\n if (!response.ok) return 'failed'\n const payload = (await response.json().catch(() => null)) as { provisioned?: boolean } | null\n if (payload?.provisioned !== true) return 'unchanged'\n trackEvent('site_published', { first_publish: true })\n return 'provisioned'\n } catch {\n return 'failed'\n }\n}\n\n/**\n * \"Use the starter site instead\" (AGL-3594): the one action a guided start\n * that did not work out offers — a site job that failed, was canceled, or\n * stopped for review — while it has built nothing. Draws nothing for any\n * other job. The AI jobs drawer places it on a failed or canceled row; the\n * plan card places it beside Try again when it is handed the signed-in user.\n * The user is a prop rather than read here, so a surface drawn outside the\n * console's Firebase provider still renders.\n */\nexport function AiSiteStarterFallback({\n job,\n user,\n}: {\n job: AiJobSummary\n user: Parameters<typeof authorizedFetch>[0]\n}): JSX.Element | null {\n const userRef = useRef(user)\n userRef.current = user\n const [state, setState] = useState<'idle' | 'busy' | 'provisioned' | 'unchanged' | 'failed'>('idle')\n const use = useCallback(async () => {\n if (!job.hostId) return\n setState('busy')\n setState(await requestAiStarterSite(userRef.current, job.hostId))\n }, [job.hostId])\n if (!aiSiteStarterFallbackOffered(job)) return null\n if (state === 'provisioned') {\n return (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {'Your site now has the starter home page, live at its address.'}\n </Typography>\n )\n }\n return (\n <>\n <Button size=\"small\" variant=\"text\" disabled={state === 'busy'} onClick={() => void use()} sx={{ mt: 1, ml: 1 }}>\n {AI_SITE_STARTER_FALLBACK_LABEL}\n </Button>\n {state === 'unchanged' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\">\n {'This site already has pages, so the starter was not added.'}\n </Typography>\n )}\n {state === 'failed' && (\n <Typography variant=\"caption\" color=\"error\" component=\"div\">\n {'The starter site could not be added. Try again.'}\n </Typography>\n )}\n </>\n )\n}\n\nexport default AiSiteStarterFallback\n"],"names":["trackEvent","authorizedFetch","Button","Typography","useCallback","useRef","useState","AI_SITE_STARTER_FALLBACK_LABEL","aiSiteStarterFallbackOffered","requestAiStarterSite","user","hostId","response","method","headers","body","JSON","stringify","ok","payload","json","catch","provisioned","first_publish","AiSiteStarterFallback","job","userRef","current","state","setState","use","variant","color","component","sx","mt","size","disabled","onClick","ml"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,UAAU,QAAQ,0CAAyC;AACpE,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,MAAM,EAAEC,UAAU,QAAQ,gBAAe;AAClD,SAASC,WAAW,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACrD,SACEC,8BAA8B,EAC9BC,4BAA4B,QACvB,kCAA8B;AAGrC;;;;;;CAMC,GACD,OAAO,eAAeC,qBACpBC,IAA2C,EAC3CC,MAAc;IAEd,IAAI;QACF,MAAMC,WAAW,MAAMX,gBAAgBS,MAAM,sBAAsB;YACjEG,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAAC;gBAAEN;YAAO;QAChC;QACA,IAAI,CAACC,SAASM,EAAE,EAAE,OAAO;QACzB,MAAMC,UAAW,MAAMP,SAASQ,IAAI,GAAGC,KAAK,CAAC,IAAM;QACnD,IAAIF,CAAAA,2BAAAA,QAASG,WAAW,MAAK,MAAM,OAAO;QAC1CtB,WAAW,kBAAkB;YAAEuB,eAAe;QAAK;QACnD,OAAO;IACT,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASC,sBAAsB,EACpCC,GAAG,EACHf,IAAI,EAIL;IACC,MAAMgB,UAAUrB,OAAOK;IACvBgB,QAAQC,OAAO,GAAGjB;IAClB,MAAM,CAACkB,OAAOC,SAAS,GAAGvB,SAAmE;IAC7F,MAAMwB,MAAM1B,YAAY;QACtB,IAAI,CAACqB,IAAId,MAAM,EAAE;QACjBkB,SAAS;QACTA,SAAS,MAAMpB,qBAAqBiB,QAAQC,OAAO,EAAEF,IAAId,MAAM;IACjE,GAAG;QAACc,IAAId,MAAM;KAAC;IACf,IAAI,CAACH,6BAA6BiB,MAAM,OAAO;IAC/C,IAAIG,UAAU,eAAe;QAC3B,qBACE,KAACzB;YAAW4B,SAAQ;YAAUC,OAAM;YAAiBC,WAAU;YAAMC,IAAI;gBAAEC,IAAI;YAAE;sBAC9E;;IAGP;IACA,qBACE;;0BACE,KAACjC;gBAAOkC,MAAK;gBAAQL,SAAQ;gBAAOM,UAAUT,UAAU;gBAAQU,SAAS,IAAM,KAAKR;gBAAOI,IAAI;oBAAEC,IAAI;oBAAGI,IAAI;gBAAE;0BAC3GhC;;YAEFqB,UAAU,6BACT,KAACzB;gBAAW4B,SAAQ;gBAAUC,OAAM;gBAAiBC,WAAU;0BAC5D;;YAGJL,UAAU,0BACT,KAACzB;gBAAW4B,SAAQ;gBAAUC,OAAM;gBAAQC,WAAU;0BACnD;;;;AAKX;AAEA,eAAeT,sBAAqB"}
@@ -14,34 +14,20 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { type AiJobOutput } from '../model/ai-jobs.types';
17
+ import { type AiJobOutput, type AiJobSummary } from '../model/ai-jobs.types';
18
18
  import type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types';
19
19
  import { authorizedFetch } from '@aglyn/shared-util-http/authorized-token';
20
20
  import type { AiInsightSurface } from '../model/ai-insight';
21
- /**
22
- * Where "open draft" goes, or `null` when the output has no page of its
23
- * own — a `text` output carries its copy on the job and is shown inline.
24
- * A versioned resource opens in the besigner on the version the job wrote;
25
- * one the console lists without a detail page opens its list; a `theme`
26
- * proposal opens the site's Theme section, where it is put in the editor.
27
- *
28
- * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves
29
- * through the member's host projection by subdomain, never by document id.
30
- * So an output that carries no `hostSubdomain` gets no link rather than one
31
- * that opens no site.
32
- */
33
- export declare function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null;
21
+ import { type AiJobsOpenRequest } from './ai-jobs-store';
22
+ import { aiJobOutputHref, aiJobPrimaryLink } from './ai-job-links';
23
+ import { readEventFrames } from './ai-job-events';
24
+ export { aiJobOutputHref, aiJobPrimaryLink, readEventFrames };
34
25
  /**
35
26
  * The navigation entry a page job proposes for the page it built (AGL-2907),
36
27
  * or `null`. The job writes no menu: the member adds the entry once the page
37
28
  * is live.
38
29
  */
39
30
  export declare function aiJobNavigationProposal(output: AiJobOutput): string | null;
40
- /**
41
- * `data:` frames out of an SSE body, one parsed event per frame. Shared with
42
- * every panel that watches a job through the events route.
43
- */
44
- export declare function readEventFrames(body: ReadableStream<Uint8Array>, onEvent: (event: Record<string, unknown>) => void): Promise<void>;
45
31
  export interface AssistJobsDrawerProps {
46
32
  /** The org the panel is scoped to — `undefined` where the page named none. */
47
33
  orgId: string | undefined;
@@ -67,6 +53,18 @@ export interface AssistJobsDrawerProps {
67
53
  surface: Exclude<AiInsightSurface, 'digest'>;
68
54
  host: string | null;
69
55
  } | null;
56
+ /**
57
+ * The workspace's jobs that are not yet settled, from the shared store the
58
+ * top-bar indicator reads (AGL-3593). The header counts them, and the
59
+ * drawer opens expanded while there is any. Absent, the drawer counts what
60
+ * it loaded itself.
61
+ */
62
+ inFlight?: readonly AiJobSummary[];
63
+ /**
64
+ * The latest request to open AI jobs (AGL-3593): a new `seq` expands the
65
+ * drawer, and a named job is scrolled to and highlighted.
66
+ */
67
+ focus?: AiJobsOpenRequest | null;
70
68
  }
71
- export declare function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible, isStaff, hostId, insight, }: AssistJobsDrawerProps): JSX.Element | null;
69
+ export declare function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible, isStaff, hostId, insight, inFlight, focus, }: AssistJobsDrawerProps): JSX.Element | null;
72
70
  export default AssistJobsDrawer;