@aglyn/plugins-ai 1.0.0-beta.249 → 1.0.0-beta.252
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/components/ai-brief-dialog.component.js +15 -1
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +1 -1
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +20 -2
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +11 -0
- package/src/lib/components/ai-jobs-store.js +22 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +3 -2
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-crm-step.js +7 -4
- package/src/lib/jobs/ai-job-crm-step.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +24 -0
- package/src/lib/jobs/ai-job-drafts.js +46 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js +6 -1
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-reuse.d.ts +78 -0
- package/src/lib/jobs/ai-job-form-reuse.js +136 -0
- package/src/lib/jobs/ai-job-form-reuse.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -0
- package/src/lib/jobs/ai-job-form-step.js +63 -11
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +6 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +12 -1
- package/src/lib/jobs/ai-job-page-language.js +48 -11
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +8 -3
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +19 -0
- package/src/lib/jobs/ai-job-plan-step.js +39 -1
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +5 -3
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +7 -5
- package/src/lib/jobs/ai-job-site-content.js +7 -5
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +17 -0
- package/src/lib/jobs/ai-job-site-datasets.js +24 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +5 -2
- package/src/lib/jobs/ai-job-site-step.js +20 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.js +6 -3
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +22 -8
- package/src/lib/jobs/ai-layout-stock-photos.js +39 -8
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-stock-business-nouns.d.ts +62 -0
- package/src/lib/jobs/ai-stock-business-nouns.js +264 -0
- package/src/lib/jobs/ai-stock-business-nouns.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +15 -0
- package/src/lib/layout-language/ai-layout-gaps.js +57 -7
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +7 -3
- package/src/lib/model/ai-build-plan.js +11 -1
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +32 -18
- package/src/lib/model/ai-credit-estimate.js +36 -18
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +30 -0
- package/src/lib/model/ai-job-activity.js +35 -0
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +5 -3
- package/src/lib/model/ai-media-credits.js +7 -5
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-site-job.js +6 -3
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-store-pages.d.ts +5 -0
- package/src/lib/model/ai-site-store-pages.js +2 -0
- package/src/lib/model/ai-site-store-pages.js.map +1 -1
- package/src/lib/plugin-config.js +2 -2
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/providers/anthropic-crm-wording-superseded.d.ts +29 -0
- package/src/lib/providers/anthropic-crm-wording-superseded.js +31 -0
- package/src/lib/providers/anthropic-crm-wording-superseded.js.map +1 -0
- package/src/lib/providers/catalog.d.ts +24 -17
- package/src/lib/providers/catalog.js +63 -30
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.d.ts +1 -1
- package/src/lib/providers/model-choice.js +39 -30
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.d.ts +20 -1
- package/src/lib/providers/routing.js +47 -8
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-dev-env.d.ts +37 -1
- package/src/lib/runtime/ai-dev-env.js +59 -6
- package/src/lib/runtime/ai-dev-env.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.js +3 -1
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +8 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +8 -0
- package/src/lib/runtime/ai-eval-live.js +34 -8
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +4 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +17 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/seo-fields.js +4 -3
- package/src/lib/runtime/seo-fields.js.map +1 -1
- package/src/lib/server/ai-assist.js +3 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/ai-media-svg.js +4 -0
- package/src/lib/server/ai-media-svg.js.map +1 -1
- package/src/lib/usage/ai-allotment-alerts.js +2 -5
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-ai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.252",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.252",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.252",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.252",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.252",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.252",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.252",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.252",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.252",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.252",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.252",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.252",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.252",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.252",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.252",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.252",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.252",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -24,8 +24,9 @@ import { Alert, Button, Checkbox, Chip, Dialog, DialogActions, DialogContent, Di
|
|
|
24
24
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
25
25
|
import { AI_PAGE_TYPES } from "../model/ai-page-job.js";
|
|
26
26
|
import { AI_TEMPLATE_SUBJECTS, parseAiTemplateJobInputs } from "../model/ai-template-subjects.js";
|
|
27
|
+
import { AI_SITE_BUILD_IN_FLIGHT_COPY, aiSiteBuildInFlight } from "../model/ai-job-activity.js";
|
|
27
28
|
import { AiJobFollow } from "./ai-job-follow.component.js";
|
|
28
|
-
import { publishAiJob } from "./ai-jobs-store.js";
|
|
29
|
+
import { publishAiJob, useAiJobsInFlightKnown } from "./ai-jobs-store.js";
|
|
29
30
|
import { AiTemplateCollectionField } from "./ai-template-collection-field.component.js";
|
|
30
31
|
/**
|
|
31
32
|
* The brief dialog every "Create with AI" opens (AGL-2907, AGL-3043, AGL-3051):
|
|
@@ -225,6 +226,15 @@ export function AiBriefDialog({ kind, open, onClose, orgId, hostId, user, orgSlu
|
|
|
225
226
|
// and a started job as the kind it is (the choice is cleared once it starts).
|
|
226
227
|
const shownKind = started && (started.kind === 'email' || started.kind === 'campaign') ? started.kind : aiBriefJobKind(kind, choice);
|
|
227
228
|
const copy = AI_BRIEF_COPY[shownKind];
|
|
229
|
+
// Another page already being built on this site (AGL-3765), from the list
|
|
230
|
+
// the top-bar indicator already holds, read again by nothing here: two at
|
|
231
|
+
// once each plan against the site as it was, and race for what its plan
|
|
232
|
+
// holds one of. A warning, not a stop.
|
|
233
|
+
const asking = open && kind === 'page' && !started;
|
|
234
|
+
const inFlight = useAiJobsInFlightKnown(user, orgId, asking);
|
|
235
|
+
const building = asking ? aiSiteBuildInFlight(inFlight, {
|
|
236
|
+
hostId
|
|
237
|
+
}) : null;
|
|
228
238
|
useEffect(()=>{
|
|
229
239
|
if (!open) return;
|
|
230
240
|
setNotice(null);
|
|
@@ -404,6 +414,10 @@ export function AiBriefDialog({ kind, open, onClose, orgId, hostId, user, orgSlu
|
|
|
404
414
|
color: "text.secondary",
|
|
405
415
|
children: copy.next
|
|
406
416
|
}),
|
|
417
|
+
building ? /*#__PURE__*/ _jsx(Alert, {
|
|
418
|
+
severity: "info",
|
|
419
|
+
children: AI_SITE_BUILD_IN_FLIGHT_COPY
|
|
420
|
+
}) : null,
|
|
407
421
|
notice ? /*#__PURE__*/ _jsx(Alert, {
|
|
408
422
|
severity: "warning",
|
|
409
423
|
children: notice
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { HelpTip } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiEmailType } from '../jobs/ai-job-email-step'\nimport type { AiJobKind, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { publishAiJob } from './ai-jobs-store'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Create with AI\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms, a\n * reusable component from Components, and an email design from the Emails\n * page's templates (AGL-3596). The `campaign` kind is the job an email's door\n * starts when the campaign is asked for too; the Campaigns section's own door\n * is a dialog of its own (`ai-campaign-create.component`, AGL-3603).\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. An email's reads the optional\n * kind of email, and its door also offers to draft the\n * campaign that would send it, which starts a `campaign` job instead — the\n * same design, plus a draft campaign aimed at nobody. The job plans first; once it\n * exists the dialog follows it live (AGL-3593) — planning, the plan with its\n * Confirm, building, then the draft to open — and \"Open AI jobs\" follows it\n * anywhere else in the console. Nothing here builds or writes anything until\n * the member confirms the plan.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component' | 'email' | 'campaign'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists, above its live status. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The template is built as a draft in your library, and nothing on your ' +\n 'site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The form is built as a draft, and it collects nothing until you place it on ' +\n 'a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The component is built as a draft, and no page shows it until you insert ' +\n 'it.',\n failed: 'The component could not be started. Try again.',\n },\n email: {\n title: 'Describe an email',\n briefLabel: 'What is the email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The email is built ' +\n 'from email blocks, with three subject lines and three preheaders to choose from. ' +\n 'Nothing is sent or scheduled.',\n submit: 'Plan the email',\n started:\n 'The email is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The email is saved as a draft template, and nothing sends it until a ' +\n 'campaign does.',\n failed: 'The email could not be started. Try again.',\n },\n campaign: {\n title: 'Describe a campaign',\n briefLabel: 'What is the campaign’s email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The job writes the ' +\n 'email and a draft campaign that would send it. The campaign is aimed at nobody and ' +\n 'sends nothing until you pick who receives it and schedule it.',\n submit: 'Plan the campaign',\n started:\n 'The campaign is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The email and the campaign are saved as drafts, and nothing is sent.',\n failed: 'The campaign could not be started. Try again.',\n },\n}\n\n/**\n * Each kind's own help beside the dialog's title (AGL-3660): the guide for\n * the thing being described, opened at the section about writing its brief,\n * never one page shared by all seven.\n */\nconst AI_BRIEF_HELP: Readonly<Record<AiBriefKind, ReturnType<typeof pluginDocsHelp>>> = {\n page: pluginDocsHelp('aiPage', {\n anchor: '#describe-the-page',\n excerpt:\n 'Say what the page is for and who it is for. A plan comes first, and the page is built as an unpublished draft once you confirm it.',\n }),\n template: pluginDocsHelp('aiTemplate', {\n anchor: '#generate-a-page-template-with-aglyn-ai',\n excerpt:\n 'Describe what each page made from the template should show. What changes per page is filled in from each record.',\n }),\n layout: pluginDocsHelp('aiLayout', {\n anchor: '#generate-a-layout-with-aglyn-ai',\n excerpt:\n 'Describe the header, navigation and footer. The layout is built as a draft, and no page uses it until you assign it.',\n }),\n form: pluginDocsHelp('aiForm', {\n anchor: '#describe-the-form',\n excerpt:\n 'Say what the form is for. Its fields, marketing consent and where submissions go are planned together, and it collects nothing until you place it.',\n }),\n component: pluginDocsHelp('aiComponent', {\n anchor: '#from-a-brief',\n excerpt:\n 'Describe one reusable block. What each page can change, such as a name or a photo, becomes a property of the component.',\n }),\n email: pluginDocsHelp('aiEmail', {\n anchor: '#write-the-brief',\n excerpt:\n 'Say what the email is for. It is built from email blocks with three subject lines to choose from, and nothing is sent.',\n }),\n campaign: pluginDocsHelp('aiEmail', {\n anchor: '#where-to-start-it',\n excerpt:\n 'Writes the email and a draft campaign that would send it. The campaign is aimed at nobody until you pick its lists and schedule it.',\n }),\n}\n\n/**\n * The kinds of email a member picks from, as their chips name them. The jobs'\n * own catalog (`AI_EMAIL_TYPES`) also holds `reply`, which only a job the\n * platform composes sets, so it is not offered here.\n */\nexport const AI_EMAIL_TYPE_LABELS: Readonly<Record<Exclude<AiEmailType, 'reply'>, string>> = {\n welcome: 'Welcome',\n newsletter: 'Newsletter',\n launch: 'Launch',\n abandonedCart: 'Cart reminder',\n eventReminder: 'Event reminder',\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n /** An email's or a campaign's kind of email; `null` or absent for none. */\n emailType?: Exclude<AiEmailType, 'reply'> | null\n /** An email's door only: also draft the campaign that would send it. */\n withCampaign?: boolean\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n emailType: null,\n withCampaign: false,\n}\n\n/**\n * The job the door starts: its own kind, except an email that also asked for\n * its campaign, which is a `campaign` job — the campaign step writes the same\n * design and drafts the campaign around it.\n */\nexport function aiBriefJobKind(kind: AiBriefKind, choice: AiBriefChoice): AiBriefKind {\n return kind === 'email' && choice.withCampaign ? 'campaign' : kind\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional, and so is an email's or a campaign's kind of\n * email (`inputs.emailType`, which both steps read). A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'email' || kind === 'campaign') {\n return choice.emailType ? { emailType: choice.emailType } : {}\n }\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n /** Path slug for the link to the draft; read from the address when omitted. */\n orgSlug?: string | null\n /** The viewer is staff (AGL-3078), for a refused plan's details. */\n isStaff?: boolean\n}\n\nexport function AiBriefDialog({\n kind,\n open,\n onClose,\n orgId,\n hostId,\n user,\n orgSlug,\n isStaff,\n}: AiBriefDialogProps) {\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n /** The job the brief started, as the create door answered with it. */\n const [started, setStarted] = useState<AiJobSummary | null>(null)\n // An email that asked for its campaign is said as the campaign it starts,\n // and a started job as the kind it is (the choice is cleared once it starts).\n const shownKind =\n started && (started.kind === 'email' || started.kind === 'campaign')\n ? started.kind\n : aiBriefJobKind(kind, choice)\n const copy = AI_BRIEF_COPY[shownKind]\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(null)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n const jobKind = aiBriefJobKind(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind: jobKind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n const created = (payload?.job as AiJobSummary | undefined) ?? null\n if (!created) {\n setNotice(copy.failed)\n return\n }\n // The indicator and the launcher count it from the moment it exists.\n publishAiJob(created)\n setStarted(created)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {copy.title}\n <HelpTip {...AI_BRIEF_HELP[shownKind]} />\n </DialogTitle>\n <DialogContent>\n {started && orgId ? (\n <AiJobFollow\n job={started}\n orgId={orgId}\n orgSlug={orgSlug}\n user={user}\n intro={copy.started}\n staff={isStaff}\n // The dialog covers the panel AI jobs opens in.\n onOpenJobs={onClose}\n />\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'email' || kind === 'campaign' ? (\n <ChipGroup\n id=\"ai-email-type-label\"\n label=\"Kind of email (optional)\"\n options={Object.entries(AI_EMAIL_TYPE_LABELS).map(([id, label]) => ({\n id,\n label,\n }))}\n value={choice.emailType ?? null}\n // Optional: pressing the pressed kind again sends none.\n onPress={(id) =>\n pick({\n emailType:\n choice.emailType === id ? null : (id as AiBriefChoice['emailType']),\n })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'email' ? (\n <FormControlLabel\n control={\n <Checkbox\n checked={Boolean(choice.withCampaign)}\n onChange={(event) => pick({ withCampaign: event.target.checked })}\n disabled={busy}\n />\n }\n label=\"Also draft a campaign that sends it\"\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","pluginDocsHelp","HelpTip","authorizedFetch","Alert","Button","Checkbox","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiJobFollow","publishAiJob","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","email","campaign","AI_BRIEF_HELP","anchor","excerpt","AI_EMAIL_TYPE_LABELS","welcome","newsletter","launch","abandonedCart","eventReminder","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","emailType","withCampaign","aiBriefJobKind","kind","choice","aiBriefJobInputs","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","orgSlug","isStaff","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","shownKind","copy","pick","patch","ready","start","jobKind","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","created","job","undefined","fullWidth","maxWidth","sx","display","alignItems","gap","intro","staff","onOpenJobs","spacing","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","Object","entries","control","checked","Boolean","variant","color","severity","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","option","aria-pressed"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { HelpTip } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiEmailType } from '../jobs/ai-job-email-step'\nimport type { AiJobKind, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AI_SITE_BUILD_IN_FLIGHT_COPY, aiSiteBuildInFlight } from '../model/ai-job-activity'\nimport { AiJobFollow } from './ai-job-follow.component'\nimport { publishAiJob, useAiJobsInFlightKnown } from './ai-jobs-store'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Create with AI\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms, a\n * reusable component from Components, and an email design from the Emails\n * page's templates (AGL-3596). The `campaign` kind is the job an email's door\n * starts when the campaign is asked for too; the Campaigns section's own door\n * is a dialog of its own (`ai-campaign-create.component`, AGL-3603).\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. An email's reads the optional\n * kind of email, and its door also offers to draft the\n * campaign that would send it, which starts a `campaign` job instead — the\n * same design, plus a draft campaign aimed at nobody. The job plans first; once it\n * exists the dialog follows it live (AGL-3593) — planning, the plan with its\n * Confirm, building, then the draft to open — and \"Open AI jobs\" follows it\n * anywhere else in the console. Nothing here builds or writes anything until\n * the member confirms the plan.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component' | 'email' | 'campaign'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists, above its live status. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The template is built as a draft in your library, and nothing on your ' +\n 'site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The form is built as a draft, and it collects nothing until you place it on ' +\n 'a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The component is built as a draft, and no page shows it until you insert ' +\n 'it.',\n failed: 'The component could not be started. Try again.',\n },\n email: {\n title: 'Describe an email',\n briefLabel: 'What is the email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The email is built ' +\n 'from email blocks, with three subject lines and three preheaders to choose from. ' +\n 'Nothing is sent or scheduled.',\n submit: 'Plan the email',\n started:\n 'The email is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The email is saved as a draft template, and nothing sends it until a ' +\n 'campaign does.',\n failed: 'The email could not be started. Try again.',\n },\n campaign: {\n title: 'Describe a campaign',\n briefLabel: 'What is the campaign’s email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The job writes the ' +\n 'email and a draft campaign that would send it. The campaign is aimed at nobody and ' +\n 'sends nothing until you pick who receives it and schedule it.',\n submit: 'Plan the campaign',\n started:\n 'The campaign is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The email and the campaign are saved as drafts, and nothing is sent.',\n failed: 'The campaign could not be started. Try again.',\n },\n}\n\n/**\n * Each kind's own help beside the dialog's title (AGL-3660): the guide for\n * the thing being described, opened at the section about writing its brief,\n * never one page shared by all seven.\n */\nconst AI_BRIEF_HELP: Readonly<Record<AiBriefKind, ReturnType<typeof pluginDocsHelp>>> = {\n page: pluginDocsHelp('aiPage', {\n anchor: '#describe-the-page',\n excerpt:\n 'Say what the page is for and who it is for. A plan comes first, and the page is built as an unpublished draft once you confirm it.',\n }),\n template: pluginDocsHelp('aiTemplate', {\n anchor: '#generate-a-page-template-with-aglyn-ai',\n excerpt:\n 'Describe what each page made from the template should show. What changes per page is filled in from each record.',\n }),\n layout: pluginDocsHelp('aiLayout', {\n anchor: '#generate-a-layout-with-aglyn-ai',\n excerpt:\n 'Describe the header, navigation and footer. The layout is built as a draft, and no page uses it until you assign it.',\n }),\n form: pluginDocsHelp('aiForm', {\n anchor: '#describe-the-form',\n excerpt:\n 'Say what the form is for. Its fields, marketing consent and where submissions go are planned together, and it collects nothing until you place it.',\n }),\n component: pluginDocsHelp('aiComponent', {\n anchor: '#from-a-brief',\n excerpt:\n 'Describe one reusable block. What each page can change, such as a name or a photo, becomes a property of the component.',\n }),\n email: pluginDocsHelp('aiEmail', {\n anchor: '#write-the-brief',\n excerpt:\n 'Say what the email is for. It is built from email blocks with three subject lines to choose from, and nothing is sent.',\n }),\n campaign: pluginDocsHelp('aiEmail', {\n anchor: '#where-to-start-it',\n excerpt:\n 'Writes the email and a draft campaign that would send it. The campaign is aimed at nobody until you pick its lists and schedule it.',\n }),\n}\n\n/**\n * The kinds of email a member picks from, as their chips name them. The jobs'\n * own catalog (`AI_EMAIL_TYPES`) also holds `reply`, which only a job the\n * platform composes sets, so it is not offered here.\n */\nexport const AI_EMAIL_TYPE_LABELS: Readonly<Record<Exclude<AiEmailType, 'reply'>, string>> = {\n welcome: 'Welcome',\n newsletter: 'Newsletter',\n launch: 'Launch',\n abandonedCart: 'Cart reminder',\n eventReminder: 'Event reminder',\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n /** An email's or a campaign's kind of email; `null` or absent for none. */\n emailType?: Exclude<AiEmailType, 'reply'> | null\n /** An email's door only: also draft the campaign that would send it. */\n withCampaign?: boolean\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n emailType: null,\n withCampaign: false,\n}\n\n/**\n * The job the door starts: its own kind, except an email that also asked for\n * its campaign, which is a `campaign` job — the campaign step writes the same\n * design and drafts the campaign around it.\n */\nexport function aiBriefJobKind(kind: AiBriefKind, choice: AiBriefChoice): AiBriefKind {\n return kind === 'email' && choice.withCampaign ? 'campaign' : kind\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional, and so is an email's or a campaign's kind of\n * email (`inputs.emailType`, which both steps read). A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'email' || kind === 'campaign') {\n return choice.emailType ? { emailType: choice.emailType } : {}\n }\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n /** Path slug for the link to the draft; read from the address when omitted. */\n orgSlug?: string | null\n /** The viewer is staff (AGL-3078), for a refused plan's details. */\n isStaff?: boolean\n}\n\nexport function AiBriefDialog({\n kind,\n open,\n onClose,\n orgId,\n hostId,\n user,\n orgSlug,\n isStaff,\n}: AiBriefDialogProps) {\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n /** The job the brief started, as the create door answered with it. */\n const [started, setStarted] = useState<AiJobSummary | null>(null)\n // An email that asked for its campaign is said as the campaign it starts,\n // and a started job as the kind it is (the choice is cleared once it starts).\n const shownKind =\n started && (started.kind === 'email' || started.kind === 'campaign')\n ? started.kind\n : aiBriefJobKind(kind, choice)\n const copy = AI_BRIEF_COPY[shownKind]\n // Another page already being built on this site (AGL-3765), from the list\n // the top-bar indicator already holds, read again by nothing here: two at\n // once each plan against the site as it was, and race for what its plan\n // holds one of. A warning, not a stop.\n const asking = open && kind === 'page' && !started\n const inFlight = useAiJobsInFlightKnown(user, orgId, asking)\n const building = asking ? aiSiteBuildInFlight(inFlight, { hostId }) : null\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(null)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n const jobKind = aiBriefJobKind(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind: jobKind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n const created = (payload?.job as AiJobSummary | undefined) ?? null\n if (!created) {\n setNotice(copy.failed)\n return\n }\n // The indicator and the launcher count it from the moment it exists.\n publishAiJob(created)\n setStarted(created)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n {copy.title}\n <HelpTip {...AI_BRIEF_HELP[shownKind]} />\n </DialogTitle>\n <DialogContent>\n {started && orgId ? (\n <AiJobFollow\n job={started}\n orgId={orgId}\n orgSlug={orgSlug}\n user={user}\n intro={copy.started}\n staff={isStaff}\n // The dialog covers the panel AI jobs opens in.\n onOpenJobs={onClose}\n />\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'email' || kind === 'campaign' ? (\n <ChipGroup\n id=\"ai-email-type-label\"\n label=\"Kind of email (optional)\"\n options={Object.entries(AI_EMAIL_TYPE_LABELS).map(([id, label]) => ({\n id,\n label,\n }))}\n value={choice.emailType ?? null}\n // Optional: pressing the pressed kind again sends none.\n onPress={(id) =>\n pick({\n emailType:\n choice.emailType === id ? null : (id as AiBriefChoice['emailType']),\n })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'email' ? (\n <FormControlLabel\n control={\n <Checkbox\n checked={Boolean(choice.withCampaign)}\n onChange={(event) => pick({ withCampaign: event.target.checked })}\n disabled={busy}\n />\n }\n label=\"Also draft a campaign that sends it\"\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {building ? <Alert severity=\"info\">{AI_SITE_BUILD_IN_FLIGHT_COPY}</Alert> : null}\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","pluginDocsHelp","HelpTip","authorizedFetch","Alert","Button","Checkbox","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AI_SITE_BUILD_IN_FLIGHT_COPY","aiSiteBuildInFlight","AiJobFollow","publishAiJob","useAiJobsInFlightKnown","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","email","campaign","AI_BRIEF_HELP","anchor","excerpt","AI_EMAIL_TYPE_LABELS","welcome","newsletter","launch","abandonedCart","eventReminder","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","emailType","withCampaign","aiBriefJobKind","kind","choice","aiBriefJobInputs","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","orgSlug","isStaff","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","shownKind","copy","asking","inFlight","building","pick","patch","ready","start","jobKind","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","created","job","undefined","fullWidth","maxWidth","sx","display","alignItems","gap","intro","staff","onOpenJobs","spacing","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","Object","entries","control","checked","Boolean","variant","color","severity","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","option","aria-pressed"],"mappings":"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"}
|
|
@@ -226,7 +226,7 @@ export { AI_SITE_BUILD_HREF };
|
|
|
226
226
|
const steps = AI_STORE_FINISH_STEPS.filter((step)=>step.id !== 'policies' || policies).map((step)=>_extends({}, step, build && step.id === 'products' ? {
|
|
227
227
|
text: AI_BUILD_STORE_PRODUCTS_TEXT
|
|
228
228
|
} : {}, {
|
|
229
|
-
href: step.opens === 'pages' ? buildRoute(Route.HOST_SCREENS, context) : step.opens === 'layouts' || step.opens === 'layout' ? buildRoute(Route.HOST_LAYOUTS, context) : pluginRecordListHref(step.opens, context)
|
|
229
|
+
href: step.opens === 'pages' ? buildRoute(Route.HOST_SCREENS, context) : step.opens === 'layouts' || step.opens === 'layout' ? buildRoute(Route.HOST_LAYOUTS, context) : step.record ? pluginRecordHref(step.opens, context, step.record) : pluginRecordListHref(step.opens, context)
|
|
230
230
|
}));
|
|
231
231
|
if (!build) return steps;
|
|
232
232
|
// The site's own header and footer (AGL-3676): links it had no list for are
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.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 */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_STORE_FINISH_STEPS, type AiStoreFinishStep } from '../model/ai-site-store-pages'\nimport { AI_BUILD_STORE_LINKS_DRAFT_STEP, AI_BUILD_STORE_LINKS_MISSING_STEP } from '../model/ai-build-store-pages'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'entry') {\n // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.\n const collectionSlug = output.proposal?.['collectionSlug']\n return typeof collectionSlug === 'string' && collectionSlug\n ? buildRoute(Route.HOST_CONTENT_COLLECTION, { orgSlug, host, collectionSlug })\n : buildRoute(Route.HOST_CONTENT, { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'media') {\n // Files a build organized (AGL-3752) open in the site's media library.\n return buildRoute(Route.HOST_MEDIA, { orgSlug, host })\n }\n if (output.resource === 'settings') {\n // Settings a build saved (AGL-3748) open where most of them are edited.\n return buildRoute(Route.HOST_SETUP_SEO, { orgSlug, host })\n }\n if (output.resource === 'tracking') {\n // Tags a build saved (AGL-3751) open on the card that edits them.\n return buildRoute(Route.HOST_SETUP_TRACKING, { orgSlug, host })\n }\n if (output.resource === 'draft' && output.draftResource) {\n // A draft another plugin wrote — a dataset a site start or a build made\n // (AGL-3616) — opens where its owner lists it, or nowhere it is not loaded.\n return pluginRecordListHref(output.draftResource, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n\n/**\n * Where a person gets more AI credits (AGL-3660): the workspace's Billing\n * page at its plans, where an upgrade and the AI add-on are both sold — the\n * door the AI credits meter's own upgrade link opens.\n */\nexport function aiCreditsBillingHref(orgSlug: string): string {\n return `${buildRoute(Route.MANAGE_BILLING, { orgSlug })}#plans`\n}\n\n/** One thing left to finish an AI-built store, with where it is done. */\nexport interface AiStoreFinishLink extends AiStoreFinishStep {\n /** The console page it is done on; `null` where the owner of that page is not loaded. */\n href: string | null\n}\n\n/**\n * What is left to finish a store a site start built (AGL-3676), or `null`\n * where the start built no store's own pages: its account, cart and policy\n * pages are written, so payments, products, shipping and tax, and the\n * policies' brackets are all that remain. Each links the exact console page:\n * the store's settings and its catalog, as the commerce plugin publishes\n * them, and the site's Pages.\n */\nexport function aiStoreFinishLinks(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'items' | 'outputs'> & Partial<Pick<AiJobSummary, 'sitePublish'>>,\n orgSlug: string,\n): AiStoreFinishLink[] | null {\n // A site start's store, or an Assist build that made the site a store (AGL-3676).\n if ((job.kind !== 'site' && job.kind !== 'build') || job.status !== 'done' || !orgSlug) return null\n const row = (job.items ?? []).find((one) => one.slot === 'store')\n if (!row || (row.status !== 'succeeded' && row.status !== 'degraded')) return null\n const host = job.outputs.find((output) => output.resource === 'screen' && output.hostSubdomain)?.hostSubdomain\n if (!host) return null\n const context = { orgSlug, host: String(host) }\n // A build's products are drafts with no price unless asked, and it may\n // have written none of the policies (the site had them): its list says so.\n const build = job.kind === 'build'\n const mine = job.outputs.filter((output) => row.outputs.includes(output.id))\n const written = new Set(mine.map((output) => output.proposal?.['storePage']))\n const policies = !build || ['shipping', 'privacy', 'terms'].some((key) => written.has(key))\n const steps: AiStoreFinishLink[] = AI_STORE_FINISH_STEPS.filter((step) => step.id !== 'policies' || policies).map((step) => ({\n ...step,\n ...(build && step.id === 'products' ? { text: AI_BUILD_STORE_PRODUCTS_TEXT } : {}),\n href:\n step.opens === 'pages'\n ? buildRoute(Route.HOST_SCREENS, context)\n : step.opens === 'layouts' || step.opens === 'layout'\n ? buildRoute(Route.HOST_LAYOUTS, context)\n : pluginRecordListHref(step.opens, context),\n }))\n if (!build) return steps\n // The site's own header and footer (AGL-3676): links it had no list for are\n // the owner's to add; links drafted into it wait for its publish.\n if (mine.some((output) => output.proposal?.['storeLinks'] === 'missing')) {\n steps.push({ ...AI_BUILD_STORE_LINKS_MISSING_STEP, href: buildRoute(Route.HOST_LAYOUTS, context) })\n }\n const draft = mine.find((output) => output.resource === 'layout' && output.proposal?.['storeLinks'] === 'draft')\n const live = draft && job.sitePublish?.published.length\n if (draft && !live) steps.push({ ...AI_BUILD_STORE_LINKS_DRAFT_STEP, href: aiJobOutputHref(draft, orgSlug) })\n return steps\n}\n\n/** What a build's \"Review your products\" step says: its products are drafts, priced only where the request said. */\nexport const AI_BUILD_STORE_PRODUCTS_TEXT =\n 'Your new products are drafts. Set their prices, photos and stock, then make them active so they show in your store.'\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","AI_STORE_FINISH_STEPS","AI_BUILD_STORE_LINKS_DRAFT_STEP","AI_BUILD_STORE_LINKS_MISSING_STEP","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","collectionSlug","proposal","HOST_CONTENT_COLLECTION","HOST_CONTENT","HOST_SETUP_THEME","HOST_MEDIA","HOST_SETUP_SEO","HOST_SETUP_TRACKING","draftResource","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages","aiCreditsBillingHref","MANAGE_BILLING","aiStoreFinishLinks","kind","row","items","find","one","slot","context","build","mine","includes","written","Set","map","policies","some","key","has","steps","step","text","AI_BUILD_STORE_PRODUCTS_TEXT","opens","HOST_LAYOUTS","push","draft","live","sitePublish","published"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.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 */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\nimport { AI_STORE_FINISH_STEPS, type AiStoreFinishStep } from '../model/ai-site-store-pages'\nimport { AI_BUILD_STORE_LINKS_DRAFT_STEP, AI_BUILD_STORE_LINKS_MISSING_STEP } from '../model/ai-build-store-pages'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'entry') {\n // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.\n const collectionSlug = output.proposal?.['collectionSlug']\n return typeof collectionSlug === 'string' && collectionSlug\n ? buildRoute(Route.HOST_CONTENT_COLLECTION, { orgSlug, host, collectionSlug })\n : buildRoute(Route.HOST_CONTENT, { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'media') {\n // Files a build organized (AGL-3752) open in the site's media library.\n return buildRoute(Route.HOST_MEDIA, { orgSlug, host })\n }\n if (output.resource === 'settings') {\n // Settings a build saved (AGL-3748) open where most of them are edited.\n return buildRoute(Route.HOST_SETUP_SEO, { orgSlug, host })\n }\n if (output.resource === 'tracking') {\n // Tags a build saved (AGL-3751) open on the card that edits them.\n return buildRoute(Route.HOST_SETUP_TRACKING, { orgSlug, host })\n }\n if (output.resource === 'draft' && output.draftResource) {\n // A draft another plugin wrote — a dataset a site start or a build made\n // (AGL-3616) — opens where its owner lists it, or nowhere it is not loaded.\n return pluginRecordListHref(output.draftResource, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n\n/**\n * Where a person gets more AI credits (AGL-3660): the workspace's Billing\n * page at its plans, where an upgrade and the AI add-on are both sold — the\n * door the AI credits meter's own upgrade link opens.\n */\nexport function aiCreditsBillingHref(orgSlug: string): string {\n return `${buildRoute(Route.MANAGE_BILLING, { orgSlug })}#plans`\n}\n\n/** One thing left to finish an AI-built store, with where it is done. */\nexport interface AiStoreFinishLink extends AiStoreFinishStep {\n /** The console page it is done on; `null` where the owner of that page is not loaded. */\n href: string | null\n}\n\n/**\n * What is left to finish a store a site start built (AGL-3676), or `null`\n * where the start built no store's own pages: its account, cart and policy\n * pages are written, so payments, products, shipping and tax, and the\n * policies' brackets are all that remain. Each links the exact console page:\n * the store's settings and its catalog, as the commerce plugin publishes\n * them, and the site's Pages.\n */\nexport function aiStoreFinishLinks(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'items' | 'outputs'> & Partial<Pick<AiJobSummary, 'sitePublish'>>,\n orgSlug: string,\n): AiStoreFinishLink[] | null {\n // A site start's store, or an Assist build that made the site a store (AGL-3676).\n if ((job.kind !== 'site' && job.kind !== 'build') || job.status !== 'done' || !orgSlug) return null\n const row = (job.items ?? []).find((one) => one.slot === 'store')\n if (!row || (row.status !== 'succeeded' && row.status !== 'degraded')) return null\n const host = job.outputs.find((output) => output.resource === 'screen' && output.hostSubdomain)?.hostSubdomain\n if (!host) return null\n const context = { orgSlug, host: String(host) }\n // A build's products are drafts with no price unless asked, and it may\n // have written none of the policies (the site had them): its list says so.\n const build = job.kind === 'build'\n const mine = job.outputs.filter((output) => row.outputs.includes(output.id))\n const written = new Set(mine.map((output) => output.proposal?.['storePage']))\n const policies = !build || ['shipping', 'privacy', 'terms'].some((key) => written.has(key))\n const steps: AiStoreFinishLink[] = AI_STORE_FINISH_STEPS.filter((step) => step.id !== 'policies' || policies).map((step) => ({\n ...step,\n ...(build && step.id === 'products' ? { text: AI_BUILD_STORE_PRODUCTS_TEXT } : {}),\n href:\n step.opens === 'pages'\n ? buildRoute(Route.HOST_SCREENS, context)\n : step.opens === 'layouts' || step.opens === 'layout'\n ? buildRoute(Route.HOST_LAYOUTS, context)\n : step.record\n ? pluginRecordHref(step.opens, context, step.record)\n : pluginRecordListHref(step.opens, context),\n }))\n if (!build) return steps\n // The site's own header and footer (AGL-3676): links it had no list for are\n // the owner's to add; links drafted into it wait for its publish.\n if (mine.some((output) => output.proposal?.['storeLinks'] === 'missing')) {\n steps.push({ ...AI_BUILD_STORE_LINKS_MISSING_STEP, href: buildRoute(Route.HOST_LAYOUTS, context) })\n }\n const draft = mine.find((output) => output.resource === 'layout' && output.proposal?.['storeLinks'] === 'draft')\n const live = draft && job.sitePublish?.published.length\n if (draft && !live) steps.push({ ...AI_BUILD_STORE_LINKS_DRAFT_STEP, href: aiJobOutputHref(draft, orgSlug) })\n return steps\n}\n\n/** What a build's \"Review your products\" step says: its products are drafts, priced only where the request said. */\nexport const AI_BUILD_STORE_PRODUCTS_TEXT =\n 'Your new products are drafts. Set their prices, photos and stock, then make them active so they show in your store.'\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","AI_STORE_FINISH_STEPS","AI_BUILD_STORE_LINKS_DRAFT_STEP","AI_BUILD_STORE_LINKS_MISSING_STEP","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","collectionSlug","proposal","HOST_CONTENT_COLLECTION","HOST_CONTENT","HOST_SETUP_THEME","HOST_MEDIA","HOST_SETUP_SEO","HOST_SETUP_TRACKING","draftResource","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages","aiCreditsBillingHref","MANAGE_BILLING","aiStoreFinishLinks","kind","row","items","find","one","slot","context","build","mine","includes","written","Set","map","policies","some","key","has","steps","step","text","AI_BUILD_STORE_PRODUCTS_TEXT","opens","HOST_LAYOUTS","record","push","draft","live","sitePublish","published"],"mappings":"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"}
|
|
@@ -22,10 +22,11 @@ import { aiFreeCreditsResetLabel } from "../model/ai-site-job.js";
|
|
|
22
22
|
import { AiCreditsPromptNotice } from "./ai-credits-prompt.component.js";
|
|
23
23
|
import { aiBuildOpNoun } from "../model/ai-build-progress.js";
|
|
24
24
|
import { aiBuildStorePagesAdded } from "../model/ai-build-store-pages.js";
|
|
25
|
-
import { aiJobConfirmingOwnPlan } from "../model/ai-job-activity.js";
|
|
25
|
+
import { AI_PAGE_BUILDING_JOB_KINDS, AI_SITE_BUILD_IN_FLIGHT_COPY, aiJobConfirmingOwnPlan, aiSiteBuildInFlight } from "../model/ai-job-activity.js";
|
|
26
26
|
import { aiSiteStarterFallbackOffered } from "../model/ai-job-failure-copy.js";
|
|
27
|
+
import { useAiJobsInFlightKnown } from "./ai-jobs-store.js";
|
|
27
28
|
import { AiSiteStarterFallback } from "./ai-site-starter-fallback.component.js";
|
|
28
|
-
import { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from "@mui/material";
|
|
29
|
+
import { Alert, Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from "@mui/material";
|
|
29
30
|
import { useState } from "react";
|
|
30
31
|
/**
|
|
31
32
|
* Where a refused answer broke its rules, and the outline of those parts, as
|
|
@@ -74,10 +75,20 @@ import { useState } from "react";
|
|
|
74
75
|
}
|
|
75
76
|
export function AiJobPlan({ job, onResume, busy = false, staff = false, user, orgSlug }) {
|
|
76
77
|
var _ref, _plan_items, _plan_embeds;
|
|
78
|
+
var _job_review;
|
|
77
79
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
78
80
|
// Unticked until the person ticks it: a build publishes only when its
|
|
79
81
|
// request asked AND they confirm it here (AGL-3616).
|
|
80
82
|
const [publish, setPublish] = useState(false);
|
|
83
|
+
// Confirming builds pages while another job is building on this site
|
|
84
|
+
// (AGL-3765), from the list the indicator and the panel already hold: said
|
|
85
|
+
// beside Confirm, which still confirms.
|
|
86
|
+
const confirmsPages = job.status === 'needs_review' && ((_job_review = job.review) == null ? void 0 : _job_review.reason) === 'plan' && !aiJobConfirmingOwnPlan(job) && AI_PAGE_BUILDING_JOB_KINDS.includes(job.kind);
|
|
87
|
+
const inFlight = useAiJobsInFlightKnown(user, job.orgId, confirmsPages);
|
|
88
|
+
const building = confirmsPages ? aiSiteBuildInFlight(inFlight, {
|
|
89
|
+
hostId: job.hostId,
|
|
90
|
+
exceptJobId: job.id
|
|
91
|
+
}) : null;
|
|
81
92
|
const { plan, review } = job;
|
|
82
93
|
if (!plan && !review) return null;
|
|
83
94
|
const details = staff ? aiJobReviewDetails(review) : [];
|
|
@@ -284,6 +295,13 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user, or
|
|
|
284
295
|
reduce: 'first-page'
|
|
285
296
|
}))
|
|
286
297
|
}),
|
|
298
|
+
building && /*#__PURE__*/ _jsx(Alert, {
|
|
299
|
+
severity: "info",
|
|
300
|
+
sx: {
|
|
301
|
+
mt: 1
|
|
302
|
+
},
|
|
303
|
+
children: AI_SITE_BUILD_IN_FLIGHT_COPY
|
|
304
|
+
}),
|
|
287
305
|
waiting && !prompt && /*#__PURE__*/ _jsx(Button, {
|
|
288
306
|
size: "small",
|
|
289
307
|
variant: "contained",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.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\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiBuildFreePageCapNote, aiBuildSmaller, aiJobCreditRange } from '../model/ai-build-job'\nimport { aiCreditRangeText, aiCreditsPromptFor } from '../model/ai-credit-estimate'\nimport { aiFreeCreditsResetLabel } from '../model/ai-site-job'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport type { AiJobResumeOptions } from './ai-job-requests'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiBuildStorePagesAdded } from '../model/ai-build-store-pages'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n /** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */\n orgSlug?: string | null\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n orgSlug,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one: what it is likely to cost — what builds like it measured\n // — and its ceiling, every pass at its reserve (AGL-3722). A page job\n // counts the creations it builds before its page (AGL-3031).\n const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null\n // A Free build past what is left asks first (AGL-3722): build what fits,\n // the home page first, or upgrade — never a refusal with no way on.\n const freeCredits = review?.freeCredits ?? null\n const prompt =\n plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null\n // A Free build at its page cap says so (AGL-3722): pages past it were left for a later request.\n const pageCapNote =\n plan && waiting && review.reason === 'plan' && job.kind === 'build'\n ? aiBuildFreePageCapNote(plan, Boolean(freeCredits))\n : null\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n const publishing = offersPublish && publish ? { publish: true } : {}\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n {/* A store's own pages (AGL-3676): added by the platform, written by code, no credits. */}\n {aiBuildStorePagesAdded(plan).map((page) => (\n <Typography key={`store-${page.key}`} variant=\"body2\" role=\"listitem\" data-platform-added=\"true\">\n {`Adds the page ${page.title} at ${page.href} — added for your store automatically, uses no AI credits`}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {range && range.ceiling > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`}\n {freeCredits && !prompt\n ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.`\n : ''}\n </Typography>\n )}\n {pageCapNote && (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mt: 0.5 }}>\n {pageCapNote}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"build\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => onResume(job, { ...publishing, creditsConfirmed: true })}\n onSmaller={() => onResume(job, { ...publishing, creditsConfirmed: true, reduce: 'first-page' })}\n />\n )}\n {waiting && !prompt && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? publishing : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiBuildFreePageCapNote","aiBuildSmaller","aiJobCreditRange","aiCreditRangeText","aiCreditsPromptFor","aiFreeCreditsResetLabel","AiCreditsPromptNotice","aiBuildOpNoun","aiBuildStorePagesAdded","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","orgSlug","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","range","kind","freeCredits","prompt","pageCapNote","Boolean","offersPublish","publishAsked","publishing","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","page","data-platform-added","href","key","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","ceiling","display","left","toLocaleString","resetsOn","retryRefusal","control","checked","onChange","event","target","label","onBuildWhatFits","creditsConfirmed","onSmaller","reduce","disabled","undefined"],"mappings":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,4BAA4B,EAC5BC,8BAA8B,EAC9BC,cAAc,QAET,4BAAwB;AAE/B,SAASC,sBAAsB,EAAEC,cAAc,EAAEC,gBAAgB,QAAQ,2BAAuB;AAChG,SAASC,iBAAiB,EAAEC,kBAAkB,QAAQ,iCAA6B;AACnF,SAASC,uBAAuB,QAAQ,0BAAsB;AAC9D,SAASC,qBAAqB,QAAQ,mCAA+B;AAErE,SAASC,aAAa,QAAQ,gCAA4B;AAC1D,SAASC,sBAAsB,QAAQ,mCAA+B;AACtE,SAASC,sBAAsB,QAAQ,8BAA0B;AACjE,SAASC,4BAA4B,QAAQ,kCAA8B;AAC3E,SAASC,qBAAqB,QAAQ,0CAAsC;AAC5E,SAASC,GAAG,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,gBAAgB,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AACpG,SAASC,QAAQ,QAAQ,QAAO;AAkChC;;;;;CAKC,GACD,OAAO,SAASC,mBAAmBC,MAA0B;QAUxCA;IATnB,IAAIA,CAAAA,0BAAAA,OAAQC,MAAM,MAAK,YAAY,OAAO,EAAE;IAC5C,MAAMC,QAAQF,OAAOG,QAAQ,CAACC,OAAO,CAAC,CAACC;YACvBA,kBAEVA;QAFJ,MAAMC,QAAQD,EAAAA,mBAAAA,QAAQE,OAAO,qBAAfF,iBAAiBG,MAAM,IACjC,CAAC,MAAM,EAAEH,QAAQE,OAAO,CAACE,IAAI,CAAC,OAAO,GACrCJ,EAAAA,iBAAAA,QAAQK,KAAK,qBAAbL,eAAeG,MAAM,IACnB,CAAC,GAAG,EAAEH,QAAQK,KAAK,CAACD,IAAI,CAAC,OAAO,GAChC;QACN,OAAOH,QAAQ;YAAC,GAAGD,QAAQM,IAAI,KAAK,OAAO,KAAK,CAAC,KAAK,EAAEN,QAAQM,IAAI,CAAC,CAAC,CAAC,GAAGN,QAAQO,IAAI,CAAC,EAAE,EAAEN,OAAO;SAAC,GAAG,EAAE;IAC1G;IACA,KAAK,MAAMO,SAAQb,kBAAAA,OAAOc,OAAO,YAAdd,kBAAkB,EAAE,CAAE;YAIjBa;YAEdA;QALRX,MAAMa,IAAI,CACR;YACE,GAAG,KAAKC,MAAM,CAACH,KAAKI,KAAK,IAAIJ,KAAKK,EAAE,CAAC,CAAC,EAAEL,KAAKM,WAAW,EAAE;eACvDC,OAAOC,OAAO,EAACR,aAAAA,KAAKS,IAAI,YAATT,aAAa,CAAC,GAAGU,GAAG,CAAC,CAAC,CAACC,MAAMC,MAAM,GAAK,GAAGD,KAAK,CAAC,EAAEE,KAAKC,SAAS,CAACF,QAAQ;eACxFZ,KAAKe,KAAK,CAACpB,MAAM,GAAG;gBAAC,CAAC,MAAM,EAAEK,KAAKe,KAAK,CAACnB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;eAC3DI,EAAAA,WAAAA,KAAKgB,EAAE,qBAAPhB,SAASL,MAAM,IAAG;gBAAC,CAAC,GAAG,EAAEK,KAAKgB,EAAE,CAACpB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;eACnDI,KAAKiB,QAAQ,CAACtB,MAAM,GAAG;gBAAC,CAAC,MAAM,EAAEK,KAAKiB,QAAQ,CAACrB,IAAI,CAAC,OAAO;aAAC,GAAG,EAAE;SACtE,CAACA,IAAI,CAAC;IAEX;IACA,OAAOP;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAAS6B,gBACdC,KAAuB,EACvBC,KAA8B;IAE9B,MAAMC,OAAOzD,8BAA8B,CAACuD,MAAME,IAAI,CAAC;IACvD,MAAM5B,QAAQ5B,eAAesD,MAAM1B,KAAK,IAAI,CAAC,cAAc,EAAE2B,MAAMD,MAAM1B,KAAK,GAAG,GAAG0B,MAAM1B,KAAK;IAC/F,MAAM6B,OAAOH,MAAMI,GAAG,GAAG,CAAC,SAAS,EAAEJ,MAAMI,GAAG,EAAE,GAAG;IACnD,OAAO,CAAC,SAAS,EAAEF,KAAK,WAAW,EAAE5B,QAAQ6B,KAAK,iBAAiB,EAAEH,MAAMK,KAAK,CAAC,aAAa,EAAEH,KAAK,sEAAsE,CAAC;AAC9K;AAEA,OAAO,SAASI,UAAU,EACxBC,GAAG,EACHC,QAAQ,EACRC,OAAO,KAAK,EACZC,QAAQ,KAAK,EACbC,IAAI,EACJC,OAAO,EACQ;cAsELC,aAMAA;IA3EV,MAAM,CAACC,aAAaC,eAAe,GAAGjD,SAAS;IAC/C,sEAAsE;IACtE,qDAAqD;IACrD,MAAM,CAACkD,SAASC,WAAW,GAAGnD,SAAS;IACvC,MAAM,EAAE+C,IAAI,EAAE7C,MAAM,EAAE,GAAGuC;IACzB,IAAI,CAACM,QAAQ,CAAC7C,QAAQ,OAAO;IAC7B,MAAMkD,UAAUR,QAAQ3C,mBAAmBC,UAAU,EAAE;IACvD,mFAAmF,GACnF,MAAMiC,QAAQ,CAACkB;;eACb,CAACA,MACG,KACAzE,eAAeyE,OACbA,IAAIC,KAAK,CAAC,OAAO5C,MAAM,YACtBqC,wBAAAA,KAAMQ,MAAM,CAACF,IAAI,mBAAIA;;IAC9B,4EAA4E;IAC5E,MAAMG,UAAUf,IAAIgB,MAAM,KAAK,kBAAkBvD,WAAW,QAAQ,CAACZ,uBAAuBmD;IAC5F,wEAAwE;IACxE,wEAAwE;IACxE,2EAA2E;IAC3E,sEAAsE;IACtE,6DAA6D;IAC7D,MAAMiB,QAAQX,QAAQS,WAAWtD,OAAOC,MAAM,KAAK,SAASpB,iBAAiB0D,IAAIkB,IAAI,EAAEZ,QAAQ;IAC/F,yEAAyE;IACzE,oEAAoE;IACpE,MAAMa,sBAAc1D,0BAAAA,OAAQ0D,WAAW,mBAAI;IAC3C,MAAMC,SACJd,QAAQW,SAASjB,IAAIkB,IAAI,KAAK,UAAU1E,mBAAmByE,OAAOE,aAAa9E,eAAeiE,SAAS;IACzG,gGAAgG;IAChG,MAAMe,cACJf,QAAQS,WAAWtD,OAAOC,MAAM,KAAK,UAAUsC,IAAIkB,IAAI,KAAK,UACxD9E,uBAAuBkE,MAAMgB,QAAQH,gBACrC;IACN,MAAMI,gBAAgBvB,IAAIkB,IAAI,KAAK,WAAWlB,IAAIwB,YAAY,KAAK,QAAQT,WAAWtD,OAAOC,MAAM,KAAK;IACxG,MAAM+D,aAAaF,iBAAiBd,UAAU;QAAEA,SAAS;IAAK,IAAI,CAAC;IACnE,qBACE,MAACzD;QAAIsC,IAAI;YAAEoC,IAAI;QAAE;;YACdpB,sBACC,MAACjD;gBAAMsE,SAAS;gBAAKC,MAAK;gBAAOC,cAAW;;kCAC1C,KAACvE;wBAAWwE,SAAQ;wBAAUC,OAAM;kCACjCzB,KAAKU,MAAM,KAAK,cAAc,mBAAmB;;oBAEnDV,KAAK0B,KAAK,CAAChD,GAAG,CAAC,CAACiD,OAAOC,sBACtB,MAAC5E;4BAAkCwE,SAAQ;4BAAQF,MAAK;;gCAAW;gCACrDK,MAAMf,IAAI,KAAK,WAAW,SAASe,MAAMf,IAAI;gCAAC;gCAAExB,MAAMuC,MAAMtD,EAAE;gCAAE;gCAAIsD,MAAME,OAAO;;2BAD9E,CAAC,MAAM,EAAED,OAAO;oBAIlC5B,KAAK8B,MAAM,CAACpD,GAAG,CAAC,CAACiD,OAAOC,sBACvB,MAAC5E;4BAAmCwE,SAAQ;4BAAQF,MAAK;;gCAAW;gCACrD3F,4BAA4B,CAACgG,MAAMf,IAAI,CAAC,CAACmB,IAAI;gCAAC;gCAAEJ,MAAMhD,IAAI;gCACtEgD,MAAMK,WAAW,GACd,CAAC,iBAAiB,EAAE5C,MAAMuC,MAAMK,WAAW,GAAG,GAC9C;gCAAI;gCAAI;gCACTL,MAAMM,GAAG;;2BALG,CAAC,OAAO,EAAEL,OAAO;oBAQnC5B,KAAKkC,OAAO,CAACxD,GAAG,CAAC,CAACyD,QAAQP,sBACzB,MAAC5E;4BAAmCwE,SAAQ;4BAAQF,MAAK;;gCACtDa,OAAOC,MAAM,GACV,CAAC,2BAA2B,EAAED,OAAOE,KAAK,CAAC,uBAAuB,EAAEjD,MAAM+C,OAAOC,MAAM,CAACE,OAAO,EAAE,KAAK,EAAEH,OAAOC,MAAM,CAACG,IAAI,CAAC,EAAE,CAAC,GAC9H,CAAC,gBAAgB,EAAEJ,OAAOE,KAAK,CAAC,IAAI,EAAEF,OAAOK,IAAI,EAAE;gCACtDL,OAAOH,WAAW,GACf,CAAC,iBAAiB,EAAE5C,MAAM+C,OAAOH,WAAW,GAAG,GAC/C;gCACHG,OAAOM,MAAM,GAAG,CAAC,IAAI,EAAErD,MAAM+C,OAAOM,MAAM,GAAG,GAAG;gCAChDN,OAAOO,QAAQ,CAAC/E,MAAM,GACnB,CAAC,EAAE,EAAEwE,OAAOO,QAAQ,CAAChE,GAAG,CAAC,CAACiE,UAAYA,QAAQhE,IAAI,EAAEf,IAAI,CAAC,OAAO,GAChE;;2BAVW,CAAC,OAAO,EAAEgE,OAAO;oBAanC,EAAC5B,cAAAA,KAAK4C,KAAK,YAAV5C,cAAc,EAAE,EAAEtB,GAAG,CAAC,CAACmE,qBACvB,MAAC7F;4BAAqCwE,SAAQ;4BAAQF,MAAK;;gCACxD,CAAC,UAAU,EAAEjF,cAAcwG,KAAKC,EAAE,EAAEC,WAAW,GAAG,CAAC,EAAEF,KAAKlE,IAAI,EAAE;gCAChEkE,KAAKZ,GAAG,GAAG,CAAC,GAAG,EAAEY,KAAKZ,GAAG,EAAE,GAAG;;2BAFhB,CAAC,KAAK,EAAEY,KAAKG,IAAI,EAAE;oBAKrC,EAAChD,eAAAA,KAAKiD,MAAM,YAAXjD,eAAe,EAAE,EAAEtB,GAAG,CAAC,CAACS,OAAOyC,sBAC/B,KAAC5E;4BAAkCwE,SAAQ;4BAAQF,MAAK;sCACrDpC,gBAAgBC,OAAOC;2BADT,CAAC,MAAM,EAAEwC,OAAO;oBAKlCtF,uBAAuB0D,MAAMtB,GAAG,CAAC,CAACwE,qBACjC,KAAClG;4BAAqCwE,SAAQ;4BAAQF,MAAK;4BAAW6B,uBAAoB;sCACvF,CAAC,cAAc,EAAED,KAAKb,KAAK,CAAC,IAAI,EAAEa,KAAKE,IAAI,CAAC,yDAAyD,CAAC;2BADxF,CAAC,MAAM,EAAEF,KAAKG,GAAG,EAAE;;;YAWzCxD,UAAS1C,0BAAAA,OAAQmG,MAAM,mBACtB,KAACtG;gBAAWwE,SAAQ;gBAAUC,OAAM;gBAAiB8B,WAAU;gBAAMvE,IAAI;oBAAEoC,IAAI;gBAAI;0BAChFjE,OAAOmG,MAAM;;YAGjBnG,CAAAA,0BAAAA,OAAQC,MAAM,MAAK,cAAcD,OAAOG,QAAQ,CAACK,MAAM,GAAG,KAAMkC,CAAAA,SAAS,CAAC1C,OAAOmG,MAAM,AAAD,mBACrF,KAAC5G;gBACC6G,WAAU;gBACVvE,IAAI;oBAAEwE,IAAI;oBAAKC,IAAI;gBAAI;gBACvBlC,cAAW;0BAEVpE,OAAOG,QAAQ,CAACoB,GAAG,CAAC,CAAClB,SAASoE,sBAC7B,KAAC5E;wBAECuG,WAAU;wBACV/B,SAAQ;kCAEPhE,QAAQkG,OAAO;uBAJX,CAAC,QAAQ,EAAE9B,OAAO;;YAS9BvB,QAAQ1C,MAAM,GAAG,mBAChB,MAACjB;gBAAIsC,IAAI;oBAAEoC,IAAI;gBAAI;;kCACjB,KAACzE;wBACCgH,MAAK;wBACLC,iBAAe3D;wBACf4D,SAAS,IAAM3D,eAAe,CAAC4D,QAAU,CAACA;kCAEzC7D,cAAc,0BAA0B;;kCAE3C,KAACpD;wBAASkH,IAAI9D;wBAAa+D,aAAa;kCACtC,cAAA,KAAChH;4BACCwE,SAAQ;4BACR+B,WAAU;4BACVhC,cAAW;4BACXvC,IAAI;gCAAEiF,GAAG;gCAAGC,YAAY;gCAAaC,YAAY;gCAAOC,WAAW;4BAAO;sCAEzE/D,QAAQzC,IAAI,CAAC;;;;;YAKrB+C,SAASA,MAAM0D,OAAO,GAAG,mBACxB,MAACrH;gBACCwE,SAAQ;gBACRC,OAAM;gBACNzC,IAAI;oBAAEsF,SAAS;oBAASlD,IAAI;gBAAE;;oBAE7B,CAAC,gBAAgB,EAAEnF,kBAAkB0E,OAAO,wCAAwC,CAAC;oBACrFE,eAAe,CAACC,SACb,CAAC,UAAU,EAAED,YAAY0D,IAAI,CAACC,cAAc,CAAC,SAAS,gDAAgD,EAAErI,wBAAwB0E,YAAY4D,QAAQ,EAAE,CAAC,CAAC,GACxJ;;;YAGP1D,6BACC,KAAC/D;gBAAWwE,SAAQ;gBAAUC,OAAM;gBAAiBzC,IAAI;oBAAEsF,SAAS;oBAASlD,IAAI;gBAAI;0BAClFL;;YAGJN,WAAWtD,OAAOuH,YAAY,IAAIvH,OAAOC,MAAM,KAAK,wBACnD,KAACJ;gBAAWwE,SAAQ;gBAAUC,OAAM;gBAAiB8B,WAAU;gBAAMvE,IAAI;oBAAEoC,IAAI;gBAAE;0BAC9EjE,OAAOuH,YAAY;;YAGvBzD,+BACC,KAACnE;gBACCkC,IAAI;oBAAEsF,SAAS;oBAAQlD,IAAI;gBAAE;gBAC7BuD,uBAAS,KAAC/H;oBAAS+G,MAAK;oBAAQiB,SAASzE;oBAAS0E,UAAU,CAACC,QAAU1E,WAAW0E,MAAMC,MAAM,CAACH,OAAO;;gBACtGI,qBAAO,KAAChI;oBAAWwE,SAAQ;8BAAS;;;YAGvCf,WAAWK,wBACV,KAAC1E;gBACC0E,QAAQA;gBACRiB,MAAK;gBACLhC,SAASA;gBACTH,MAAMA;gBACNqF,iBAAiB,IAAMtF,SAASD,KAAK,aAAKyB;wBAAY+D,kBAAkB;;gBACxEC,WAAW,IAAMxF,SAASD,KAAK,aAAKyB;wBAAY+D,kBAAkB;wBAAME,QAAQ;;;YAGnF3E,WAAW,CAACK,wBACX,KAACnE;gBACCgH,MAAK;gBACLnC,SAAQ;gBACR,8DAA8D;gBAC9D,yCAAyC;gBACzC6D,UAAUzF,QAASzC,OAAOC,MAAM,KAAK,UAAU4D,QAAQ7D,OAAOuH,YAAY;gBAC1Eb,SAAS,IAAMlE,SAASD,KAAKuB,iBAAiBd,UAAUgB,aAAamE;gBACrEtG,IAAI;oBAAEoC,IAAI;gBAAE;0BAEXjE,OAAOC,MAAM,KAAK,SAAS,iBAAiB;;YAIhDqD,WAAWX,QAAQtD,6BAA6BkD,sBAAQ,KAACjD;gBAAsBiD,KAAKA;gBAAKI,MAAMA;;;;AAGtG;AAEA,eAAeL,UAAS"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.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\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiBuildFreePageCapNote, aiBuildSmaller, aiJobCreditRange } from '../model/ai-build-job'\nimport { aiCreditRangeText, aiCreditsPromptFor } from '../model/ai-credit-estimate'\nimport { aiFreeCreditsResetLabel } from '../model/ai-site-job'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport type { AiJobResumeOptions } from './ai-job-requests'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiBuildStorePagesAdded } from '../model/ai-build-store-pages'\nimport {\n AI_PAGE_BUILDING_JOB_KINDS,\n AI_SITE_BUILD_IN_FLIGHT_COPY,\n aiJobConfirmingOwnPlan,\n aiSiteBuildInFlight,\n} from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { useAiJobsInFlightKnown } from './ai-jobs-store'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Alert, Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n /** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */\n orgSlug?: string | null\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n orgSlug,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n // Confirming builds pages while another job is building on this site\n // (AGL-3765), from the list the indicator and the panel already hold: said\n // beside Confirm, which still confirms.\n const confirmsPages =\n job.status === 'needs_review' &&\n job.review?.reason === 'plan' &&\n !aiJobConfirmingOwnPlan(job) &&\n AI_PAGE_BUILDING_JOB_KINDS.includes(job.kind)\n const inFlight = useAiJobsInFlightKnown(user, job.orgId, confirmsPages)\n const building = confirmsPages ? aiSiteBuildInFlight(inFlight, { hostId: job.hostId, exceptJobId: job.id }) : null\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one: what it is likely to cost — what builds like it measured\n // — and its ceiling, every pass at its reserve (AGL-3722). A page job\n // counts the creations it builds before its page (AGL-3031).\n const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null\n // A Free build past what is left asks first (AGL-3722): build what fits,\n // the home page first, or upgrade — never a refusal with no way on.\n const freeCredits = review?.freeCredits ?? null\n const prompt =\n plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null\n // A Free build at its page cap says so (AGL-3722): pages past it were left for a later request.\n const pageCapNote =\n plan && waiting && review.reason === 'plan' && job.kind === 'build'\n ? aiBuildFreePageCapNote(plan, Boolean(freeCredits))\n : null\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n const publishing = offersPublish && publish ? { publish: true } : {}\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n {/* A store's own pages (AGL-3676): added by the platform, written by code, no credits. */}\n {aiBuildStorePagesAdded(plan).map((page) => (\n <Typography key={`store-${page.key}`} variant=\"body2\" role=\"listitem\" data-platform-added=\"true\">\n {`Adds the page ${page.title} at ${page.href} — added for your store automatically, uses no AI credits`}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {range && range.ceiling > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`}\n {freeCredits && !prompt\n ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.`\n : ''}\n </Typography>\n )}\n {pageCapNote && (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mt: 0.5 }}>\n {pageCapNote}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"build\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => onResume(job, { ...publishing, creditsConfirmed: true })}\n onSmaller={() => onResume(job, { ...publishing, creditsConfirmed: true, reduce: 'first-page' })}\n />\n )}\n {building && (\n <Alert severity=\"info\" sx={{ mt: 1 }}>\n {AI_SITE_BUILD_IN_FLIGHT_COPY}\n </Alert>\n )}\n {waiting && !prompt && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? publishing : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiBuildFreePageCapNote","aiBuildSmaller","aiJobCreditRange","aiCreditRangeText","aiCreditsPromptFor","aiFreeCreditsResetLabel","AiCreditsPromptNotice","aiBuildOpNoun","aiBuildStorePagesAdded","AI_PAGE_BUILDING_JOB_KINDS","AI_SITE_BUILD_IN_FLIGHT_COPY","aiJobConfirmingOwnPlan","aiSiteBuildInFlight","aiSiteStarterFallbackOffered","useAiJobsInFlightKnown","AiSiteStarterFallback","Alert","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","orgSlug","plan","detailsOpen","setDetailsOpen","publish","setPublish","confirmsPages","status","includes","kind","inFlight","orgId","building","hostId","exceptJobId","details","ref","slice","labels","waiting","range","freeCredits","prompt","pageCapNote","Boolean","offersPublish","publishAsked","publishing","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","page","data-platform-added","href","key","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","ceiling","display","left","toLocaleString","resetsOn","retryRefusal","control","checked","onChange","event","target","label","onBuildWhatFits","creditsConfirmed","onSmaller","reduce","severity","disabled","undefined"],"mappings":"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"}
|
|
@@ -87,6 +87,17 @@ export declare function resetAiJobsStoreForTests(): void;
|
|
|
87
87
|
export declare function useAiJobsInFlight(user: (TokenSource & {
|
|
88
88
|
uid?: string | null;
|
|
89
89
|
}) | null | undefined, orgId: string | null | undefined, enabled?: boolean): readonly AiJobSummary[];
|
|
90
|
+
/**
|
|
91
|
+
* The same list, as the surfaces that read it hold it, without reading it
|
|
92
|
+
* here (AGL-3765): for a door that warns from what is already known — the
|
|
93
|
+
* top-bar indicator and the Assist panel read the list wherever a person can
|
|
94
|
+
* start a job — and asks no server of its own. It moves with every read they
|
|
95
|
+
* make and every job a surface hands in through {@link publishAiJob}; empty
|
|
96
|
+
* where nothing has read it, and when `enabled` is false.
|
|
97
|
+
*/
|
|
98
|
+
export declare function useAiJobsInFlightKnown(user: (TokenSource & {
|
|
99
|
+
uid?: string | null;
|
|
100
|
+
}) | null | undefined, orgId: string | null | undefined, enabled?: boolean): readonly AiJobSummary[];
|
|
90
101
|
/** What the indicator and the launcher show for the workspace, or `null` for nothing. */
|
|
91
102
|
export declare function useAiJobsActivity(user: (TokenSource & {
|
|
92
103
|
uid?: string | null;
|