@aglyn/plugins-ai 1.0.0-beta.288 → 1.0.0-beta.290
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.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.d.ts +3 -2
- package/src/lib/activity/ai-activity-actions.js +3 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +2 -0
- package/src/lib/components/ai-job-requests.js +2 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +4 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-import-job.component.d.ts +6 -1
- package/src/lib/components/ai-site-import-job.component.js +124 -17
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.d.ts +6 -0
- package/src/lib/components/ai-site-import-start.component.js +18 -4
- package/src/lib/components/ai-site-import-start.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.js +2 -1
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/jobs/ai-image-reads.d.ts +20 -2
- package/src/lib/jobs/ai-image-reads.js +47 -0
- package/src/lib/jobs/ai-image-reads.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +53 -3
- package/src/lib/jobs/ai-job-import-step.js +386 -64
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +33 -0
- package/src/lib/jobs/ai-job-layout-language.js +75 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +8 -3
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +29 -0
- package/src/lib/jobs/ai-job-page-language.js +178 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +3 -0
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-site-import-document.d.ts +117 -0
- package/src/lib/jobs/ai-site-import-document.js +710 -0
- package/src/lib/jobs/ai-site-import-document.js.map +1 -0
- package/src/lib/jobs/ai-site-import-look.d.ts +112 -0
- package/src/lib/jobs/ai-site-import-look.js +402 -0
- package/src/lib/jobs/ai-site-import-look.js.map +1 -0
- package/src/lib/jobs/ai-site-import-publish.d.ts +41 -0
- package/src/lib/jobs/ai-site-import-publish.js +92 -0
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +27 -2
- package/src/lib/jobs/ai-theme-brand-inputs.js +37 -8
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +17 -0
- package/src/lib/layout-language/ai-layout-frame.js +73 -2
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +43 -0
- package/src/lib/layout-language/ai-layout-language.js +199 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +15 -0
- package/src/lib/model/ai-credit-estimate.js +14 -1
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +13 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-import-view.d.ts +47 -3
- package/src/lib/model/ai-site-import-view.js +122 -20
- package/src/lib/model/ai-site-import-view.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +232 -7
- package/src/lib/model/ai-site-import.js +365 -35
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +18 -0
- package/src/lib/runtime/ai-doctrine-validators.js +16 -5
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +31 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-image-reads-route.js +24 -1
- package/src/lib/server/ai-image-reads-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +7 -1
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/site-import/fixtures/aglyn-com-recordings.d.ts +57 -0
- package/src/lib/site-import/fixtures/aglyn-com-recordings.js +172 -0
- package/src/lib/site-import/fixtures/aglyn-com-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +32 -7
- package/src/lib/site-import/site-import-crawl.js +133 -44
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-edge-server.d.ts +31 -0
- package/src/lib/site-import/site-import-edge-server.js +31 -0
- package/src/lib/site-import/site-import-edge-server.js.map +1 -0
- package/src/lib/site-import/site-import-edge.d.ts +44 -0
- package/src/lib/site-import/site-import-edge.js +108 -0
- package/src/lib/site-import/site-import-edge.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +73 -3
- package/src/lib/site-import/site-import-html.js +332 -45
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-images.d.ts +22 -0
- package/src/lib/site-import/site-import-images.js +78 -2
- package/src/lib/site-import/site-import-images.js.map +1 -1
- package/src/lib/site-import/site-import-page.d.ts +44 -2
- package/src/lib/site-import/site-import-page.js +116 -16
- package/src/lib/site-import/site-import-page.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, 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 { mdiCreation, mdiPageLayoutHeaderFooter, mdiWebSync } from '@aglyn/shared-data-mdi'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { HelpTip, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport BlockedControl from '@aglyn/shared-ui-jsx/components/blocked-control.component'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\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 DialogContentText,\n DialogTitle,\n IconButton,\n Stack,\n Toolbar,\n Typography,\n useMediaQuery,\n} from '@mui/material'\nimport { alpha, useTheme, type Theme } from '@mui/material/styles'\nimport { useRouter } from 'next/navigation'\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n type SyntheticEvent,\n} from 'react'\nimport {\n AI_JOB_AUTO_CONFIRM_INPUT,\n AI_SITE_FREE_PAGES,\n aiFreeCreditsNoneLeftText,\n aiFreeCreditsResetLabel,\n aiFreeSiteCreditRange,\n aiFreeSitePrompt,\n aiSiteCreditRange,\n type AiFreeCreditsLeft,\n aiSitePagesBand,\n} from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport {\n AI_CREDITS_CONFIRM_CODE,\n aiCreditRangeText,\n type AiCreditRange,\n type AiCreditsPrompt,\n} from '../model/ai-credit-estimate'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport {\n AI_SITE_START_ANSWERS,\n aiSiteStartAnswersFromClientBuild,\n aiSiteStartBrief,\n aiSiteStartClientBrief,\n aiSiteStartClientInputs,\n aiSiteStartClientNote,\n aiSiteStartExtrasGiven,\n aiSiteStartInputs,\n aiSiteStartRefusal,\n type AiSiteStartAnswers,\n} from '../model/ai-site-start'\nimport { aiSiteInspirationLimit } from '../model/ai-site-inspiration'\nimport {\n aiSiteStartFieldErrors,\n aiSiteStartFirstInvalidStep,\n aiSiteStartNextStep,\n aiSiteStartPreviousStep,\n aiSiteStartStepErrors,\n aiSiteStartStepIndex,\n AI_SITE_START_STEPS,\n AI_SITE_START_STEP_FIELDS,\n type AiSiteStartField,\n type AiSiteStartStep,\n} from '../model/ai-site-start-steps'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiSiteKindOfInputs } from '../model/ai-site-kinds'\nimport { aiSiteStartClickOf, useAiSiteStartTracking } from './ai-site-start-tracking'\nimport {\n AudienceStep,\n DetailsStep,\n KindStep,\n MoreDetails,\n ReviewStep,\n StartProgress,\n StepHelp,\n StyleStep,\n} from './ai-site-start-steps.component'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { AiModelSelector } from './ai-model-selector.component'\nimport { useAiModelChoice } from './use-ai-model-choice'\nimport { aiCreditsBillingHref, aiSiteBuildHref } from './ai-job-links'\nimport { publishAiJob } from './ai-jobs-store'\nimport { clearAiSiteStartAnswers, readAiSiteStartAnswers } from '../model/ai-site-start-handoff'\nimport { AiSiteImportStart } from './ai-site-import-start.component'\nimport { aiSiteImportOfferOf, type AiSiteImportAvailability } from '../model/ai-site-import-view'\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 on to the\n * questions, one decision a step (AGL-3898; see {@link AiSiteStartCard}).\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 * ── Or before the site exists (AGL-3761) ─────────────────────────────────\n *\n * \"Create site\" draws this straight away, with the zone's `pendingSite` and\n * no site. Step 1 then asks the site's name and address (the shell's own\n * fields) above the two cards, and the site is created only by a choice that\n * needs one: the starter card or Skip (`startBlank`, which creates it with the\n * starter), or Plan my site, which creates it and then starts the job on it.\n * The close control and Escape create nothing there: they are `leave`. If the\n * site was created and its job could not start, the site is a real one, and\n * every way out gives it the starter as above, so none is left empty.\n *\n * Escape and the close control are DISMISSALS, though, and Skip is a choice\n * (AGL-3660, 2026-10-08). Once anything is typed or picked, a dismissal asks\n * \"Leave without your answers?\" first, focused on Keep editing, so a stray\n * Escape — the second press after closing the model menu was the one in\n * production — can never trade a brief for the starter. Escape that reached\n * the dialog from a portal it does not contain (a menu, a select's list) is\n * that popup's, and the dialog ignores it.\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 builds and publishes for you ──────────────────────────────────────\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. The\n * guided start confirms its own plan (AGL-3594), so the build follows with no\n * approval to make, and the site is published, indexable, when it is done.\n * The person edits or unpublishes it afterwards.\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\n/** Labels the dialog for a reader, from its own heading. */\nconst TITLE_ID = 'ai-site-start-title'\n\n/**\n * The takeover's surface, for its paper, its bar and its footer alike: the\n * console page's own background (AGL-3596). The paper sits at elevation 0,\n * which is what keeps MUI's dark-mode elevation overlay off it.\n */\nexport const AI_SITE_START_SURFACE_SX = { bgcolor: 'background.default' } 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 blocked = null,\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 /**\n * Why the card cannot be chosen here, when the workspace can change that\n * (AGL-3976): drawn disabled with the reason, never hidden.\n */\n blocked?: string | null\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 <BlockedControl blocked={Boolean(blocked)} reason={blocked ?? ''} fullWidth>\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 </BlockedControl>\n </Card>\n )\n}\n\n/** A small label above a group of questions. */\n/**\n * Each part of the guided start links its own section of the Start with AI\n * guide (AGL-3660), so a `?` says what that one group of answers changes.\n */\nconst START_HELP = {\n choose: pluginDocsHelp('aiStart', {\n anchor: '#how-do-you-want-to-start',\n excerpt:\n 'The starter site gives you a ready-made home page to edit. Start with AI asks a few questions, then plans, writes and publishes your pages.',\n }),\n estimate: pluginDocsHelp('aiCredits', {\n anchor: '#before-a-job-starts',\n excerpt:\n 'About what a start like this costs, measured on real builds, and the most it can cost. What it really costs is what each step spends, and you can watch that add up while it runs.',\n }),\n}\n\nfunction SectionLabel({ children, help }: { children: ReactNode; help?: ReturnType<typeof pluginDocsHelp> }) {\n return (\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Typography variant=\"overline\" color=\"text.secondary\" component=\"h2\" sx={{ display: 'block' }}>\n {children}\n </Typography>\n {help ? <HelpTip {...help} /> : null}\n </Stack>\n )\n}\n\n/**\n * The guided start, as the `hostFirstRun` zone draws it, or reopened on a\n * failed start's own answers (AGL-3596): `initialAnswers` opens it on the\n * questions, filled in, for the person to adjust and start a fresh job.\n */\nexport type AiSiteStartCardProps = ConsoleHostFirstRunZoneProps & {\n initialAnswers?: AiSiteStartAnswers | null\n}\n\n/**\n * The questions are one decision a step (AGL-3898): what kind of site it is\n * (typed, or a card), who it is for, the style (cards that show each one,\n * \"Pick for me\" first and picked), the details, and a review that plans the\n * site (`ai-site-start-steps.component.tsx`). Back and Next keep every\n * answer; Enter in a text answer is Next; each step's heading takes the focus\n * when it is drawn; a step that cannot go forward says why beside the field\n * and puts the cursor in it. The answers are the same object the single form\n * held, and only the review turns them into the job, through the same\n * `aiSiteStartBrief` and `aiSiteStartInputs`. Each step is reported as a\n * funnel, with where people struggle (`ai-site-start-tracking.ts`).\n */\nexport function AiSiteStartCard({\n hostId,\n orgId,\n orgSlug,\n host,\n startBlank,\n leave,\n pendingSite,\n clientBuild,\n initialAnswers = null,\n}: AiSiteStartCardProps) {\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 // \"Start again\" on a start whose empty site was removed (AGL-3936) opens\n // Create site with that start's answers: read once, and only here, where\n // the site does not exist yet. It opens on the first question, where the\n // site's name and address are asked, with every answer after it filled in.\n const [handedOff] = useState(() => (pendingSite && !initialAnswers ? readAiSiteStartAnswers() : null))\n // A site staff are building for a client (AGL-3958): the intake answered\n // the questions already, so the start opens on the review with them, and\n // stays on this screen once it has started. Read once, like a reopened start.\n const [clientAnswers] = useState(() =>\n clientBuild && !initialAnswers ? aiSiteStartAnswersFromClientBuild(clientBuild.intake) : null,\n )\n const [clientIntake] = useState(() => clientBuild?.intake ?? null)\n const clientBuildRef = useRef(clientBuild)\n clientBuildRef.current = clientBuild\n // Read once: a start reopened on its answers stays one while it is open.\n const reopened = useRef(initialAnswers !== null || handedOff !== null || clientAnswers !== null)\n // A start reopened on its answers opens on the review, every answer there to change.\n const [step, setStep] = useState<AiSiteStartStep>(initialAnswers || clientAnswers ? 'review' : 'choose')\n const [answers, setAnswers] = useState<AiSiteStartAnswers>(\n initialAnswers ?? clientAnswers ?? handedOff ?? AI_SITE_START_ANSWERS,\n )\n // The steps whose reasons not to go forward are shown: the ones Next was refused on.\n const [checked, setChecked] = useState<ReadonlySet<AiSiteStartStep>>(() => new Set())\n // A step opened from the review's Edit goes back to the review on Next.\n const [returnTo, setReturnTo] = useState<AiSiteStartStep | null>(null)\n // Each step's heading takes the focus when the step changes, never on the first draw.\n const headingRef = useRef<HTMLHeadingElement | null>(null)\n const drawnStep = useRef<AiSiteStartStep | null>(null)\n const fieldRefs = useRef<Partial<Record<AiSiteStartField, HTMLInputElement | null>>>({})\n const theme = useTheme()\n const compact = useMediaQuery(theme.breakpoints.down('sm'), { noSsr: true })\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 // Whether the Style step asks for the sites the person likes (AGL-3975),\n // read off the same verdict request: `release_ai_site_inspiration`.\n const [siteInspiration, setSiteInspiration] = useState(false)\n // What the Free workspace has left this month (AGL-3660), as the same read\n // the create door refuses on: the less of its own band and its owner's\n // allowance across their Free workspaces. `null` when the route said none.\n const [freeCredits, setFreeCredits] = useState<AiFreeCreditsLeft | null>(null)\n // \"Leave without your answers?\" (AGL-3660): a dismissal with a brief typed.\n const [confirmingLeave, setConfirmingLeave] = useState(false)\n // The dialog's own root, to tell its Escape from a nested popup's.\n const dialogRef = useRef<HTMLDivElement | null>(null)\n // Drawn by \"Create site\" before the site exists (AGL-3761): the shell's\n // name and address are asked on step 1, and the site is created only by\n // the starter, Skip, or Plan my site. Read through a ref by the verdict\n // below, which must not re-run when the shell re-renders it.\n const pendingRef = useRef(pendingSite)\n pendingRef.current = pendingSite\n const creating = Boolean(pendingSite) && !pendingSite?.created\n // The funnel (AGL-3898): each step drawn, left and abandoned, and where people struggle.\n const tracker = useAiSiteStartTracking(step, verdict === 'ready')\n // \"Bring my site\" (AGL-3976): asked of its own door, absent while its flag\n // is off, drawn disabled with the reason where the workspace can change it.\n const [importAvailability, setImportAvailability] = useState<AiSiteImportAvailability>({ state: 'checking' })\n const [importing, setImporting] = 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 // `siteImport=1`: whether \"Bring my site\" is offered here (AGL-3976), on the same read.\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1&siteImport=1`,\n )\n const payload = response.ok ? await response.json().catch(() => null) : null\n if (active) {\n setSiteInspiration(payload?.siteInspiration === true)\n if (payload?.freeTaste === true) {\n setFreeTaste(true)\n const credits = payload?.freeCredits as AiFreeCreditsLeft | undefined\n if (credits && Number.isFinite(credits.left)) setFreeCredits(credits)\n // A reopened start keeps the pages it asked for, within the band.\n setAnswers((current) => ({\n ...current,\n pages: reopened.current ? Math.min(current.pages, AI_SITE_FREE_PAGES.max) : AI_SITE_FREE_PAGES.max,\n welcomeEmail: false,\n }))\n }\n const shown = response.status !== 404 && response.status !== 403\n setImportAvailability(aiSiteImportOfferOf(payload?.siteImport))\n setVerdict(shown ? 'ready' : 'hidden')\n // \"Create site\" shows its own dialog when this one will not draw.\n pendingRef.current?.ready(shown)\n clientBuildRef.current?.ready?.(shown)\n }\n } catch {\n if (active) {\n setVerdict('hidden')\n pendingRef.current?.ready(false)\n clientBuildRef.current?.ready?.(false)\n }\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 // A Free start past what is left asks first (AGL-3722): it is admitted on\n // its measured p90, and one past what is left starts only on \"Build what\n // fits\" (pausing with Resume where the credits run out), or smaller as the\n // home page alone. The create door asks the same, so a stale dialog meets\n // the prompt there too (`serverPrompt`). Nothing at all left is a dead\n // stop until the credits renew or the workspace upgrades.\n const [serverPrompt, setServerPrompt] = useState<AiCreditsPrompt | null>(null)\n // The door's prompt was for the pages it was asked with.\n useEffect(() => setServerPrompt(null), [answers.pages])\n // A store's first products and a blog's first posts are part of the start (AGL-3676, AGL-3798).\n const siteKind = aiSiteKindOfInputs(aiSiteStartInputs(answers))?.id ?? null\n const prompt = freeTaste ? (aiFreeSitePrompt(freeCredits, answers.pages, { siteKind }) ?? serverPrompt) : null\n const noneLeft = freeTaste && freeCredits !== null && freeCredits.left <= 0\n const refusal = aiSiteStartRefusal(answers, { freeTaste })\n const band = aiSitePagesBand(freeTaste)\n // On a paid plan the person picks the model that builds the site (AGL-3660),\n // Auto by default, from the models their plan allows; every step of the job\n // runs on it. A Free workspace builds on the default and is offered none.\n const modelChoice = useAiModelChoice({ orgId, hostId, surface: 'jobs', kind: 'job.page' })\n // A pick remembered from before needs its cost to estimate by; Auto does not,\n // and the list is otherwise read when the picker opens.\n const { load: loadModels, model: rememberedModel } = modelChoice\n useEffect(() => {\n if (!freeTaste && step === 'review' && rememberedModel) loadModels()\n }, [freeTaste, step, rememberedModel, loadModels])\n const pickedModel = freeTaste ? null : modelChoice.model\n\n const plan = useCallback(async (options: { creditsConfirmed?: boolean; pages?: number } = {}) => {\n const asked = options.pages ? { ...answers, pages: options.pages } : answers\n if (!orgId || aiSiteStartRefusal(asked, { freeTaste }) || noneLeft) return\n // Past what is left, only an explicit go-ahead starts it.\n if (freeTaste && !options.creditsConfirmed && aiFreeSitePrompt(freeCredits, asked.pages, { siteKind })) return\n if (options.pages) answer({ pages: options.pages })\n setBusy(true)\n setNotice(null)\n setServerPrompt(null)\n try {\n const requestJob = (siteId: string | null, check: boolean) =>\n authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId: siteId,\n kind: 'site',\n // A client build's intake rides along (AGL-3958): the name and\n // towns as the inputs the questions leave empty (the towns only\n // while no address is given, AGL-3995), and how to reach the\n // business, its hours and the owner's words in the brief.\n brief: [aiSiteStartBrief(asked), clientIntake ? aiSiteStartClientBrief(clientIntake) : '']\n .filter(Boolean)\n .join(' '),\n // The guided start confirms its own plan (AGL-3594): the build\n // follows the plan with no approval to make.\n // The sites they like go only while the start asks for them (AGL-3975).\n inputs: {\n ...aiSiteStartInputs(siteInspiration ? asked : { ...asked, inspiration: [] }),\n ...(clientIntake ? aiSiteStartClientInputs(clientIntake, asked) : {}),\n [AI_JOB_AUTO_CONFIRM_INPUT]: true,\n },\n ...(pickedModel ? { model: pickedModel } : {}),\n ...(options.creditsConfirmed ? { creditsConfirmed: true } : {}),\n ...(check ? { check: true } : {}),\n }),\n })\n // A refusal said in the dialog, or the Free go-ahead it asks for.\n const refused = async (response: Response): Promise<unknown> => {\n const payload = await response.json().catch(() => null)\n if (response.ok) return payload ?? {}\n if (response.status === 409 && payload?.code === AI_CREDITS_CONFIRM_CODE && payload?.credits) {\n setServerPrompt(payload.credits as AiCreditsPrompt)\n return null\n }\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 null\n }\n // The site, created now when it does not exist yet (AGL-3761): this is\n // the moment the person asked for it. The start is checked first\n // (AGL-3859), so one the jobs route would refuse, or would ask the Free\n // go-ahead for, is answered before any site exists. A refusal about the\n // name or address goes back to step 1, where they are typed.\n let site = { hostId, host, orgSlug }\n if (!hostId && pendingSite) {\n if (!pendingSite.created && !(await refused(await requestJob(null, true)))) return\n const made = await pendingSite.create()\n if ('error' in made) {\n setNotice(made.error)\n if (made.address) setStep('choose')\n return\n }\n site = { hostId: made.hostId, host: made.host, orgSlug: made.orgSlug || orgSlug }\n }\n if (!site.hostId) return\n const payload = (await refused(await requestJob(site.hostId, false))) as { job?: AiJobSummary } | null\n if (!payload) return\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 // A removed start's answers have been used (AGL-3936).\n clearAiSiteStartAnswers()\n // Which optional details the site was asked for with (AGL-3979), by id only.\n tracker.detailsGiven(\n aiSiteStartExtrasGiven(asked).map((field) => ({\n field,\n step: AI_SITE_START_STEPS.find((at) => AI_SITE_START_STEP_FIELDS[at].includes(field)) ?? 'details',\n })),\n )\n // The funnel's last step: the site is started, and nothing after is leaving it.\n tracker.complete()\n tracker.finish()\n // A client build follows its job here, on the build's own screen, which\n // goes on to send the owner the preview (AGL-3958).\n if (clientIntake) {\n clientBuildRef.current?.onStarted?.({ id: created.id })\n setStarted(created)\n return\n }\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(site.orgSlug, site.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, answer, freeTaste, freeCredits, noneLeft, orgSlug, host, leave, router, pickedModel, pendingSite, siteKind, tracker, clientIntake, siteInspiration])\n\n // The zone's `startBlank`. Before the site exists it creates it with the\n // starter, and answers `false` when it could not — most often a name or\n // address to fix, which step 1 shows.\n const runStartBlank = useCallback(() => {\n const result = startBlank()\n if (result && typeof result.then === 'function') {\n void result.then((done) => {\n if (done) return\n setStartingStarter(false)\n setStep('choose')\n })\n }\n }, [startBlank])\n\n const chooseStarter = useCallback(() => {\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n setStartingStarter(true)\n trackEvent('site_start_choice', { choice: 'starter' })\n // The starter is a way to start, not a way to leave: no abandonment after it.\n tracker.finish()\n runStartBlank()\n }, [pendingSite, runStartBlank, tracker])\n\n const chooseImport = useCallback(() => {\n if (importAvailability.state !== 'ready') return\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n trackEvent('site_start_choice', { choice: 'import' })\n tracker.complete()\n setImporting(true)\n }, [importAvailability.state, pendingSite, tracker])\n\n const chooseAi = useCallback(() => {\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n trackEvent('site_start_choice', { choice: 'ai' })\n tracker.complete()\n setStep('kind')\n }, [pendingSite, tracker])\n\n // The heading of each step takes the focus as the step is drawn, so a\n // keyboard and a screen reader start where the new question does.\n useEffect(() => {\n if (verdict !== 'ready') return\n if (drawnStep.current !== null && drawnStep.current !== step) headingRef.current?.focus()\n drawnStep.current = step\n }, [step, verdict])\n\n /** Forward from a question step, when its answers may go forward. */\n const next = useCallback(() => {\n if (step === 'choose' || step === 'review') return\n const errors = aiSiteStartStepErrors(step, answers, { freeTaste })\n if (errors.length) {\n setChecked((current) => new Set(current).add(step))\n for (const error of errors) tracker.fieldError(error.field, error.code)\n fieldRefs.current[errors[0].field]?.focus()\n return\n }\n tracker.complete()\n const target = returnTo ?? aiSiteStartNextStep(step)\n setReturnTo(null)\n // A review reached with an answer that cannot go forward opens on that answer instead.\n if (target === 'review') {\n const invalid = aiSiteStartFirstInvalidStep(answers, { freeTaste })\n if (invalid && invalid !== 'review') {\n setChecked((current) => new Set(current).add(invalid))\n setStep(invalid)\n return\n }\n }\n if (target) setStep(target)\n }, [step, answers, freeTaste, returnTo, tracker])\n\n /** Back one step, keeping every answer. */\n const back = useCallback(() => {\n const target = aiSiteStartPreviousStep(step)\n if (!target) return\n tracker.back()\n setReturnTo(null)\n setStep(target)\n }, [step, tracker])\n\n /** From the review, to the step that asks an answer, and back to the review on Next. */\n const edit = useCallback(\n (target: AiSiteStartStep) => {\n tracker.back()\n setReturnTo('review')\n setStep(target)\n },\n [tracker],\n )\n\n /** From the progress, back to a step already answered: from the review it is Edit. */\n const goBackTo = (target: AiSiteStartStep) => {\n if (aiSiteStartStepIndex(target) >= aiSiteStartStepIndex(step)) return\n if (step === 'review') {\n edit(target)\n return\n }\n tracker.back()\n setReturnTo(null)\n setStep(target)\n }\n\n if (verdict !== 'ready') return null\n\n // Before a job is started Skip is the blank site, which writes the starter\n // (AGL-3594); after, it only closes — the job builds the site.\n const exit = started ? (leave ?? runStartBlank) : runStartBlank\n // Skip, as the header draws it: before a site is started it is leaving the questions.\n const skip = () => {\n if (!started) tracker.abandon('skip')\n exit()\n }\n // Escape and the close control. Before \"Create site\" has made the site\n // (AGL-3761) there is nothing to give a starter to, so they only close,\n // and nothing is created; otherwise they are the same way out as Skip.\n const dismissTo = creating && !started ? (leave ?? runStartBlank) : exit\n // Anything the person typed or picked, which a dismissal must not discard\n // silently (AGL-3660). After a job starts there is nothing left to lose.\n const drafted =\n !started &&\n Boolean(\n answers.siteType.trim() ||\n answers.audience.trim() ||\n answers.kind !== null ||\n aiSiteStartExtrasGiven(answers).length > 0 ||\n (creating && pendingSite?.dirty),\n )\n // Escape and the close control: a dismissal, never a choice. Escape that\n // bubbled here through React from a portal outside this dialog's own DOM is\n // a nested menu's, already handled there, and is not this dialog's to act on.\n const dismiss = (event?: SyntheticEvent | object, reason?: string) => {\n if (reason === 'escapeKeyDown') {\n const target = (event as SyntheticEvent | undefined)?.target\n const root = dialogRef.current\n if (root && target instanceof Node && !root.contains(target)) return\n }\n if (drafted) setConfirmingLeave(true)\n else closeTo()\n }\n // The dismissal itself, once nothing typed is in the way of it.\n const closeTo = () => {\n if (!started) tracker.abandon('close')\n dismissTo()\n }\n const leaveDraft = () => {\n setConfirmingLeave(false)\n closeTo()\n }\n\n // The chosen model's cost against Auto's, as the model list states it.\n const modelMultiplier =\n (pickedModel && modelChoice.options?.options.find((option) => option.id === pickedModel)?.multiplier) || 1\n // What the start is likely to cost and at most (AGL-3722): \"About 137\n // credits (up to 283)\", the measured figures scaled by the chosen model.\n const scaled = (range: AiCreditRange): AiCreditRange => ({\n likely: Math.round(range.likely * modelMultiplier),\n p90: Math.round(range.p90 * modelMultiplier),\n ceiling: Math.round(range.ceiling * modelMultiplier),\n })\n const estimate = freeTaste\n ? aiFreeSiteCreditRange(answers.pages, { siteKind })\n : scaled(aiSiteCreditRange(answers.pages, { welcomeEmail: answers.welcomeEmail, siteKind }))\n // A Free start quotes what is LEFT (AGL-3660), shared across the owner's\n // Free workspaces — never the month's whole allowance as if none were spent.\n const estimateText = freeTaste\n ? freeCredits\n ? `${aiCreditRangeText(estimate)}. You have ${freeCredits.left.toLocaleString('en-US')} of your ${freeCredits.total.toLocaleString('en-US')} free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}`\n : `${aiCreditRangeText(estimate)}, of the ${FREE_AI_TASTE_CREDITS_PER_MONTH} AI credits you get free each month`\n : `${aiCreditRangeText(estimate)}, estimated`\n\n // The card is drawn wherever the workspace could use it, disabled where it cannot yet.\n const showsImport = importAvailability.state === 'ready' || importAvailability.state === 'blocked'\n const choosing = step === 'choose' && !started && !importing\n const reviewing = step === 'review' && !started && !importing\n const asking = step !== 'choose' && !started && !importing\n // Each answer's reason not to go forward, shown on a step once Next was refused there.\n const fieldErrors = aiSiteStartFieldErrors(answers, { freeTaste })\n const shownErrors = Object.fromEntries(\n AI_SITE_START_STEP_FIELDS[step]\n .filter((field) => checked.has(step) && fieldErrors[field])\n .map((field) => [field, fieldErrors[field]]),\n )\n const stepProps = {\n answers,\n answer,\n tracker,\n errors: shownErrors,\n fieldRef: (field: AiSiteStartField) => (node: HTMLInputElement | null) => {\n fieldRefs.current[field] = node\n },\n }\n const HEADINGS: Record<AiSiteStartStep, { title: string; lede: string }> = {\n choose: {\n title: 'How do you want to start?',\n lede: creating ? 'Name your site, then choose how it starts. You can change everything later.' : 'You can change everything later.',\n },\n kind: {\n title: 'What are you building?',\n lede: `Say it in a few words, or pick the closest. ${PLATFORM_BRAND_NAME} AI plans and writes your pages from it.`,\n },\n audience: { title: 'Who is it for?', lede: 'The pages and the contact form are written for them.' },\n style: {\n title: 'Choose a style',\n lede: 'Each picture is an example home page in that style. Yours is written for your business, with its own colors and fonts within the style you pick.',\n },\n details: {\n title: 'A few details',\n lede: 'How customers reach you, where messages go, how many pages, and what else to draft. All can be changed later.',\n },\n review: {\n title: 'Review your site',\n lede: started\n ? `${PLATFORM_BRAND_NAME} AI is on it.`\n : `${PLATFORM_BRAND_NAME} AI plans and writes your pages from these answers, then publishes your site.`,\n },\n }\n const heading = importing\n ? {\n title: 'Bring my site',\n lede: `Bring the website you have now into ${PLATFORM_BRAND_NAME}: its pages, words and photos. First, prove it is yours.`,\n }\n : HEADINGS[step]\n // Enter in a text answer is Next. A select, a card or a button keeps its own Enter.\n const onStepKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' || event.nativeEvent.isComposing || !asking || reviewing) return\n const target = event.target as HTMLElement\n if (target.tagName !== 'INPUT' || (target as HTMLInputElement).type === 'hidden') return\n event.preventDefault()\n next()\n }\n\n return (\n <Dialog\n open\n fullScreen\n ref={dialogRef}\n onClose={dismiss}\n aria-labelledby={TITLE_ID}\n // Where people get lost (AGL-3898): any input puts off the idle signal,\n // and every click is read for rage and dead clicks.\n onClickCapture={(event: MouseEvent<HTMLElement>) => tracker.click(aiSiteStartClickOf(event))}\n onKeyDownCapture={() => tracker.activity()}\n onInputCapture={() => tracker.activity()}\n // The console's own page surface, in both modes. A Dialog's paper sits\n // at elevation 24 by default, and in dark mode MUI lightens an elevated\n // paper with a white overlay, which turned the whole takeover a\n // washed-out gray; the overlay is for floating surfaces, and this one\n // fills the screen, so its paper is flat.\n slotProps={{ paper: { elevation: 0, sx: AI_SITE_START_SURFACE_SX } }}\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 data-start-area=\"header\"\n sx={{ ...AI_SITE_START_SURFACE_SX, borderBottom: 1, borderColor: 'divider' }}\n >\n <Toolbar variant=\"dense\" sx={{ gap: 1 }}>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={() => dismiss()}\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={skip} 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 } }} onKeyDown={onStepKeyDown}>\n {/* The step timeline spans the whole dialog, above the step's own\n narrower column (AGL-4014): MUI's Stepper as it comes. */}\n {importing ? null : (\n <Box sx={{ pt: { xs: 3, sm: 4 } }}>\n <StartProgress step={step} compact={compact} onStep={started ? undefined : goBackTo} />\n </Box>\n )}\n <Container\n maxWidth={choosing || step === 'kind' || step === 'style' ? 'md' : 'sm'}\n disableGutters\n sx={{ py: { xs: 3, sm: 5 } }}\n >\n <Stack spacing={4}>\n <Stack spacing={1} sx={{ textAlign: choosing ? 'center' : 'left' }} data-start-area=\"heading\">\n <Typography\n ref={headingRef}\n tabIndex={-1}\n variant=\"h4\"\n component=\"h1\"\n sx={{ '&:focus': { outline: 'none' }, '&:focus-visible': { outline: 'none' } }}\n >\n {heading.title}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {choosing ? (\n <HelpTip sx={{ float: 'right' }} {...START_HELP.choose} />\n ) : importing ? null : (\n <StepHelp step={step} />\n )}\n {heading.lede}\n </Typography>\n </Stack>\n {notice && (\n <Alert severity=\"info\" data-start-area=\"notice\">\n {notice}\n </Alert>\n )}\n {asking && noneLeft && freeCredits && (\n <Alert\n severity=\"warning\"\n data-start-area=\"notice\"\n action={\n orgSlug ? (\n <Button color=\"inherit\" size=\"small\" href={aiCreditsBillingHref(orgSlug)}>\n {'Upgrade'}\n </Button>\n ) : undefined\n }\n >\n {aiFreeCreditsNoneLeftText(freeCredits.resetsOn)}\n </Alert>\n )}\n {choosing && creating && pendingSite ? (\n <Stack spacing={2} component=\"section\" aria-label=\"Your site\">\n <SectionLabel>{'Your site'}</SectionLabel>\n {pendingSite.fields}\n </Stack>\n ) : null}\n {importing && orgId ? (\n <AiSiteImportStart\n orgId={orgId}\n orgSlug={orgSlug}\n hostId={hostId}\n host={host}\n user={user}\n pendingSite={pendingSite}\n onBack={() => setImporting(false)}\n leave={leave}\n />\n ) : choosing ? (\n <Stack spacing={2}>\n <Box\n data-start-area=\"choose-cards\"\n sx={{\n display: 'grid',\n gridTemplateColumns: showsImport ? { xs: '1fr', sm: '1fr 1fr', md: '1fr 1fr 1fr' } : { 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 {showsImport && (\n <ChoiceCard\n accent=\"secondary\"\n icon={mdiWebSync.path}\n iconId=\"web-sync\"\n title=\"Bring my site\"\n description={`Bring the website you have now into ${PLATFORM_BRAND_NAME}: its pages, words and photos, rewritten in your new design.`}\n blocked={importAvailability.state === 'blocked' ? importAvailability.message : null}\n onChoose={chooseImport}\n >\n {importAvailability.state === 'blocked' && importAvailability.reason === 'plan' ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Paid plans\" />\n ) : null}\n </ChoiceCard>\n )}\n </Box>\n {importAvailability.state === 'blocked' ? (\n <PrerequisiteNotice\n {...(importAvailability.reason === 'plan' && orgSlug\n ? { href: aiCreditsBillingHref(orgSlug), actionLabel: 'Upgrade' }\n : {})}\n >\n {importAvailability.message}\n </PrerequisiteNotice>\n ) : null}\n </Stack>\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. It is then built and published for you, with ' +\n 'nothing to approve. You can edit or unpublish it afterwards.'\n }\n // Full screen: AI jobs opens in the panel this dialog covers.\n onOpenJobs={exit}\n />\n </Card>\n ) : (\n <Box component=\"section\">\n {step === 'kind' && <KindStep {...stepProps} />}\n {step === 'audience' && <AudienceStep {...stepProps} />}\n {step === 'style' && (\n <StyleStep\n {...stepProps}\n inspiration={\n siteInspiration\n ? { limit: aiSiteInspirationLimit(freeTaste), upgradeHref: orgSlug ? aiCreditsBillingHref(orgSlug) : null }\n : null\n }\n />\n )}\n {step === 'details' && <DetailsStep {...stepProps} freeTaste={freeTaste} band={band} />}\n {/* Each question step's optional extras, collapsed, after its own answers (AGL-3979). */}\n {step !== 'review' && (\n <Box sx={{ mt: 3 }}>\n <MoreDetails key={step} step={step} {...stepProps} />\n </Box>\n )}\n {step === 'review' && (\n <ReviewStep answers={answers} freeTaste={freeTaste} onEdit={edit} inspiration={siteInspiration}>\n {clientIntake && (\n <Alert severity=\"info\" variant=\"outlined\" data-start-area=\"client-intake\">\n {aiSiteStartClientNote(clientIntake)}\n </Alert>\n )}\n {!noneLeft && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"site\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => void plan({ creditsConfirmed: true })}\n onSmaller={() => void plan({ creditsConfirmed: true, pages: 1 })}\n />\n )}\n </ReviewStep>\n )}\n </Box>\n )}\n </Stack>\n </Container>\n </DialogContent>\n {asking && (\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 data-start-area=\"footer\"\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 ...AI_SITE_START_SURFACE_SX,\n }}\n >\n {reviewing && (\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 <HelpTip {...START_HELP.estimate} ariaLabel=\"How the estimate works\" />\n </Stack>\n )}\n {reviewing && refusal && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {refusal}\n </Typography>\n )}\n {reviewing && !freeTaste && <AiModelSelector choice={modelChoice} disabled={busy} />}\n <Button onClick={back} sx={reviewing ? undefined : { mr: 'auto' }}>\n {'Back'}\n </Button>\n {reviewing ? (\n <Button\n variant=\"contained\"\n size=\"large\"\n disabled={busy || Boolean(refusal) || Boolean(prompt) || noneLeft}\n onClick={() => void plan()}\n startIcon={<MdiIcon path={mdiCreation.path} />}\n >\n {busy ? 'Starting…' : 'Plan my site'}\n </Button>\n ) : (\n <Button variant=\"contained\" size=\"large\" onClick={next}>\n {returnTo === 'review' ? 'Back to review' : 'Next'}\n </Button>\n )}\n </DialogActions>\n )}\n <Dialog\n open={confirmingLeave}\n onClose={() => setConfirmingLeave(false)}\n aria-labelledby={`${TITLE_ID}-leave`}\n aria-describedby={`${TITLE_ID}-leave-text`}\n maxWidth=\"xs\"\n >\n <DialogTitle id={`${TITLE_ID}-leave`}>{'Leave without your answers?'}</DialogTitle>\n <DialogContent>\n <DialogContentText id={`${TITLE_ID}-leave-text`}>\n {creating\n ? 'What you typed is not saved, and no site is created.'\n : 'What you typed is not saved. Leaving starts your site from the starter site instead.'}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={leaveDraft}>\n {creating ? 'Leave' : 'Leave and start blank'}\n </Button>\n <Button variant=\"contained\" autoFocus onClick={() => setConfirmingLeave(false)}>\n {'Keep editing'}\n </Button>\n </DialogActions>\n </Dialog>\n </Dialog>\n )\n}\n\nexport default AiSiteStartCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","PLATFORM_BRAND_NAME","trackEvent","ICON_VARIANT_CLOSE","mdiCreation","mdiPageLayoutHeaderFooter","mdiWebSync","pluginDocsHelp","HelpTip","MdiIcon","BlockedControl","PrerequisiteNotice","authorizedFetch","useUser","Alert","AppBar","Box","Button","Card","CardActionArea","Chip","CircularProgress","Container","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","Stack","Toolbar","Typography","useMediaQuery","alpha","useTheme","useRouter","useCallback","useEffect","useId","useRef","useState","AI_JOB_AUTO_CONFIRM_INPUT","AI_SITE_FREE_PAGES","aiFreeCreditsNoneLeftText","aiFreeCreditsResetLabel","aiFreeSiteCreditRange","aiFreeSitePrompt","aiSiteCreditRange","aiSitePagesBand","FREE_AI_TASTE_CREDITS_PER_MONTH","AI_CREDITS_CONFIRM_CODE","aiCreditRangeText","AiCreditsPromptNotice","AI_SITE_START_ANSWERS","aiSiteStartAnswersFromClientBuild","aiSiteStartBrief","aiSiteStartClientBrief","aiSiteStartClientInputs","aiSiteStartClientNote","aiSiteStartExtrasGiven","aiSiteStartInputs","aiSiteStartRefusal","aiSiteInspirationLimit","aiSiteStartFieldErrors","aiSiteStartFirstInvalidStep","aiSiteStartNextStep","aiSiteStartPreviousStep","aiSiteStartStepErrors","aiSiteStartStepIndex","AI_SITE_START_STEPS","AI_SITE_START_STEP_FIELDS","aiSiteKindOfInputs","aiSiteStartClickOf","useAiSiteStartTracking","AudienceStep","DetailsStep","KindStep","MoreDetails","ReviewStep","StartProgress","StepHelp","StyleStep","AiJobFollow","AiModelSelector","useAiModelChoice","aiCreditsBillingHref","aiSiteBuildHref","publishAiJob","clearAiSiteStartAnswers","readAiSiteStartAnswers","AiSiteImportStart","aiSiteImportOfferOf","TITLE_ID","AI_SITE_START_SURFACE_SX","bgcolor","IconBadge","accent","children","aria-hidden","sx","theme","width","spacing","height","borderRadius","display","alignItems","justifyContent","palette","main","mode","color","fontSize","ChoiceCard","icon","iconId","title","description","busy","blocked","onChoose","id","variant","transition","transitions","create","duration","shorter","borderColor","boxShadow","shadows","transform","Boolean","reason","fullWidth","onClick","disabled","aria-labelledby","aria-describedby","aria-busy","undefined","p","xs","sm","textAlign","size","aria-label","path","data-icon","component","START_HELP","choose","anchor","excerpt","estimate","SectionLabel","help","direction","AiSiteStartCard","hostId","orgId","orgSlug","host","startBlank","leave","pendingSite","clientBuild","initialAnswers","modelChoice","data","user","router","userRef","current","uid","verdict","setVerdict","handedOff","clientAnswers","intake","clientIntake","clientBuildRef","reopened","step","setStep","answers","setAnswers","checked","setChecked","Set","returnTo","setReturnTo","headingRef","drawnStep","fieldRefs","compact","breakpoints","down","noSsr","setBusy","startingStarter","setStartingStarter","notice","setNotice","started","setStarted","freeTaste","setFreeTaste","siteInspiration","setSiteInspiration","freeCredits","setFreeCredits","confirmingLeave","setConfirmingLeave","dialogRef","pendingRef","creating","created","tracker","importAvailability","setImportAvailability","state","importing","setImporting","active","response","encodeURIComponent","payload","ok","json","catch","credits","Number","isFinite","left","pages","Math","min","max","welcomeEmail","shown","status","siteImport","ready","answer","patch","serverPrompt","setServerPrompt","siteKind","prompt","noneLeft","refusal","band","surface","kind","load","loadModels","model","rememberedModel","pickedModel","plan","options","asked","creditsConfirmed","requestJob","siteId","check","method","headers","body","JSON","stringify","brief","filter","join","inputs","inspiration","refused","code","locked","String","error","site","made","address","job","detailsGiven","map","field","find","at","includes","complete","finish","onStarted","href","push","runStartBlank","result","then","done","chooseStarter","choice","chooseImport","chooseAi","focus","next","errors","length","add","fieldError","target","invalid","back","edit","goBackTo","exit","skip","abandon","dismissTo","drafted","siteType","trim","audience","dirty","dismiss","event","root","Node","contains","closeTo","leaveDraft","modelMultiplier","option","multiplier","scaled","range","likely","round","p90","ceiling","estimateText","toLocaleString","total","resetsOn","showsImport","choosing","reviewing","asking","fieldErrors","shownErrors","Object","fromEntries","has","stepProps","fieldRef","node","HEADINGS","lede","style","details","review","heading","onStepKeyDown","key","nativeEvent","isComposing","tagName","type","preventDefault","open","fullScreen","ref","onClose","onClickCapture","click","onKeyDownCapture","activity","onInputCapture","slotProps","paper","elevation","position","enableColorOnDark","data-start-area","borderBottom","gap","edge","noWrap","textOverflow","flex","px","onKeyDown","pt","onStep","maxWidth","disableGutters","py","tabIndex","outline","float","severity","action","fields","onBack","gridTemplateColumns","md","label","message","actionLabel","intro","onOpenJobs","limit","upgradeHref","mt","onEdit","noun","onBuildWhatFits","onSmaller","flexWrap","rowGap","columnGap","borderTop","mr","minWidth","ariaLabel","startIcon","autoFocus"],"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 { mdiCreation, mdiPageLayoutHeaderFooter, mdiWebSync } from '@aglyn/shared-data-mdi'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { HelpTip, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport BlockedControl from '@aglyn/shared-ui-jsx/components/blocked-control.component'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\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 DialogContentText,\n DialogTitle,\n IconButton,\n Stack,\n Toolbar,\n Typography,\n useMediaQuery,\n} from '@mui/material'\nimport { alpha, useTheme, type Theme } from '@mui/material/styles'\nimport { useRouter } from 'next/navigation'\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n type SyntheticEvent,\n} from 'react'\nimport {\n AI_JOB_AUTO_CONFIRM_INPUT,\n AI_SITE_FREE_PAGES,\n aiFreeCreditsNoneLeftText,\n aiFreeCreditsResetLabel,\n aiFreeSiteCreditRange,\n aiFreeSitePrompt,\n aiSiteCreditRange,\n type AiFreeCreditsLeft,\n aiSitePagesBand,\n} from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport {\n AI_CREDITS_CONFIRM_CODE,\n aiCreditRangeText,\n type AiCreditRange,\n type AiCreditsPrompt,\n} from '../model/ai-credit-estimate'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport {\n AI_SITE_START_ANSWERS,\n aiSiteStartAnswersFromClientBuild,\n aiSiteStartBrief,\n aiSiteStartClientBrief,\n aiSiteStartClientInputs,\n aiSiteStartClientNote,\n aiSiteStartExtrasGiven,\n aiSiteStartInputs,\n aiSiteStartRefusal,\n type AiSiteStartAnswers,\n} from '../model/ai-site-start'\nimport { aiSiteInspirationLimit } from '../model/ai-site-inspiration'\nimport {\n aiSiteStartFieldErrors,\n aiSiteStartFirstInvalidStep,\n aiSiteStartNextStep,\n aiSiteStartPreviousStep,\n aiSiteStartStepErrors,\n aiSiteStartStepIndex,\n AI_SITE_START_STEPS,\n AI_SITE_START_STEP_FIELDS,\n type AiSiteStartField,\n type AiSiteStartStep,\n} from '../model/ai-site-start-steps'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiSiteKindOfInputs } from '../model/ai-site-kinds'\nimport { aiSiteStartClickOf, useAiSiteStartTracking } from './ai-site-start-tracking'\nimport {\n AudienceStep,\n DetailsStep,\n KindStep,\n MoreDetails,\n ReviewStep,\n StartProgress,\n StepHelp,\n StyleStep,\n} from './ai-site-start-steps.component'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { AiModelSelector } from './ai-model-selector.component'\nimport { useAiModelChoice } from './use-ai-model-choice'\nimport { aiCreditsBillingHref, aiSiteBuildHref } from './ai-job-links'\nimport { publishAiJob } from './ai-jobs-store'\nimport { clearAiSiteStartAnswers, readAiSiteStartAnswers } from '../model/ai-site-start-handoff'\nimport { AiSiteImportStart } from './ai-site-import-start.component'\nimport { aiSiteImportOfferOf, type AiSiteImportAvailability } from '../model/ai-site-import-view'\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 on to the\n * questions, one decision a step (AGL-3898; see {@link AiSiteStartCard}).\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 * ── Or before the site exists (AGL-3761) ─────────────────────────────────\n *\n * \"Create site\" draws this straight away, with the zone's `pendingSite` and\n * no site. Step 1 then asks the site's name and address (the shell's own\n * fields) above the two cards, and the site is created only by a choice that\n * needs one: the starter card or Skip (`startBlank`, which creates it with the\n * starter), or Plan my site, which creates it and then starts the job on it.\n * The close control and Escape create nothing there: they are `leave`. If the\n * site was created and its job could not start, the site is a real one, and\n * every way out gives it the starter as above, so none is left empty.\n *\n * Escape and the close control are DISMISSALS, though, and Skip is a choice\n * (AGL-3660, 2026-10-08). Once anything is typed or picked, a dismissal asks\n * \"Leave without your answers?\" first, focused on Keep editing, so a stray\n * Escape — the second press after closing the model menu was the one in\n * production — can never trade a brief for the starter. Escape that reached\n * the dialog from a portal it does not contain (a menu, a select's list) is\n * that popup's, and the dialog ignores it.\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 builds and publishes for you ──────────────────────────────────────\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. The\n * guided start confirms its own plan (AGL-3594), so the build follows with no\n * approval to make, and the site is published, indexable, when it is done.\n * The person edits or unpublishes it afterwards.\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\n/** Labels the dialog for a reader, from its own heading. */\nconst TITLE_ID = 'ai-site-start-title'\n\n/**\n * The takeover's surface, for its paper, its bar and its footer alike: the\n * console page's own background (AGL-3596). The paper sits at elevation 0,\n * which is what keeps MUI's dark-mode elevation overlay off it.\n */\nexport const AI_SITE_START_SURFACE_SX = { bgcolor: 'background.default' } 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 blocked = null,\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 /**\n * Why the card cannot be chosen here, when the workspace can change that\n * (AGL-3976): drawn disabled with the reason, never hidden.\n */\n blocked?: string | null\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 <BlockedControl blocked={Boolean(blocked)} reason={blocked ?? ''} fullWidth>\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 </BlockedControl>\n </Card>\n )\n}\n\n/** A small label above a group of questions. */\n/**\n * Each part of the guided start links its own section of the Start with AI\n * guide (AGL-3660), so a `?` says what that one group of answers changes.\n */\nconst START_HELP = {\n choose: pluginDocsHelp('aiStart', {\n anchor: '#how-do-you-want-to-start',\n excerpt:\n 'The starter site gives you a ready-made home page to edit. Start with AI asks a few questions, then plans, writes and publishes your pages.',\n }),\n estimate: pluginDocsHelp('aiCredits', {\n anchor: '#before-a-job-starts',\n excerpt:\n 'About what a start like this costs, measured on real builds, and the most it can cost. What it really costs is what each step spends, and you can watch that add up while it runs.',\n }),\n}\n\nfunction SectionLabel({ children, help }: { children: ReactNode; help?: ReturnType<typeof pluginDocsHelp> }) {\n return (\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Typography variant=\"overline\" color=\"text.secondary\" component=\"h2\" sx={{ display: 'block' }}>\n {children}\n </Typography>\n {help ? <HelpTip {...help} /> : null}\n </Stack>\n )\n}\n\n/**\n * The guided start, as the `hostFirstRun` zone draws it, or reopened on a\n * failed start's own answers (AGL-3596): `initialAnswers` opens it on the\n * questions, filled in, for the person to adjust and start a fresh job.\n */\nexport type AiSiteStartCardProps = ConsoleHostFirstRunZoneProps & {\n initialAnswers?: AiSiteStartAnswers | null\n}\n\n/**\n * The questions are one decision a step (AGL-3898): what kind of site it is\n * (typed, or a card), who it is for, the style (cards that show each one,\n * \"Pick for me\" first and picked), the details, and a review that plans the\n * site (`ai-site-start-steps.component.tsx`). Back and Next keep every\n * answer; Enter in a text answer is Next; each step's heading takes the focus\n * when it is drawn; a step that cannot go forward says why beside the field\n * and puts the cursor in it. The answers are the same object the single form\n * held, and only the review turns them into the job, through the same\n * `aiSiteStartBrief` and `aiSiteStartInputs`. Each step is reported as a\n * funnel, with where people struggle (`ai-site-start-tracking.ts`).\n */\nexport function AiSiteStartCard({\n hostId,\n orgId,\n orgSlug,\n host,\n startBlank,\n leave,\n pendingSite,\n clientBuild,\n initialAnswers = null,\n}: AiSiteStartCardProps) {\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 // \"Start again\" on a start whose empty site was removed (AGL-3936) opens\n // Create site with that start's answers: read once, and only here, where\n // the site does not exist yet. It opens on the first question, where the\n // site's name and address are asked, with every answer after it filled in.\n const [handedOff] = useState(() => (pendingSite && !initialAnswers ? readAiSiteStartAnswers() : null))\n // A site staff are building for a client (AGL-3958): the intake answered\n // the questions already, so the start opens on the review with them, and\n // stays on this screen once it has started. Read once, like a reopened start.\n const [clientAnswers] = useState(() =>\n clientBuild && !initialAnswers ? aiSiteStartAnswersFromClientBuild(clientBuild.intake) : null,\n )\n const [clientIntake] = useState(() => clientBuild?.intake ?? null)\n const clientBuildRef = useRef(clientBuild)\n clientBuildRef.current = clientBuild\n // Read once: a start reopened on its answers stays one while it is open.\n const reopened = useRef(initialAnswers !== null || handedOff !== null || clientAnswers !== null)\n // A start reopened on its answers opens on the review, every answer there to change.\n const [step, setStep] = useState<AiSiteStartStep>(initialAnswers || clientAnswers ? 'review' : 'choose')\n const [answers, setAnswers] = useState<AiSiteStartAnswers>(\n initialAnswers ?? clientAnswers ?? handedOff ?? AI_SITE_START_ANSWERS,\n )\n // The steps whose reasons not to go forward are shown: the ones Next was refused on.\n const [checked, setChecked] = useState<ReadonlySet<AiSiteStartStep>>(() => new Set())\n // A step opened from the review's Edit goes back to the review on Next.\n const [returnTo, setReturnTo] = useState<AiSiteStartStep | null>(null)\n // Each step's heading takes the focus when the step changes, never on the first draw.\n const headingRef = useRef<HTMLHeadingElement | null>(null)\n const drawnStep = useRef<AiSiteStartStep | null>(null)\n const fieldRefs = useRef<Partial<Record<AiSiteStartField, HTMLInputElement | null>>>({})\n const theme = useTheme()\n const compact = useMediaQuery(theme.breakpoints.down('sm'), { noSsr: true })\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 // Whether the Style step asks for the sites the person likes (AGL-3975),\n // read off the same verdict request: `release_ai_site_inspiration`.\n const [siteInspiration, setSiteInspiration] = useState(false)\n // What the Free workspace has left this month (AGL-3660), as the same read\n // the create door refuses on: the less of its own band and its owner's\n // allowance across their Free workspaces. `null` when the route said none.\n const [freeCredits, setFreeCredits] = useState<AiFreeCreditsLeft | null>(null)\n // \"Leave without your answers?\" (AGL-3660): a dismissal with a brief typed.\n const [confirmingLeave, setConfirmingLeave] = useState(false)\n // The dialog's own root, to tell its Escape from a nested popup's.\n const dialogRef = useRef<HTMLDivElement | null>(null)\n // Drawn by \"Create site\" before the site exists (AGL-3761): the shell's\n // name and address are asked on step 1, and the site is created only by\n // the starter, Skip, or Plan my site. Read through a ref by the verdict\n // below, which must not re-run when the shell re-renders it.\n const pendingRef = useRef(pendingSite)\n pendingRef.current = pendingSite\n const creating = Boolean(pendingSite) && !pendingSite?.created\n // The funnel (AGL-3898): each step drawn, left and abandoned, and where people struggle.\n const tracker = useAiSiteStartTracking(step, verdict === 'ready')\n // \"Bring my site\" (AGL-3976): asked of its own door, absent while its flag\n // is off, drawn disabled with the reason where the workspace can change it.\n const [importAvailability, setImportAvailability] = useState<AiSiteImportAvailability>({ state: 'checking' })\n const [importing, setImporting] = 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 // `siteImport=1`: whether \"Bring my site\" is offered here (AGL-3976), on the same read.\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1&siteImport=1`,\n )\n const payload = response.ok ? await response.json().catch(() => null) : null\n if (active) {\n setSiteInspiration(payload?.siteInspiration === true)\n if (payload?.freeTaste === true) {\n setFreeTaste(true)\n const credits = payload?.freeCredits as AiFreeCreditsLeft | undefined\n if (credits && Number.isFinite(credits.left)) setFreeCredits(credits)\n // A reopened start keeps the pages it asked for, within the band.\n setAnswers((current) => ({\n ...current,\n pages: reopened.current ? Math.min(current.pages, AI_SITE_FREE_PAGES.max) : AI_SITE_FREE_PAGES.max,\n welcomeEmail: false,\n }))\n }\n const shown = response.status !== 404 && response.status !== 403\n setImportAvailability(aiSiteImportOfferOf(payload?.siteImport))\n setVerdict(shown ? 'ready' : 'hidden')\n // \"Create site\" shows its own dialog when this one will not draw.\n pendingRef.current?.ready(shown)\n clientBuildRef.current?.ready?.(shown)\n }\n } catch {\n if (active) {\n setVerdict('hidden')\n pendingRef.current?.ready(false)\n clientBuildRef.current?.ready?.(false)\n }\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 // A Free start past what is left asks first (AGL-3722): it is admitted on\n // its measured p90, and one past what is left starts only on \"Build what\n // fits\" (pausing with Resume where the credits run out), or smaller as the\n // home page alone. The create door asks the same, so a stale dialog meets\n // the prompt there too (`serverPrompt`). Nothing at all left is a dead\n // stop until the credits renew or the workspace upgrades.\n const [serverPrompt, setServerPrompt] = useState<AiCreditsPrompt | null>(null)\n // The door's prompt was for the pages it was asked with.\n useEffect(() => setServerPrompt(null), [answers.pages])\n // A store's first products and a blog's first posts are part of the start (AGL-3676, AGL-3798).\n const siteKind = aiSiteKindOfInputs(aiSiteStartInputs(answers))?.id ?? null\n const prompt = freeTaste ? (aiFreeSitePrompt(freeCredits, answers.pages, { siteKind }) ?? serverPrompt) : null\n const noneLeft = freeTaste && freeCredits !== null && freeCredits.left <= 0\n const refusal = aiSiteStartRefusal(answers, { freeTaste })\n const band = aiSitePagesBand(freeTaste)\n // On a paid plan the person picks the model that builds the site (AGL-3660),\n // Auto by default, from the models their plan allows; every step of the job\n // runs on it. A Free workspace builds on the default and is offered none.\n const modelChoice = useAiModelChoice({ orgId, hostId, surface: 'jobs', kind: 'job.page' })\n // A pick remembered from before needs its cost to estimate by; Auto does not,\n // and the list is otherwise read when the picker opens.\n const { load: loadModels, model: rememberedModel } = modelChoice\n useEffect(() => {\n if (!freeTaste && step === 'review' && rememberedModel) loadModels()\n }, [freeTaste, step, rememberedModel, loadModels])\n const pickedModel = freeTaste ? null : modelChoice.model\n\n const plan = useCallback(async (options: { creditsConfirmed?: boolean; pages?: number } = {}) => {\n const asked = options.pages ? { ...answers, pages: options.pages } : answers\n if (!orgId || aiSiteStartRefusal(asked, { freeTaste }) || noneLeft) return\n // Past what is left, only an explicit go-ahead starts it.\n if (freeTaste && !options.creditsConfirmed && aiFreeSitePrompt(freeCredits, asked.pages, { siteKind })) return\n if (options.pages) answer({ pages: options.pages })\n setBusy(true)\n setNotice(null)\n setServerPrompt(null)\n try {\n const requestJob = (siteId: string | null, check: boolean) =>\n authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId: siteId,\n kind: 'site',\n // A client build's intake rides along (AGL-3958): the name and\n // towns as the inputs the questions leave empty (the towns only\n // while no address is given, AGL-3995), and how to reach the\n // business, its hours and the owner's words in the brief.\n brief: [aiSiteStartBrief(asked), clientIntake ? aiSiteStartClientBrief(clientIntake) : '']\n .filter(Boolean)\n .join(' '),\n // The guided start confirms its own plan (AGL-3594): the build\n // follows the plan with no approval to make.\n // The sites they like go only while the start asks for them (AGL-3975).\n inputs: {\n ...aiSiteStartInputs(siteInspiration ? asked : { ...asked, inspiration: [] }),\n ...(clientIntake ? aiSiteStartClientInputs(clientIntake, asked) : {}),\n [AI_JOB_AUTO_CONFIRM_INPUT]: true,\n },\n ...(pickedModel ? { model: pickedModel } : {}),\n ...(options.creditsConfirmed ? { creditsConfirmed: true } : {}),\n ...(check ? { check: true } : {}),\n }),\n })\n // A refusal said in the dialog, or the Free go-ahead it asks for.\n const refused = async (response: Response): Promise<unknown> => {\n const payload = await response.json().catch(() => null)\n if (response.ok) return payload ?? {}\n if (response.status === 409 && payload?.code === AI_CREDITS_CONFIRM_CODE && payload?.credits) {\n setServerPrompt(payload.credits as AiCreditsPrompt)\n return null\n }\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 null\n }\n // The site, created now when it does not exist yet (AGL-3761): this is\n // the moment the person asked for it. The start is checked first\n // (AGL-3859), so one the jobs route would refuse, or would ask the Free\n // go-ahead for, is answered before any site exists. A refusal about the\n // name or address goes back to step 1, where they are typed.\n let site = { hostId, host, orgSlug }\n if (!hostId && pendingSite) {\n if (!pendingSite.created && !(await refused(await requestJob(null, true)))) return\n const made = await pendingSite.create()\n if ('error' in made) {\n setNotice(made.error)\n if (made.address) setStep('choose')\n return\n }\n site = { hostId: made.hostId, host: made.host, orgSlug: made.orgSlug || orgSlug }\n }\n if (!site.hostId) return\n const payload = (await refused(await requestJob(site.hostId, false))) as { job?: AiJobSummary } | null\n if (!payload) return\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 // A removed start's answers have been used (AGL-3936).\n clearAiSiteStartAnswers()\n // Which optional details the site was asked for with (AGL-3979), by id only.\n tracker.detailsGiven(\n aiSiteStartExtrasGiven(asked).map((field) => ({\n field,\n step: AI_SITE_START_STEPS.find((at) => AI_SITE_START_STEP_FIELDS[at].includes(field)) ?? 'details',\n })),\n )\n // The funnel's last step: the site is started, and nothing after is leaving it.\n tracker.complete()\n tracker.finish()\n // A client build follows its job here, on the build's own screen, which\n // goes on to send the owner the preview (AGL-3958).\n if (clientIntake) {\n clientBuildRef.current?.onStarted?.({ id: created.id })\n setStarted(created)\n return\n }\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(site.orgSlug, site.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, answer, freeTaste, freeCredits, noneLeft, orgSlug, host, leave, router, pickedModel, pendingSite, siteKind, tracker, clientIntake, siteInspiration])\n\n // The zone's `startBlank`. Before the site exists it creates it with the\n // starter, and answers `false` when it could not — most often a name or\n // address to fix, which step 1 shows.\n const runStartBlank = useCallback(() => {\n const result = startBlank()\n if (result && typeof result.then === 'function') {\n void result.then((done) => {\n if (done) return\n setStartingStarter(false)\n setStep('choose')\n })\n }\n }, [startBlank])\n\n const chooseStarter = useCallback(() => {\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n setStartingStarter(true)\n trackEvent('site_start_choice', { choice: 'starter' })\n // The starter is a way to start, not a way to leave: no abandonment after it.\n tracker.finish()\n runStartBlank()\n }, [pendingSite, runStartBlank, tracker])\n\n const chooseImport = useCallback(() => {\n if (importAvailability.state !== 'ready') return\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n trackEvent('site_start_choice', { choice: 'import' })\n tracker.complete()\n setImporting(true)\n }, [importAvailability.state, pendingSite, tracker])\n\n const chooseAi = useCallback(() => {\n if (pendingSite && !pendingSite.created && !pendingSite.check()) return\n trackEvent('site_start_choice', { choice: 'ai' })\n tracker.complete()\n setStep('kind')\n }, [pendingSite, tracker])\n\n // The heading of each step takes the focus as the step is drawn, so a\n // keyboard and a screen reader start where the new question does.\n useEffect(() => {\n if (verdict !== 'ready') return\n if (drawnStep.current !== null && drawnStep.current !== step) headingRef.current?.focus()\n drawnStep.current = step\n }, [step, verdict])\n\n /** Forward from a question step, when its answers may go forward. */\n const next = useCallback(() => {\n if (step === 'choose' || step === 'review') return\n const errors = aiSiteStartStepErrors(step, answers, { freeTaste })\n if (errors.length) {\n setChecked((current) => new Set(current).add(step))\n for (const error of errors) tracker.fieldError(error.field, error.code)\n fieldRefs.current[errors[0].field]?.focus()\n return\n }\n tracker.complete()\n const target = returnTo ?? aiSiteStartNextStep(step)\n setReturnTo(null)\n // A review reached with an answer that cannot go forward opens on that answer instead.\n if (target === 'review') {\n const invalid = aiSiteStartFirstInvalidStep(answers, { freeTaste })\n if (invalid && invalid !== 'review') {\n setChecked((current) => new Set(current).add(invalid))\n setStep(invalid)\n return\n }\n }\n if (target) setStep(target)\n }, [step, answers, freeTaste, returnTo, tracker])\n\n /** Back one step, keeping every answer. */\n const back = useCallback(() => {\n const target = aiSiteStartPreviousStep(step)\n if (!target) return\n tracker.back()\n setReturnTo(null)\n setStep(target)\n }, [step, tracker])\n\n /** From the review, to the step that asks an answer, and back to the review on Next. */\n const edit = useCallback(\n (target: AiSiteStartStep) => {\n tracker.back()\n setReturnTo('review')\n setStep(target)\n },\n [tracker],\n )\n\n /** From the progress, back to a step already answered: from the review it is Edit. */\n const goBackTo = (target: AiSiteStartStep) => {\n if (aiSiteStartStepIndex(target) >= aiSiteStartStepIndex(step)) return\n if (step === 'review') {\n edit(target)\n return\n }\n tracker.back()\n setReturnTo(null)\n setStep(target)\n }\n\n if (verdict !== 'ready') return null\n\n // Before a job is started Skip is the blank site, which writes the starter\n // (AGL-3594); after, it only closes — the job builds the site.\n const exit = started ? (leave ?? runStartBlank) : runStartBlank\n // Skip, as the header draws it: before a site is started it is leaving the questions.\n const skip = () => {\n if (!started) tracker.abandon('skip')\n exit()\n }\n // Escape and the close control. Before \"Create site\" has made the site\n // (AGL-3761) there is nothing to give a starter to, so they only close,\n // and nothing is created; otherwise they are the same way out as Skip.\n const dismissTo = creating && !started ? (leave ?? runStartBlank) : exit\n // Anything the person typed or picked, which a dismissal must not discard\n // silently (AGL-3660). After a job starts there is nothing left to lose.\n const drafted =\n !started &&\n Boolean(\n answers.siteType.trim() ||\n answers.audience.trim() ||\n answers.kind !== null ||\n aiSiteStartExtrasGiven(answers).length > 0 ||\n (creating && pendingSite?.dirty),\n )\n // Escape and the close control: a dismissal, never a choice. Escape that\n // bubbled here through React from a portal outside this dialog's own DOM is\n // a nested menu's, already handled there, and is not this dialog's to act on.\n const dismiss = (event?: SyntheticEvent | object, reason?: string) => {\n if (reason === 'escapeKeyDown') {\n const target = (event as SyntheticEvent | undefined)?.target\n const root = dialogRef.current\n if (root && target instanceof Node && !root.contains(target)) return\n }\n if (drafted) setConfirmingLeave(true)\n else closeTo()\n }\n // The dismissal itself, once nothing typed is in the way of it.\n const closeTo = () => {\n if (!started) tracker.abandon('close')\n dismissTo()\n }\n const leaveDraft = () => {\n setConfirmingLeave(false)\n closeTo()\n }\n\n // The chosen model's cost against Auto's, as the model list states it.\n const modelMultiplier =\n (pickedModel && modelChoice.options?.options.find((option) => option.id === pickedModel)?.multiplier) || 1\n // What the start is likely to cost and at most (AGL-3722): \"About 137\n // credits (up to 283)\", the measured figures scaled by the chosen model.\n const scaled = (range: AiCreditRange): AiCreditRange => ({\n likely: Math.round(range.likely * modelMultiplier),\n p90: Math.round(range.p90 * modelMultiplier),\n ceiling: Math.round(range.ceiling * modelMultiplier),\n })\n const estimate = freeTaste\n ? aiFreeSiteCreditRange(answers.pages, { siteKind })\n : scaled(aiSiteCreditRange(answers.pages, { welcomeEmail: answers.welcomeEmail, siteKind }))\n // A Free start quotes what is LEFT (AGL-3660), shared across the owner's\n // Free workspaces — never the month's whole allowance as if none were spent.\n const estimateText = freeTaste\n ? freeCredits\n ? `${aiCreditRangeText(estimate)}. You have ${freeCredits.left.toLocaleString('en-US')} of your ${freeCredits.total.toLocaleString('en-US')} free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}`\n : `${aiCreditRangeText(estimate)}, of the ${FREE_AI_TASTE_CREDITS_PER_MONTH} AI credits you get free each month`\n : `${aiCreditRangeText(estimate)}, estimated`\n\n // The card is drawn wherever the workspace could use it, disabled where it cannot yet.\n const showsImport = importAvailability.state === 'ready' || importAvailability.state === 'blocked'\n const choosing = step === 'choose' && !started && !importing\n const reviewing = step === 'review' && !started && !importing\n const asking = step !== 'choose' && !started && !importing\n // Each answer's reason not to go forward, shown on a step once Next was refused there.\n const fieldErrors = aiSiteStartFieldErrors(answers, { freeTaste })\n const shownErrors = Object.fromEntries(\n AI_SITE_START_STEP_FIELDS[step]\n .filter((field) => checked.has(step) && fieldErrors[field])\n .map((field) => [field, fieldErrors[field]]),\n )\n const stepProps = {\n answers,\n answer,\n tracker,\n errors: shownErrors,\n fieldRef: (field: AiSiteStartField) => (node: HTMLInputElement | null) => {\n fieldRefs.current[field] = node\n },\n }\n const HEADINGS: Record<AiSiteStartStep, { title: string; lede: string }> = {\n choose: {\n title: 'How do you want to start?',\n lede: creating ? 'Name your site, then choose how it starts. You can change everything later.' : 'You can change everything later.',\n },\n kind: {\n title: 'What are you building?',\n lede: `Say it in a few words, or pick the closest. ${PLATFORM_BRAND_NAME} AI plans and writes your pages from it.`,\n },\n audience: { title: 'Who is it for?', lede: 'The pages and the contact form are written for them.' },\n style: {\n title: 'Choose a style',\n lede: 'Each picture is an example home page in that style. Yours is written for your business, with its own colors and fonts within the style you pick.',\n },\n details: {\n title: 'A few details',\n lede: 'How customers reach you, where messages go, how many pages, and what else to draft. All can be changed later.',\n },\n review: {\n title: 'Review your site',\n lede: started\n ? `${PLATFORM_BRAND_NAME} AI is on it.`\n : `${PLATFORM_BRAND_NAME} AI plans and writes your pages from these answers, then publishes your site.`,\n },\n }\n const heading = importing\n ? {\n title: 'Bring my site',\n lede: `Bring the website you have now into ${PLATFORM_BRAND_NAME}: its pages, words and photos. First, prove it is yours.`,\n }\n : HEADINGS[step]\n // Enter in a text answer is Next. A select, a card or a button keeps its own Enter.\n const onStepKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' || event.nativeEvent.isComposing || !asking || reviewing) return\n const target = event.target as HTMLElement\n if (target.tagName !== 'INPUT' || (target as HTMLInputElement).type === 'hidden') return\n event.preventDefault()\n next()\n }\n\n return (\n <Dialog\n open\n fullScreen\n ref={dialogRef}\n onClose={dismiss}\n aria-labelledby={TITLE_ID}\n // Where people get lost (AGL-3898): any input puts off the idle signal,\n // and every click is read for rage and dead clicks.\n onClickCapture={(event: MouseEvent<HTMLElement>) => tracker.click(aiSiteStartClickOf(event))}\n onKeyDownCapture={() => tracker.activity()}\n onInputCapture={() => tracker.activity()}\n // The console's own page surface, in both modes. A Dialog's paper sits\n // at elevation 24 by default, and in dark mode MUI lightens an elevated\n // paper with a white overlay, which turned the whole takeover a\n // washed-out gray; the overlay is for floating surfaces, and this one\n // fills the screen, so its paper is flat.\n slotProps={{ paper: { elevation: 0, sx: AI_SITE_START_SURFACE_SX } }}\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 data-start-area=\"header\"\n sx={{ ...AI_SITE_START_SURFACE_SX, borderBottom: 1, borderColor: 'divider' }}\n >\n <Toolbar variant=\"dense\" sx={{ gap: 1 }}>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={() => dismiss()}\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={skip} 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 } }} onKeyDown={onStepKeyDown}>\n {/* The step timeline spans the whole dialog, above the step's own\n narrower column (AGL-4014): MUI's Stepper as it comes. */}\n {importing ? null : (\n <Box sx={{ pt: { xs: 3, sm: 4 } }}>\n <StartProgress step={step} compact={compact} onStep={started ? undefined : goBackTo} />\n </Box>\n )}\n <Container\n maxWidth={choosing || step === 'kind' || step === 'style' ? 'md' : 'sm'}\n disableGutters\n sx={{ py: { xs: 3, sm: 5 } }}\n >\n <Stack spacing={4}>\n <Stack spacing={1} sx={{ textAlign: choosing ? 'center' : 'left' }} data-start-area=\"heading\">\n <Typography\n ref={headingRef}\n tabIndex={-1}\n variant=\"h4\"\n component=\"h1\"\n sx={{ '&:focus': { outline: 'none' }, '&:focus-visible': { outline: 'none' } }}\n >\n {heading.title}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n {choosing ? (\n <HelpTip sx={{ float: 'right' }} {...START_HELP.choose} />\n ) : importing ? null : (\n <StepHelp step={step} />\n )}\n {heading.lede}\n </Typography>\n </Stack>\n {notice && (\n <Alert severity=\"info\" data-start-area=\"notice\">\n {notice}\n </Alert>\n )}\n {asking && noneLeft && freeCredits && (\n <Alert\n severity=\"warning\"\n data-start-area=\"notice\"\n action={\n orgSlug ? (\n <Button color=\"inherit\" size=\"small\" href={aiCreditsBillingHref(orgSlug)}>\n {'Upgrade'}\n </Button>\n ) : undefined\n }\n >\n {aiFreeCreditsNoneLeftText(freeCredits.resetsOn)}\n </Alert>\n )}\n {choosing && creating && pendingSite ? (\n <Stack spacing={2} component=\"section\" aria-label=\"Your site\">\n <SectionLabel>{'Your site'}</SectionLabel>\n {pendingSite.fields}\n {/* Start in preview (AGL-4086). Off for the starter and for\n Start with AI; Bring my site asks again, defaulting on. */}\n {pendingSite.previewControl?.('ai')}\n </Stack>\n ) : null}\n {importing && orgId ? (\n <AiSiteImportStart\n orgId={orgId}\n orgSlug={orgSlug}\n hostId={hostId}\n host={host}\n user={user}\n pendingSite={pendingSite}\n onBack={() => setImporting(false)}\n leave={leave}\n />\n ) : choosing ? (\n <Stack spacing={2}>\n <Box\n data-start-area=\"choose-cards\"\n sx={{\n display: 'grid',\n gridTemplateColumns: showsImport ? { xs: '1fr', sm: '1fr 1fr', md: '1fr 1fr 1fr' } : { 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 {showsImport && (\n <ChoiceCard\n accent=\"secondary\"\n icon={mdiWebSync.path}\n iconId=\"web-sync\"\n title=\"Bring my site\"\n description={`Bring the website you have now into ${PLATFORM_BRAND_NAME}: its pages, words and photos, rewritten in your new design.`}\n blocked={importAvailability.state === 'blocked' ? importAvailability.message : null}\n onChoose={chooseImport}\n >\n {importAvailability.state === 'blocked' && importAvailability.reason === 'plan' ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Paid plans\" />\n ) : null}\n </ChoiceCard>\n )}\n </Box>\n {importAvailability.state === 'blocked' ? (\n <PrerequisiteNotice\n {...(importAvailability.reason === 'plan' && orgSlug\n ? { href: aiCreditsBillingHref(orgSlug), actionLabel: 'Upgrade' }\n : {})}\n >\n {importAvailability.message}\n </PrerequisiteNotice>\n ) : null}\n </Stack>\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. It is then built and published for you, with ' +\n 'nothing to approve. You can edit or unpublish it afterwards.'\n }\n // Full screen: AI jobs opens in the panel this dialog covers.\n onOpenJobs={exit}\n />\n </Card>\n ) : (\n <Box component=\"section\">\n {step === 'kind' && <KindStep {...stepProps} />}\n {step === 'audience' && <AudienceStep {...stepProps} />}\n {step === 'style' && (\n <StyleStep\n {...stepProps}\n inspiration={\n siteInspiration\n ? { limit: aiSiteInspirationLimit(freeTaste), upgradeHref: orgSlug ? aiCreditsBillingHref(orgSlug) : null }\n : null\n }\n />\n )}\n {step === 'details' && <DetailsStep {...stepProps} freeTaste={freeTaste} band={band} />}\n {/* Each question step's optional extras, collapsed, after its own answers (AGL-3979). */}\n {step !== 'review' && (\n <Box sx={{ mt: 3 }}>\n <MoreDetails key={step} step={step} {...stepProps} />\n </Box>\n )}\n {step === 'review' && (\n <ReviewStep answers={answers} freeTaste={freeTaste} onEdit={edit} inspiration={siteInspiration}>\n {clientIntake && (\n <Alert severity=\"info\" variant=\"outlined\" data-start-area=\"client-intake\">\n {aiSiteStartClientNote(clientIntake)}\n </Alert>\n )}\n {!noneLeft && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"site\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => void plan({ creditsConfirmed: true })}\n onSmaller={() => void plan({ creditsConfirmed: true, pages: 1 })}\n />\n )}\n </ReviewStep>\n )}\n </Box>\n )}\n </Stack>\n </Container>\n </DialogContent>\n {asking && (\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 data-start-area=\"footer\"\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 ...AI_SITE_START_SURFACE_SX,\n }}\n >\n {reviewing && (\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 <HelpTip {...START_HELP.estimate} ariaLabel=\"How the estimate works\" />\n </Stack>\n )}\n {reviewing && refusal && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {refusal}\n </Typography>\n )}\n {reviewing && !freeTaste && <AiModelSelector choice={modelChoice} disabled={busy} />}\n <Button onClick={back} sx={reviewing ? undefined : { mr: 'auto' }}>\n {'Back'}\n </Button>\n {reviewing ? (\n <Button\n variant=\"contained\"\n size=\"large\"\n disabled={busy || Boolean(refusal) || Boolean(prompt) || noneLeft}\n onClick={() => void plan()}\n startIcon={<MdiIcon path={mdiCreation.path} />}\n >\n {busy ? 'Starting…' : 'Plan my site'}\n </Button>\n ) : (\n <Button variant=\"contained\" size=\"large\" onClick={next}>\n {returnTo === 'review' ? 'Back to review' : 'Next'}\n </Button>\n )}\n </DialogActions>\n )}\n <Dialog\n open={confirmingLeave}\n onClose={() => setConfirmingLeave(false)}\n aria-labelledby={`${TITLE_ID}-leave`}\n aria-describedby={`${TITLE_ID}-leave-text`}\n maxWidth=\"xs\"\n >\n <DialogTitle id={`${TITLE_ID}-leave`}>{'Leave without your answers?'}</DialogTitle>\n <DialogContent>\n <DialogContentText id={`${TITLE_ID}-leave-text`}>\n {creating\n ? 'What you typed is not saved, and no site is created.'\n : 'What you typed is not saved. Leaving starts your site from the starter site instead.'}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={leaveDraft}>\n {creating ? 'Leave' : 'Leave and start blank'}\n </Button>\n <Button variant=\"contained\" autoFocus onClick={() => setConfirmingLeave(false)}>\n {'Keep editing'}\n </Button>\n </DialogActions>\n </Dialog>\n </Dialog>\n )\n}\n\nexport default AiSiteStartCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","PLATFORM_BRAND_NAME","trackEvent","ICON_VARIANT_CLOSE","mdiCreation","mdiPageLayoutHeaderFooter","mdiWebSync","pluginDocsHelp","HelpTip","MdiIcon","BlockedControl","PrerequisiteNotice","authorizedFetch","useUser","Alert","AppBar","Box","Button","Card","CardActionArea","Chip","CircularProgress","Container","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","Stack","Toolbar","Typography","useMediaQuery","alpha","useTheme","useRouter","useCallback","useEffect","useId","useRef","useState","AI_JOB_AUTO_CONFIRM_INPUT","AI_SITE_FREE_PAGES","aiFreeCreditsNoneLeftText","aiFreeCreditsResetLabel","aiFreeSiteCreditRange","aiFreeSitePrompt","aiSiteCreditRange","aiSitePagesBand","FREE_AI_TASTE_CREDITS_PER_MONTH","AI_CREDITS_CONFIRM_CODE","aiCreditRangeText","AiCreditsPromptNotice","AI_SITE_START_ANSWERS","aiSiteStartAnswersFromClientBuild","aiSiteStartBrief","aiSiteStartClientBrief","aiSiteStartClientInputs","aiSiteStartClientNote","aiSiteStartExtrasGiven","aiSiteStartInputs","aiSiteStartRefusal","aiSiteInspirationLimit","aiSiteStartFieldErrors","aiSiteStartFirstInvalidStep","aiSiteStartNextStep","aiSiteStartPreviousStep","aiSiteStartStepErrors","aiSiteStartStepIndex","AI_SITE_START_STEPS","AI_SITE_START_STEP_FIELDS","aiSiteKindOfInputs","aiSiteStartClickOf","useAiSiteStartTracking","AudienceStep","DetailsStep","KindStep","MoreDetails","ReviewStep","StartProgress","StepHelp","StyleStep","AiJobFollow","AiModelSelector","useAiModelChoice","aiCreditsBillingHref","aiSiteBuildHref","publishAiJob","clearAiSiteStartAnswers","readAiSiteStartAnswers","AiSiteImportStart","aiSiteImportOfferOf","TITLE_ID","AI_SITE_START_SURFACE_SX","bgcolor","IconBadge","accent","children","aria-hidden","sx","theme","width","spacing","height","borderRadius","display","alignItems","justifyContent","palette","main","mode","color","fontSize","ChoiceCard","icon","iconId","title","description","busy","blocked","onChoose","id","variant","transition","transitions","create","duration","shorter","borderColor","boxShadow","shadows","transform","Boolean","reason","fullWidth","onClick","disabled","aria-labelledby","aria-describedby","aria-busy","undefined","p","xs","sm","textAlign","size","aria-label","path","data-icon","component","START_HELP","choose","anchor","excerpt","estimate","SectionLabel","help","direction","AiSiteStartCard","hostId","orgId","orgSlug","host","startBlank","leave","pendingSite","clientBuild","initialAnswers","modelChoice","data","user","router","userRef","current","uid","verdict","setVerdict","handedOff","clientAnswers","intake","clientIntake","clientBuildRef","reopened","step","setStep","answers","setAnswers","checked","setChecked","Set","returnTo","setReturnTo","headingRef","drawnStep","fieldRefs","compact","breakpoints","down","noSsr","setBusy","startingStarter","setStartingStarter","notice","setNotice","started","setStarted","freeTaste","setFreeTaste","siteInspiration","setSiteInspiration","freeCredits","setFreeCredits","confirmingLeave","setConfirmingLeave","dialogRef","pendingRef","creating","created","tracker","importAvailability","setImportAvailability","state","importing","setImporting","active","response","encodeURIComponent","payload","ok","json","catch","credits","Number","isFinite","left","pages","Math","min","max","welcomeEmail","shown","status","siteImport","ready","answer","patch","serverPrompt","setServerPrompt","siteKind","prompt","noneLeft","refusal","band","surface","kind","load","loadModels","model","rememberedModel","pickedModel","plan","options","asked","creditsConfirmed","requestJob","siteId","check","method","headers","body","JSON","stringify","brief","filter","join","inputs","inspiration","refused","code","locked","String","error","site","made","address","job","detailsGiven","map","field","find","at","includes","complete","finish","onStarted","href","push","runStartBlank","result","then","done","chooseStarter","choice","chooseImport","chooseAi","focus","next","errors","length","add","fieldError","target","invalid","back","edit","goBackTo","exit","skip","abandon","dismissTo","drafted","siteType","trim","audience","dirty","dismiss","event","root","Node","contains","closeTo","leaveDraft","modelMultiplier","option","multiplier","scaled","range","likely","round","p90","ceiling","estimateText","toLocaleString","total","resetsOn","showsImport","choosing","reviewing","asking","fieldErrors","shownErrors","Object","fromEntries","has","stepProps","fieldRef","node","HEADINGS","lede","style","details","review","heading","onStepKeyDown","key","nativeEvent","isComposing","tagName","type","preventDefault","open","fullScreen","ref","onClose","onClickCapture","click","onKeyDownCapture","activity","onInputCapture","slotProps","paper","elevation","position","enableColorOnDark","data-start-area","borderBottom","gap","edge","noWrap","textOverflow","flex","px","onKeyDown","pt","onStep","maxWidth","disableGutters","py","tabIndex","outline","float","severity","action","fields","previewControl","onBack","gridTemplateColumns","md","label","message","actionLabel","intro","onOpenJobs","limit","upgradeHref","mt","onEdit","noun","onBuildWhatFits","onSmaller","flexWrap","rowGap","columnGap","borderTop","mr","minWidth","ariaLabel","startIcon","autoFocus"],"mappings":"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"}
|
|
@@ -21,8 +21,8 @@ export declare const AI_IMAGE_READS_PATH = "ai/image-reads";
|
|
|
21
21
|
export declare const AI_IMAGE_READS_SIGNATURE = "x-aglyn-images-signature";
|
|
22
22
|
/** How far a signed request's moment may be from the function's clock. */
|
|
23
23
|
export declare const AI_IMAGE_READS_WINDOW_MS: number;
|
|
24
|
-
/** The
|
|
25
|
-
export type AiImageReadsOp = 'product-photo' | 'logo-colors';
|
|
24
|
+
/** The reads the function makes; `logo-raster` turns a site import's SVG logo into a PNG (AGL-4084). */
|
|
25
|
+
export type AiImageReadsOp = 'product-photo' | 'logo-colors' | 'logo-raster';
|
|
26
26
|
/** One request's body: the asset by its media reference, and whose read it is. */
|
|
27
27
|
export interface AiImageReadsRequest {
|
|
28
28
|
op: AiImageReadsOp;
|
|
@@ -57,3 +57,21 @@ export declare function aiImageReadsRequest(op: AiImageReadsOp, location: AiMedi
|
|
|
57
57
|
* step's own bound for it (`signal`). Throws on anything but an answer.
|
|
58
58
|
*/
|
|
59
59
|
export declare function requestAiImageReads<T>(origin: string, payload: AiImageReadsRequest, signal: AbortSignal): Promise<T>;
|
|
60
|
+
/** The largest SVG turned into a picture. */
|
|
61
|
+
export declare const AI_LOGO_RASTER_MAX_BYTES: number;
|
|
62
|
+
/** The tallest picture an SVG logo is drawn at. */
|
|
63
|
+
export declare const AI_LOGO_RASTER_MAX_HEIGHT = 512;
|
|
64
|
+
/** The request for an SVG logo drawn as a PNG: the drawing itself, never an address, and the height. */
|
|
65
|
+
export interface AiLogoRasterRequest {
|
|
66
|
+
op: 'logo-raster';
|
|
67
|
+
svg: string;
|
|
68
|
+
height: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* An SVG logo drawn as a PNG of the height asked, in this process (AGL-4084).
|
|
72
|
+
* `sharp`'s SVG reader loads nothing the drawing links to, so only its own
|
|
73
|
+
* shapes are drawn. `null` for what is not a drawing, or is too large.
|
|
74
|
+
*/
|
|
75
|
+
export declare function rasterizeAiLogoSvg(svg: Uint8Array, height: number): Promise<Uint8Array | null>;
|
|
76
|
+
/** An SVG logo drawn as a PNG by the images function (AGL-4084); `null` on anything but a picture. */
|
|
77
|
+
export declare function requestAiLogoRaster(origin: string, svg: Uint8Array, height: number): Promise<Uint8Array | null>;
|
|
@@ -134,5 +134,52 @@ function signature(at, body) {
|
|
|
134
134
|
if (!response.ok) throw new Error(`the AI image reads function answered ${response.status}`);
|
|
135
135
|
return await response.json();
|
|
136
136
|
}
|
|
137
|
+
/*------------------------------------------
|
|
138
|
+
* A site import's SVG logo, as a picture (AGL-4084)
|
|
139
|
+
*-----------------------------------------*/ /** The largest SVG turned into a picture. */ export const AI_LOGO_RASTER_MAX_BYTES = 512 * 1024;
|
|
140
|
+
/** The tallest picture an SVG logo is drawn at. */ export const AI_LOGO_RASTER_MAX_HEIGHT = 512;
|
|
141
|
+
/**
|
|
142
|
+
* An SVG logo drawn as a PNG of the height asked, in this process (AGL-4084).
|
|
143
|
+
* `sharp`'s SVG reader loads nothing the drawing links to, so only its own
|
|
144
|
+
* shapes are drawn. `null` for what is not a drawing, or is too large.
|
|
145
|
+
*/ export async function rasterizeAiLogoSvg(svg, height) {
|
|
146
|
+
if (!svg.byteLength || svg.byteLength > AI_LOGO_RASTER_MAX_BYTES) return null;
|
|
147
|
+
const text = Buffer.from(svg).toString('utf8');
|
|
148
|
+
if (!/<svg[\s>]/i.test(text)) return null;
|
|
149
|
+
const { loadAiMediaSharp } = await import("./ai-media-asset.js");
|
|
150
|
+
const sharp = await loadAiMediaSharp();
|
|
151
|
+
const size = Math.max(16, Math.min(AI_LOGO_RASTER_MAX_HEIGHT, Math.round(height)));
|
|
152
|
+
const png = await sharp(Buffer.from(svg), {
|
|
153
|
+
limitInputPixels: 64000000,
|
|
154
|
+
density: 300
|
|
155
|
+
}).resize({
|
|
156
|
+
height: size
|
|
157
|
+
}).png().toBuffer();
|
|
158
|
+
return new Uint8Array(png);
|
|
159
|
+
}
|
|
160
|
+
/** An SVG logo drawn as a PNG by the images function (AGL-4084); `null` on anything but a picture. */ export async function requestAiLogoRaster(origin, svg, height) {
|
|
161
|
+
if (!svg.byteLength || svg.byteLength > AI_LOGO_RASTER_MAX_BYTES) return null;
|
|
162
|
+
const body = JSON.stringify({
|
|
163
|
+
op: 'logo-raster',
|
|
164
|
+
svg: Buffer.from(svg).toString('base64'),
|
|
165
|
+
height
|
|
166
|
+
});
|
|
167
|
+
try {
|
|
168
|
+
const response = await fetch(`${origin}/api/${PLUGIN_IMAGES_API_PREFIX}/${AI_IMAGE_READS_PATH}`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: _extends({
|
|
171
|
+
'content-type': 'application/json',
|
|
172
|
+
[AI_IMAGE_READS_SIGNATURE]: signAiImageReadsRequest(body)
|
|
173
|
+
}, edgeBypassHeaders()),
|
|
174
|
+
body,
|
|
175
|
+
signal: AbortSignal.timeout(15000)
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) return null;
|
|
178
|
+
const answer = await response.json();
|
|
179
|
+
return typeof answer.png === 'string' && answer.png ? new Uint8Array(Buffer.from(answer.png, 'base64')) : null;
|
|
180
|
+
} catch (unused) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
137
184
|
|
|
138
185
|
//# sourceMappingURL=ai-image-reads.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-image-reads.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 { createHash, createHmac, timingSafeEqual } from 'node:crypto'\nimport { PLUGIN_IMAGES_API_PREFIX } from '@aglyn/aglyn/app-utils/api-plugins'\nimport { formatMediaRef } from '@aglyn/aglyn/app-utils/media-ref'\nimport { platformConsoleOrigin } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { tokenSigningSecret } from '@aglyn/tenant-data-admin/server/media-signing'\nimport type { AiMediaAssetLocation } from './ai-media-asset'\n\n/*==========================================\n * A JOB'S IMAGE READS HAVE THEIR OWN FUNCTION (AGL-4048).\n *\n * Two job steps decode a picture from a site's media library with `sharp`:\n * the products step makes the JPEG of a product's first photo that the\n * model is shown (`ai-product-image.ts`), and the theme step reduces the\n * site logo to its dominant colors (`ai-theme-brand-inputs.ts`). `sharp`\n * takes libvips, a 17 MB native library that on Vercel only the console's\n * plugins' image function ships: neither the plugin dispatcher, where a door\n * runs a job's first step, nor the AI jobs beat, which runs every step after\n * it, carries it. So the read is a route of its own, registered to draw\n * images (`images: true`) and served at `/api/plugin-images/ai/image-reads`\n * (`server/ai-image-reads-route.ts`), and on Vercel a step asks it:\n *\n * product-photo {ref, hostId, orgId} `{read}`: the photo as the step\n * sends it, or why it was not read\n * logo-colors {ref, hostId, orgId} `{colors}`: the logo's dominant\n * colors, or null\n *\n * The function locates the asset again from the reference, the site and\n * the org, under the same rule the step used, and reads it the way the media\n * CDN would serve it, so it reads nothing the step could not have read\n * itself. Each request is signed with the platform's signing secret over the\n * moment and the body, as the card reader start-up screen's are, under this\n * route's own name, so a signature for one route is none for another. Off\n * Vercel one process holds every route, and libvips with it, so the read\n * runs where it is asked for, as before.\n *\n * Any failure is the read's own: the photo is unreadable and the copy is\n * written from the product's words, or the logo's colors are left out with\n * the note that says so.\n *=========================================*/\n\n/** The images route's registered path, served under `/api/plugin-images/`. */\nexport const AI_IMAGE_READS_PATH = 'ai/image-reads'\n\n/** The request header that carries the signature: `t=<ms>,v1=<hex>`. */\nexport const AI_IMAGE_READS_SIGNATURE = 'x-aglyn-images-signature'\n\n/** How far a signed request's moment may be from the function's clock. */\nexport const AI_IMAGE_READS_WINDOW_MS = 5 * 60 * 1000\n\n/** The two reads the function makes. */\nexport type AiImageReadsOp = 'product-photo' | 'logo-colors'\n\n/** One request's body: the asset by its media reference, and whose read it is. */\nexport interface AiImageReadsRequest {\n op: AiImageReadsOp\n ref: string\n hostId: string\n orgId: string\n}\n\nlet secretOverride: (() => string) | null = null\n\n/** Test seam. */\nexport function setAiImageReadsSecretForTests(secret: (() => string) | null): void {\n secretOverride = secret\n}\n\nfunction signature(at: number, body: string): string {\n const digest = createHash('sha256').update(body).digest('hex')\n return createHmac('sha256', (secretOverride ?? tokenSigningSecret)())\n .update(`ai-image-reads:${at}.${digest}`)\n .digest('hex')\n}\n\n/** The header value for a body signed now. Throws on a deploy with no signing secret. */\nexport function signAiImageReadsRequest(body: string, nowMs: number = Date.now()): string {\n return `t=${nowMs},v1=${signature(nowMs, body)}`\n}\n\n/**\n * Whether a request's signature is ours, over this body, and recent. False\n * for anything else, a deploy with no signing secret included (fail closed).\n */\nexport function verifyAiImageReadsRequest(\n header: string | null | undefined,\n body: string,\n nowMs: number = Date.now(),\n): boolean {\n const parts = Object.fromEntries(\n String(header ?? '')\n .split(',')\n .map((part) => part.trim().split('=') as [string, string])\n .filter((pair) => pair.length === 2),\n )\n const at = Number(parts['t'])\n const given = String(parts['v1'] ?? '')\n if (!Number.isFinite(at) || !/^[a-f0-9]{64}$/.test(given)) return false\n if (Math.abs(nowMs - at) > AI_IMAGE_READS_WINDOW_MS) return false\n let expected: string\n try {\n expected = signature(at, body)\n } catch {\n return false\n }\n return timingSafeEqual(Buffer.from(given), Buffer.from(expected))\n}\n\n/**\n * Where to ask for an image read, or `null` to make it in this process. On\n * Vercel only the images function ships libvips, so every other function\n * asks it, at the console's own address; anywhere else this process can\n * decode.\n */\nexport function aiImageReadsOrigin(): string | null {\n if (process.env['VERCEL'] !== '1') return null\n return platformConsoleOrigin()\n}\n\n/**\n * The request for a located asset's read: the asset named by its media\n * reference, which the function locates again. `null` for a location no\n * reference can name, which is never read.\n */\nexport function aiImageReadsRequest(\n op: AiImageReadsOp,\n location: AiMediaAssetLocation,\n hostId: string,\n orgId: string,\n): AiImageReadsRequest | null {\n const ref = formatMediaRef(location.scopeSegment, location.mediaId)\n return ref ? { op, ref, hostId, orgId } : null\n}\n\n/**\n * The headers that get our own request past our own edge, when configured:\n * the same pair the render monitor sends (AGL-3571).\n */\nfunction edgeBypassHeaders(): Record<string, string> {\n const probe = String(process.env['AGLYN_PROBE_TOKEN'] ?? '').trim()\n const bypass = String(\n process.env['AGLYN_VERCEL_BYPASS'] || process.env['VERCEL_AUTOMATION_BYPASS_SECRET'] || '',\n ).trim()\n return {\n ...(probe ? { 'x-aglyn-probe': probe } : {}),\n ...(bypass ? { 'x-vercel-protection-bypass': bypass } : {}),\n }\n}\n\n/**\n * Asks the images function for one read, signed, waiting no longer than the\n * step's own bound for it (`signal`). Throws on anything but an answer.\n */\nexport async function requestAiImageReads<T>(\n origin: string,\n payload: AiImageReadsRequest,\n signal: AbortSignal,\n): Promise<T> {\n const body = JSON.stringify(payload)\n const response = await fetch(`${origin}/api/${PLUGIN_IMAGES_API_PREFIX}/${AI_IMAGE_READS_PATH}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n [AI_IMAGE_READS_SIGNATURE]: signAiImageReadsRequest(body),\n ...edgeBypassHeaders(),\n },\n body,\n signal,\n })\n if (!response.ok) throw new Error(`the AI image reads function answered ${response.status}`)\n return (await response.json()) as T\n}\n"],"names":["createHash","createHmac","timingSafeEqual","PLUGIN_IMAGES_API_PREFIX","formatMediaRef","platformConsoleOrigin","tokenSigningSecret","AI_IMAGE_READS_PATH","AI_IMAGE_READS_SIGNATURE","AI_IMAGE_READS_WINDOW_MS","secretOverride","setAiImageReadsSecretForTests","secret","signature","at","body","digest","update","signAiImageReadsRequest","nowMs","Date","now","verifyAiImageReadsRequest","header","parts","Object","fromEntries","String","split","map","part","trim","filter","pair","length","Number","given","isFinite","test","Math","abs","expected","Buffer","from","aiImageReadsOrigin","process","env","aiImageReadsRequest","op","location","hostId","orgId","ref","scopeSegment","mediaId","edgeBypassHeaders","probe","bypass","requestAiImageReads","origin","payload","signal","JSON","stringify","response","fetch","method","headers","ok","Error","status","json"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-image-reads.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 { createHash, createHmac, timingSafeEqual } from 'node:crypto'\nimport { PLUGIN_IMAGES_API_PREFIX } from '@aglyn/aglyn/app-utils/api-plugins'\nimport { formatMediaRef } from '@aglyn/aglyn/app-utils/media-ref'\nimport { platformConsoleOrigin } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { tokenSigningSecret } from '@aglyn/tenant-data-admin/server/media-signing'\nimport type { AiMediaAssetLocation } from './ai-media-asset'\n\n/*==========================================\n * A JOB'S IMAGE READS HAVE THEIR OWN FUNCTION (AGL-4048).\n *\n * Two job steps decode a picture from a site's media library with `sharp`:\n * the products step makes the JPEG of a product's first photo that the\n * model is shown (`ai-product-image.ts`), and the theme step reduces the\n * site logo to its dominant colors (`ai-theme-brand-inputs.ts`). `sharp`\n * takes libvips, a 17 MB native library that on Vercel only the console's\n * plugins' image function ships: neither the plugin dispatcher, where a door\n * runs a job's first step, nor the AI jobs beat, which runs every step after\n * it, carries it. So the read is a route of its own, registered to draw\n * images (`images: true`) and served at `/api/plugin-images/ai/image-reads`\n * (`server/ai-image-reads-route.ts`), and on Vercel a step asks it:\n *\n * product-photo {ref, hostId, orgId} `{read}`: the photo as the step\n * sends it, or why it was not read\n * logo-colors {ref, hostId, orgId} `{colors}`: the logo's dominant\n * colors, or null\n *\n * The function locates the asset again from the reference, the site and\n * the org, under the same rule the step used, and reads it the way the media\n * CDN would serve it, so it reads nothing the step could not have read\n * itself. Each request is signed with the platform's signing secret over the\n * moment and the body, as the card reader start-up screen's are, under this\n * route's own name, so a signature for one route is none for another. Off\n * Vercel one process holds every route, and libvips with it, so the read\n * runs where it is asked for, as before.\n *\n * Any failure is the read's own: the photo is unreadable and the copy is\n * written from the product's words, or the logo's colors are left out with\n * the note that says so.\n *=========================================*/\n\n/** The images route's registered path, served under `/api/plugin-images/`. */\nexport const AI_IMAGE_READS_PATH = 'ai/image-reads'\n\n/** The request header that carries the signature: `t=<ms>,v1=<hex>`. */\nexport const AI_IMAGE_READS_SIGNATURE = 'x-aglyn-images-signature'\n\n/** How far a signed request's moment may be from the function's clock. */\nexport const AI_IMAGE_READS_WINDOW_MS = 5 * 60 * 1000\n\n/** The reads the function makes; `logo-raster` turns a site import's SVG logo into a PNG (AGL-4084). */\nexport type AiImageReadsOp = 'product-photo' | 'logo-colors' | 'logo-raster'\n\n/** One request's body: the asset by its media reference, and whose read it is. */\nexport interface AiImageReadsRequest {\n op: AiImageReadsOp\n ref: string\n hostId: string\n orgId: string\n}\n\nlet secretOverride: (() => string) | null = null\n\n/** Test seam. */\nexport function setAiImageReadsSecretForTests(secret: (() => string) | null): void {\n secretOverride = secret\n}\n\nfunction signature(at: number, body: string): string {\n const digest = createHash('sha256').update(body).digest('hex')\n return createHmac('sha256', (secretOverride ?? tokenSigningSecret)())\n .update(`ai-image-reads:${at}.${digest}`)\n .digest('hex')\n}\n\n/** The header value for a body signed now. Throws on a deploy with no signing secret. */\nexport function signAiImageReadsRequest(body: string, nowMs: number = Date.now()): string {\n return `t=${nowMs},v1=${signature(nowMs, body)}`\n}\n\n/**\n * Whether a request's signature is ours, over this body, and recent. False\n * for anything else, a deploy with no signing secret included (fail closed).\n */\nexport function verifyAiImageReadsRequest(\n header: string | null | undefined,\n body: string,\n nowMs: number = Date.now(),\n): boolean {\n const parts = Object.fromEntries(\n String(header ?? '')\n .split(',')\n .map((part) => part.trim().split('=') as [string, string])\n .filter((pair) => pair.length === 2),\n )\n const at = Number(parts['t'])\n const given = String(parts['v1'] ?? '')\n if (!Number.isFinite(at) || !/^[a-f0-9]{64}$/.test(given)) return false\n if (Math.abs(nowMs - at) > AI_IMAGE_READS_WINDOW_MS) return false\n let expected: string\n try {\n expected = signature(at, body)\n } catch {\n return false\n }\n return timingSafeEqual(Buffer.from(given), Buffer.from(expected))\n}\n\n/**\n * Where to ask for an image read, or `null` to make it in this process. On\n * Vercel only the images function ships libvips, so every other function\n * asks it, at the console's own address; anywhere else this process can\n * decode.\n */\nexport function aiImageReadsOrigin(): string | null {\n if (process.env['VERCEL'] !== '1') return null\n return platformConsoleOrigin()\n}\n\n/**\n * The request for a located asset's read: the asset named by its media\n * reference, which the function locates again. `null` for a location no\n * reference can name, which is never read.\n */\nexport function aiImageReadsRequest(\n op: AiImageReadsOp,\n location: AiMediaAssetLocation,\n hostId: string,\n orgId: string,\n): AiImageReadsRequest | null {\n const ref = formatMediaRef(location.scopeSegment, location.mediaId)\n return ref ? { op, ref, hostId, orgId } : null\n}\n\n/**\n * The headers that get our own request past our own edge, when configured:\n * the same pair the render monitor sends (AGL-3571).\n */\nfunction edgeBypassHeaders(): Record<string, string> {\n const probe = String(process.env['AGLYN_PROBE_TOKEN'] ?? '').trim()\n const bypass = String(\n process.env['AGLYN_VERCEL_BYPASS'] || process.env['VERCEL_AUTOMATION_BYPASS_SECRET'] || '',\n ).trim()\n return {\n ...(probe ? { 'x-aglyn-probe': probe } : {}),\n ...(bypass ? { 'x-vercel-protection-bypass': bypass } : {}),\n }\n}\n\n/**\n * Asks the images function for one read, signed, waiting no longer than the\n * step's own bound for it (`signal`). Throws on anything but an answer.\n */\nexport async function requestAiImageReads<T>(\n origin: string,\n payload: AiImageReadsRequest,\n signal: AbortSignal,\n): Promise<T> {\n const body = JSON.stringify(payload)\n const response = await fetch(`${origin}/api/${PLUGIN_IMAGES_API_PREFIX}/${AI_IMAGE_READS_PATH}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n [AI_IMAGE_READS_SIGNATURE]: signAiImageReadsRequest(body),\n ...edgeBypassHeaders(),\n },\n body,\n signal,\n })\n if (!response.ok) throw new Error(`the AI image reads function answered ${response.status}`)\n return (await response.json()) as T\n}\n\n/*------------------------------------------\n * A site import's SVG logo, as a picture (AGL-4084)\n *-----------------------------------------*/\n\n/** The largest SVG turned into a picture. */\nexport const AI_LOGO_RASTER_MAX_BYTES = 512 * 1024\n\n/** The tallest picture an SVG logo is drawn at. */\nexport const AI_LOGO_RASTER_MAX_HEIGHT = 512\n\n/** The request for an SVG logo drawn as a PNG: the drawing itself, never an address, and the height. */\nexport interface AiLogoRasterRequest {\n op: 'logo-raster'\n svg: string\n height: number\n}\n\ninterface SharpSvg {\n (input: Buffer, options?: { limitInputPixels?: number; density?: number }): {\n resize(options: { height: number; withoutEnlargement?: boolean }): { png(): { toBuffer(): Promise<Buffer> } }\n }\n}\n\n/**\n * An SVG logo drawn as a PNG of the height asked, in this process (AGL-4084).\n * `sharp`'s SVG reader loads nothing the drawing links to, so only its own\n * shapes are drawn. `null` for what is not a drawing, or is too large.\n */\nexport async function rasterizeAiLogoSvg(svg: Uint8Array, height: number): Promise<Uint8Array | null> {\n if (!svg.byteLength || svg.byteLength > AI_LOGO_RASTER_MAX_BYTES) return null\n const text = Buffer.from(svg).toString('utf8')\n if (!/<svg[\\s>]/i.test(text)) return null\n const { loadAiMediaSharp } = await import('./ai-media-asset')\n const sharp = (await loadAiMediaSharp()) as SharpSvg\n const size = Math.max(16, Math.min(AI_LOGO_RASTER_MAX_HEIGHT, Math.round(height)))\n const png = await sharp(Buffer.from(svg), { limitInputPixels: 64_000_000, density: 300 }).resize({ height: size }).png().toBuffer()\n return new Uint8Array(png)\n}\n\n/** An SVG logo drawn as a PNG by the images function (AGL-4084); `null` on anything but a picture. */\nexport async function requestAiLogoRaster(origin: string, svg: Uint8Array, height: number): Promise<Uint8Array | null> {\n if (!svg.byteLength || svg.byteLength > AI_LOGO_RASTER_MAX_BYTES) return null\n const body = JSON.stringify({ op: 'logo-raster', svg: Buffer.from(svg).toString('base64'), height } satisfies AiLogoRasterRequest)\n try {\n const response = await fetch(`${origin}/api/${PLUGIN_IMAGES_API_PREFIX}/${AI_IMAGE_READS_PATH}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', [AI_IMAGE_READS_SIGNATURE]: signAiImageReadsRequest(body), ...edgeBypassHeaders() },\n body,\n signal: AbortSignal.timeout(15_000),\n })\n if (!response.ok) return null\n const answer = (await response.json()) as { png?: unknown }\n return typeof answer.png === 'string' && answer.png ? new Uint8Array(Buffer.from(answer.png, 'base64')) : null\n } catch {\n return null\n }\n}\n"],"names":["createHash","createHmac","timingSafeEqual","PLUGIN_IMAGES_API_PREFIX","formatMediaRef","platformConsoleOrigin","tokenSigningSecret","AI_IMAGE_READS_PATH","AI_IMAGE_READS_SIGNATURE","AI_IMAGE_READS_WINDOW_MS","secretOverride","setAiImageReadsSecretForTests","secret","signature","at","body","digest","update","signAiImageReadsRequest","nowMs","Date","now","verifyAiImageReadsRequest","header","parts","Object","fromEntries","String","split","map","part","trim","filter","pair","length","Number","given","isFinite","test","Math","abs","expected","Buffer","from","aiImageReadsOrigin","process","env","aiImageReadsRequest","op","location","hostId","orgId","ref","scopeSegment","mediaId","edgeBypassHeaders","probe","bypass","requestAiImageReads","origin","payload","signal","JSON","stringify","response","fetch","method","headers","ok","Error","status","json","AI_LOGO_RASTER_MAX_BYTES","AI_LOGO_RASTER_MAX_HEIGHT","rasterizeAiLogoSvg","svg","height","byteLength","text","toString","loadAiMediaSharp","sharp","size","max","min","round","png","limitInputPixels","density","resize","toBuffer","Uint8Array","requestAiLogoRaster","AbortSignal","timeout","answer"],"mappings":"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"}
|