@aglyn/plugins-ai 1.0.0-beta.215 → 1.0.0-beta.217

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-ai",
3
- "version": "1.0.0-beta.215",
3
+ "version": "1.0.0-beta.217",
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.215",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.215",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.215",
31
- "@aglyn/shared-data-types": "1.0.0-beta.215",
32
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.215",
33
- "@aglyn/shared-ui-jsx": "1.0.0-beta.215",
34
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.215",
35
- "@aglyn/shared-ui-theme": "1.0.0-beta.215",
36
- "@aglyn/shared-util-email": "1.0.0-beta.215",
37
- "@aglyn/shared-util-errors": "1.0.0-beta.215",
38
- "@aglyn/shared-util-http": "1.0.0-beta.215",
39
- "@aglyn/shared-util-timestamp": "1.0.0-beta.215",
40
- "@aglyn/shared-util-tools": "1.0.0-beta.215",
41
- "@aglyn/tenant-data-admin": "1.0.0-beta.215",
42
- "@aglyn/tenant-feature-instance": "1.0.0-beta.215",
43
- "@aglyn/tenant-runtime": "1.0.0-beta.215",
28
+ "@aglyn/aglyn": "1.0.0-beta.217",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.217",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.217",
31
+ "@aglyn/shared-data-types": "1.0.0-beta.217",
32
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.217",
33
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.217",
34
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.217",
35
+ "@aglyn/shared-ui-theme": "1.0.0-beta.217",
36
+ "@aglyn/shared-util-email": "1.0.0-beta.217",
37
+ "@aglyn/shared-util-errors": "1.0.0-beta.217",
38
+ "@aglyn/shared-util-http": "1.0.0-beta.217",
39
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.217",
40
+ "@aglyn/shared-util-tools": "1.0.0-beta.217",
41
+ "@aglyn/tenant-data-admin": "1.0.0-beta.217",
42
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.217",
43
+ "@aglyn/tenant-runtime": "1.0.0-beta.217",
44
44
  "@swc/helpers": "0.5.23"
45
45
  },
46
46
  "peerDependencies": {
@@ -45,7 +45,8 @@ import { readEventFrames } from "./assist-jobs-drawer.component.js";
45
45
  const BRIEF_MAX_CHARS = 4000;
46
46
  const SOURCE_COPY = {
47
47
  installed: 'Saved as your changes on top of the installed theme. The publisher’s version stays as it is and can still take an update.',
48
- custom: 'Changes this site’s own theme.',
48
+ preset: 'Saved as your changes on top of the built-in theme, which stays as it is and can be restored.',
49
+ custom: 'Saved as your changes on top of this theme of yours. Update it to keep them in the theme itself.',
49
50
  default: 'Only these values are stored. Everything else keeps following the default theme.'
50
51
  };
51
52
  const isTerminal = (job)=>AI_JOB_TERMINAL_STATUSES.includes(job.status);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-theme-proposal-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { HostThemeSource } from '@aglyn/aglyn/app-utils/marketplace-theme'\nimport type { ConsoleHostThemeZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport type { HostThemeScheme } from '@aglyn/shared-data-types'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { themeEditorControl } from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Grid,\n Stack,\n Tab,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Tabs,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n AI_JOB_TERMINAL_STATUSES,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport {\n aiThemeColorToken,\n aiThemeProposalAfter,\n aiThemeStaleChanges,\n readAiThemeProposal,\n type AiThemeControlChange,\n type AiThemeCorrection,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { readEventFrames } from './assist-jobs-drawer.component'\n\n/**\n * The theme assistant (AGL-2938), on the site's Theme section through the\n * `hostTheme` zone: a brief in, a theme proposal out, previewed before and\n * after with the editor's own preview, and put in the editor below as\n * unsaved changes when the person chooses. It never writes the theme — the\n * editor's Save does, through the same guarded path every edit takes.\n *\n * It renders nothing until the jobs route has answered for this workspace.\n * The shell has already decided the plan and the member's permission before\n * mounting it, but a released-off feature does not exist, and the release\n * flag is the route's to decide: a 404 there is this card staying absent.\n * The same read lists the site's recent proposals, so one made while the\n * person was elsewhere is still here to preview.\n */\n\n/** How many recent jobs the card reads to find this site's proposals. */\nconst RECENT_JOBS_READ = 20\n\n/** How many of this site's proposals the card offers again. */\nconst RECENT_PROPOSALS_SHOWN = 3\n\nconst BRIEF_MAX_CHARS = 4_000\n\nconst SOURCE_COPY: Record<HostThemeSource, string> = {\n installed:\n 'Saved as your changes on top of the installed theme. The publisher’s version stays as it is and can still take an update.',\n custom: 'Changes this site’s own theme.',\n default:\n 'Only these values are stored. Everything else keeps following the default theme.',\n}\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\nconst isTerminal = (job: AiJobSummary) => AI_JOB_TERMINAL_STATUSES.includes(job.status)\n\n/** The proposal a theme job produced, when it produced one. */\nfunction proposalOf(job: AiJobSummary | null): AiThemeProposal | null {\n const output = job?.outputs.find((entry) => entry.resource === 'theme')\n return output ? readAiThemeProposal(output.proposal) : null\n}\n\n/** A control's label, with its scheme when it has one. */\nfunction changeLabel(change: Pick<AiThemeControlChange, 'control' | 'scheme'>): string {\n return themeEditorControl(change.control).label\n}\n\nfunction Swatch({ value }: { value: string | number | null }) {\n if (value === null) {\n return (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Default'}\n </Typography>\n )\n }\n const text = String(value)\n const color = typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value)\n return (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n {color ? (\n <Box\n aria-hidden\n sx={{\n width: 14,\n height: 14,\n borderRadius: 0.5,\n border: 1,\n borderColor: 'divider',\n backgroundColor: text,\n flexShrink: 0,\n }}\n />\n ) : null}\n <Typography variant=\"body2\" sx={{ fontFamily: 'monospace', wordBreak: 'break-all' }}>\n {text}\n </Typography>\n </Stack>\n )\n}\n\nfunction correctionText(correction: AiThemeCorrection): string {\n const label = `${changeLabel(correction)} (${correction.scheme})`\n if (correction.reason === 'dark-value') {\n return `${label} set to ${correction.to} so the color keeps reading on the dark background.`\n }\n return (\n `${label} moved to ${correction.to} to reach ${correction.required}:1` +\n (correction.against?.length ? ` against ${correction.against.join(' and ')}` : '') +\n (correction.ratio ? ` (it was ${correction.ratio}:1).` : '.')\n )\n}\n\nexport function AiThemeProposalCard(props: ConsoleHostThemeZoneProps) {\n const { hostId, orgId, theme, themeSource, ThemePreview, proposeDraft } = props\n const { data: user } = useUser()\n // Held in a ref so the reads below key on WHO is signed in, never on the\n // identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [notice, setNotice] = useState<string | null>(null)\n const [recent, setRecent] = useState<AiJobSummary[]>([])\n const [brief, setBrief] = useState('')\n const [mode, setMode] = useState<AiThemeProposalMode>('modify')\n const [busy, setBusy] = useState(false)\n const [job, setJob] = useState<AiJobSummary | null>(null)\n const [scheme, setScheme] = useState<HostThemeScheme>('light')\n const [applied, setApplied] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n useEffect(() => () => watchRef.current?.abort(), [])\n\n useEffect(() => {\n if (!orgId || !hostId || !uid) return\n let active = true\n setVerdict('checking')\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=${RECENT_JOBS_READ}`,\n )\n if (!active) return\n // The flag off (404) and the plan without generation (403): the\n // feature is not this workspace's, so the card is not here at all.\n if (response.status === 404 || response.status === 403) {\n setVerdict('hidden')\n return\n }\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'Recent theme proposals could not be loaded.'),\n )\n } else {\n const jobs = (payload?.jobs ?? []) as AiJobSummary[]\n setRecent(\n jobs\n .filter((entry) => entry.kind === 'theme' && entry.hostId === hostId)\n .filter((entry) => proposalOf(entry) !== null)\n .slice(0, RECENT_PROPOSALS_SHOWN),\n )\n }\n setVerdict('ready')\n } catch {\n if (!active) return\n setNotice('Recent theme proposals could not be loaded.')\n setVerdict('ready')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, hostId, uid])\n\n /** Follows a job through the events route until it settles. */\n const watch = useCallback(async (jobId: string, org: string) => {\n watchRef.current?.abort()\n const controller = new AbortController()\n watchRef.current = controller\n let again = true\n while (again && !controller.signal.aborted) {\n again = false\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs/${encodeURIComponent(jobId)}/events?orgId=${encodeURIComponent(org)}`,\n { signal: controller.signal },\n )\n if (!response.ok || !response.body) return\n await readEventFrames(response.body, (event) => {\n if (event['type'] === 'state') setJob(event['job'] as AiJobSummary)\n else if (event['type'] === 'reconnect') again = true\n })\n } catch {\n return\n }\n }\n }, [])\n\n const propose = useCallback(async () => {\n if (!orgId || !brief.trim()) return\n setBusy(true)\n setNotice(null)\n setApplied(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'theme',\n brief: brief.trim(),\n inputs: { mode },\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'The theme proposal could not be started — try again.'),\n )\n return\n }\n const next = payload?.job as AiJobSummary | undefined\n if (!next) return\n setJob(next)\n if (!isTerminal(next)) await watch(next.id, orgId)\n } catch {\n setNotice('The theme proposal could not be started — try again.')\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, brief, mode, watch])\n\n const proposal = useMemo(() => proposalOf(job), [job])\n const after = useMemo(\n () => (proposal ? aiThemeProposalAfter(theme, proposal) : null),\n [theme, proposal],\n )\n const stale = useMemo(\n () => (proposal ? aiThemeStaleChanges(theme, proposal).length > 0 : false),\n [theme, proposal],\n )\n\n const apply = useCallback(() => {\n if (!job || !after) return\n proposeDraft(after.theme, job.id)\n setApplied(job.id)\n }, [job, after, proposeDraft])\n\n if (verdict !== 'ready') return null\n\n const running = Boolean(job && !isTerminal(job))\n\n return (\n <CardDisplay\n contentGutterX\n contentGutterY\n header=\"Theme assistant\"\n help={pluginDocsHelp('aiThemes', {\n excerpt:\n 'Describe the change and get a proposal for every control the editor has, previewed before and after. Nothing is saved until you save it in the editor.',\n })}\n >\n <Stack spacing={2}>\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n value={mode}\n onChange={(_, value: AiThemeProposalMode | null) => value && setMode(value)}\n aria-label=\"What to propose\"\n >\n <ToggleButton value=\"modify\">{'Change what I describe'}</ToggleButton>\n <ToggleButton value=\"create\">{'Design a new theme'}</ToggleButton>\n </ToggleButtonGroup>\n <TextField\n multiline\n minRows={2}\n fullWidth\n label=\"Describe the change\"\n placeholder=\"Warmer, with a deep green primary and bigger headings on mobile\"\n helperText=\"A hex color in your description is used exactly. Paste a link to borrow a page’s colors.\"\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button variant=\"contained\" disabled={busy || !brief.trim()} onClick={() => void propose()}>\n {'Propose changes'}\n </Button>\n {running ? <CircularProgress size={18} aria-label=\"Working on a proposal\" /> : null}\n </Stack>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n\n {job && job.error && !proposal ? (\n <Alert severity={job.status === 'failed' ? 'error' : 'warning'}>{job.error}</Alert>\n ) : null}\n\n {!job && recent.length ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Recent proposals for this site'}</Typography>\n {recent.map((entry) => (\n <Stack key={entry.id} direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flexGrow: 1, minWidth: 0 }} noWrap>\n {entry.brief}\n </Typography>\n <Button size=\"small\" onClick={() => setJob(entry)}>\n {'Show'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ) : null}\n\n {proposal && after ? (\n <Stack spacing={2} aria-label=\"Theme proposal\">\n {proposal.summary ? <Typography variant=\"body1\">{proposal.summary}</Typography> : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {SOURCE_COPY[proposal.source]}\n </Typography>\n {stale ? (\n <Alert severity=\"info\">\n {'The theme has changed since this proposal was made. The preview and the editor apply it to the theme as it is now.'}\n </Alert>\n ) : null}\n\n {proposal.changes.length || proposal.components.length || proposal.resetComponents ? (\n <Box>\n <ScrollTable size=\"small\" aria-label=\"Proposed changes\">\n <TableHead>\n <TableRow>\n <TableCell>{'Control'}</TableCell>\n <TableCell>{'Now'}</TableCell>\n <TableCell>{'Proposed'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {proposal.changes.map((change) => (\n <TableRow key={`${change.control}:${change.scheme ?? ''}`}>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{changeLabel(change)}</Typography>\n {aiThemeColorToken(change.control) && change.scheme ? (\n <Chip size=\"small\" variant=\"outlined\" label={change.scheme} />\n ) : null}\n </Stack>\n </TableCell>\n <TableCell>\n <Swatch value={change.before} />\n </TableCell>\n <TableCell>\n <Swatch value={change.value} />\n </TableCell>\n </TableRow>\n ))}\n {proposal.resetComponents ? (\n <TableRow>\n <TableCell>{themeEditorControl('components').label}</TableCell>\n <TableCell>{'This site’s overrides'}</TableCell>\n <TableCell>{'Theme defaults'}</TableCell>\n </TableRow>\n ) : null}\n {proposal.components.map((leaf) => (\n <TableRow key={`${leaf.component}:${leaf.target}:${leaf.slot}:${leaf.media}:${leaf.property}`}>\n <TableCell>\n <Typography variant=\"body2\">\n {[\n leaf.component,\n leaf.target === 'defaultProps' ? 'default' : leaf.slot,\n leaf.property,\n ].join(' · ')}\n </Typography>\n {leaf.media ? <Chip size=\"small\" variant=\"outlined\" label={leaf.media} /> : null}\n </TableCell>\n <TableCell>{'—'}</TableCell>\n <TableCell>\n <Swatch value={typeof leaf.value === 'boolean' ? String(leaf.value) : leaf.value} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </Box>\n ) : (\n <Typography variant=\"body2\">{'No changes proposed.'}</Typography>\n )}\n\n {[...proposal.corrections, ...after.corrections].length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">{'Adjusted for readability'}</Typography>\n {[...proposal.corrections, ...after.corrections].map((correction) => (\n <Typography\n key={`${correction.control}:${correction.scheme}:${correction.to}`}\n variant=\"body2\"\n >\n {correctionText(correction)}\n </Typography>\n ))}\n </Stack>\n ) : null}\n\n {[...proposal.notes, ...after.notes, ...proposal.dropped].length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">{'Worth knowing'}</Typography>\n {[...proposal.notes, ...after.notes].map((line) => (\n <Typography key={`note:${line}`} variant=\"body2\" color=\"text.secondary\">\n {line}\n </Typography>\n ))}\n {proposal.dropped.map((line) => (\n <Typography key={`dropped:${line}`} variant=\"body2\" color=\"text.secondary\">\n {`Left out — ${line}`}\n </Typography>\n ))}\n </Stack>\n ) : null}\n\n <Tabs\n value={scheme}\n onChange={(_, value: HostThemeScheme) => setScheme(value)}\n aria-label=\"Preview scheme\"\n >\n <Tab label=\"Light\" value=\"light\" />\n <Tab label=\"Dark\" value=\"dark\" />\n </Tabs>\n <Grid container spacing={2}>\n <Grid size={{ xs: 12, md: 6 }}>\n <Typography variant=\"subtitle2\" gutterBottom>\n {'Before'}\n </Typography>\n <Box data-preview=\"before\">\n <ThemePreview theme={theme ?? {}} scheme={scheme} />\n </Box>\n </Grid>\n <Grid size={{ xs: 12, md: 6 }}>\n <Typography variant=\"subtitle2\" gutterBottom>\n {'After'}\n </Typography>\n <Box data-preview=\"after\">\n <ThemePreview theme={after.theme} scheme={scheme} />\n </Box>\n </Grid>\n </Grid>\n\n {applied === job?.id ? (\n <Alert severity=\"success\">\n {'In the editor below as unsaved changes. Review them there, then Save — or Discard changes to go back.'}\n </Alert>\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"contained\" onClick={apply} disabled={applied === job?.id}>\n {'Put in the editor'}\n </Button>\n <Button\n onClick={() => {\n setJob(null)\n setApplied(null)\n }}\n >\n {'Dismiss'}\n </Button>\n </Stack>\n </Stack>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nAiThemeProposalCard.displayName = 'AiThemeProposalCard'\n\nexport default AiThemeProposalCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","pluginDocsHelp","CardDisplay","ScrollTable","themeEditorControl","authorizedFetch","useUser","Alert","Box","Button","Chip","CircularProgress","Grid","Stack","Tab","TableBody","TableCell","TableHead","TableRow","Tabs","TextField","ToggleButton","ToggleButtonGroup","Typography","useCallback","useEffect","useMemo","useRef","useState","AI_JOB_TERMINAL_STATUSES","aiThemeColorToken","aiThemeProposalAfter","aiThemeStaleChanges","readAiThemeProposal","readEventFrames","RECENT_JOBS_READ","RECENT_PROPOSALS_SHOWN","BRIEF_MAX_CHARS","SOURCE_COPY","installed","custom","default","isTerminal","job","includes","status","proposalOf","output","outputs","find","entry","resource","proposal","changeLabel","change","control","label","Swatch","value","variant","color","text","String","test","direction","spacing","sx","alignItems","aria-hidden","width","height","borderRadius","border","borderColor","backgroundColor","flexShrink","fontFamily","wordBreak","correctionText","correction","scheme","reason","to","required","against","length","join","ratio","AiThemeProposalCard","props","hostId","orgId","theme","themeSource","ThemePreview","proposeDraft","data","user","userRef","current","uid","verdict","setVerdict","notice","setNotice","recent","setRecent","brief","setBrief","mode","setMode","busy","setBusy","setJob","setScheme","applied","setApplied","watchRef","abort","active","response","encodeURIComponent","payload","json","catch","ok","locked","error","jobs","filter","kind","slice","watch","jobId","org","controller","AbortController","again","signal","aborted","body","event","propose","trim","method","headers","JSON","stringify","inputs","next","id","after","stale","apply","running","Boolean","contentGutterX","contentGutterY","header","help","excerpt","exclusive","size","onChange","_","aria-label","multiline","minRows","fullWidth","placeholder","helperText","target","disabled","onClick","severity","map","flexGrow","minWidth","noWrap","summary","source","changes","components","resetComponents","before","leaf","component","slot","property","media","corrections","notes","dropped","line","container","xs","md","gutterBottom","data-preview","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-theme-proposal-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { HostThemeSource } from '@aglyn/aglyn/app-utils/marketplace-theme'\nimport type { ConsoleHostThemeZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport type { HostThemeScheme } from '@aglyn/shared-data-types'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { themeEditorControl } from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Grid,\n Stack,\n Tab,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Tabs,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n AI_JOB_TERMINAL_STATUSES,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport {\n aiThemeColorToken,\n aiThemeProposalAfter,\n aiThemeStaleChanges,\n readAiThemeProposal,\n type AiThemeControlChange,\n type AiThemeCorrection,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { readEventFrames } from './assist-jobs-drawer.component'\n\n/**\n * The theme assistant (AGL-2938), on the site's Theme section through the\n * `hostTheme` zone: a brief in, a theme proposal out, previewed before and\n * after with the editor's own preview, and put in the editor below as\n * unsaved changes when the person chooses. It never writes the theme — the\n * editor's Save does, through the same guarded path every edit takes.\n *\n * It renders nothing until the jobs route has answered for this workspace.\n * The shell has already decided the plan and the member's permission before\n * mounting it, but a released-off feature does not exist, and the release\n * flag is the route's to decide: a 404 there is this card staying absent.\n * The same read lists the site's recent proposals, so one made while the\n * person was elsewhere is still here to preview.\n */\n\n/** How many recent jobs the card reads to find this site's proposals. */\nconst RECENT_JOBS_READ = 20\n\n/** How many of this site's proposals the card offers again. */\nconst RECENT_PROPOSALS_SHOWN = 3\n\nconst BRIEF_MAX_CHARS = 4_000\n\nconst SOURCE_COPY: Record<HostThemeSource, string> = {\n installed:\n 'Saved as your changes on top of the installed theme. The publisher’s version stays as it is and can still take an update.',\n preset:\n 'Saved as your changes on top of the built-in theme, which stays as it is and can be restored.',\n custom:\n 'Saved as your changes on top of this theme of yours. Update it to keep them in the theme itself.',\n default:\n 'Only these values are stored. Everything else keeps following the default theme.',\n}\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\nconst isTerminal = (job: AiJobSummary) => AI_JOB_TERMINAL_STATUSES.includes(job.status)\n\n/** The proposal a theme job produced, when it produced one. */\nfunction proposalOf(job: AiJobSummary | null): AiThemeProposal | null {\n const output = job?.outputs.find((entry) => entry.resource === 'theme')\n return output ? readAiThemeProposal(output.proposal) : null\n}\n\n/** A control's label, with its scheme when it has one. */\nfunction changeLabel(change: Pick<AiThemeControlChange, 'control' | 'scheme'>): string {\n return themeEditorControl(change.control).label\n}\n\nfunction Swatch({ value }: { value: string | number | null }) {\n if (value === null) {\n return (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Default'}\n </Typography>\n )\n }\n const text = String(value)\n const color = typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value)\n return (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n {color ? (\n <Box\n aria-hidden\n sx={{\n width: 14,\n height: 14,\n borderRadius: 0.5,\n border: 1,\n borderColor: 'divider',\n backgroundColor: text,\n flexShrink: 0,\n }}\n />\n ) : null}\n <Typography variant=\"body2\" sx={{ fontFamily: 'monospace', wordBreak: 'break-all' }}>\n {text}\n </Typography>\n </Stack>\n )\n}\n\nfunction correctionText(correction: AiThemeCorrection): string {\n const label = `${changeLabel(correction)} (${correction.scheme})`\n if (correction.reason === 'dark-value') {\n return `${label} set to ${correction.to} so the color keeps reading on the dark background.`\n }\n return (\n `${label} moved to ${correction.to} to reach ${correction.required}:1` +\n (correction.against?.length ? ` against ${correction.against.join(' and ')}` : '') +\n (correction.ratio ? ` (it was ${correction.ratio}:1).` : '.')\n )\n}\n\nexport function AiThemeProposalCard(props: ConsoleHostThemeZoneProps) {\n const { hostId, orgId, theme, themeSource, ThemePreview, proposeDraft } = props\n const { data: user } = useUser()\n // Held in a ref so the reads below key on WHO is signed in, never on the\n // identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [notice, setNotice] = useState<string | null>(null)\n const [recent, setRecent] = useState<AiJobSummary[]>([])\n const [brief, setBrief] = useState('')\n const [mode, setMode] = useState<AiThemeProposalMode>('modify')\n const [busy, setBusy] = useState(false)\n const [job, setJob] = useState<AiJobSummary | null>(null)\n const [scheme, setScheme] = useState<HostThemeScheme>('light')\n const [applied, setApplied] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n useEffect(() => () => watchRef.current?.abort(), [])\n\n useEffect(() => {\n if (!orgId || !hostId || !uid) return\n let active = true\n setVerdict('checking')\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=${RECENT_JOBS_READ}`,\n )\n if (!active) return\n // The flag off (404) and the plan without generation (403): the\n // feature is not this workspace's, so the card is not here at all.\n if (response.status === 404 || response.status === 403) {\n setVerdict('hidden')\n return\n }\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'Recent theme proposals could not be loaded.'),\n )\n } else {\n const jobs = (payload?.jobs ?? []) as AiJobSummary[]\n setRecent(\n jobs\n .filter((entry) => entry.kind === 'theme' && entry.hostId === hostId)\n .filter((entry) => proposalOf(entry) !== null)\n .slice(0, RECENT_PROPOSALS_SHOWN),\n )\n }\n setVerdict('ready')\n } catch {\n if (!active) return\n setNotice('Recent theme proposals could not be loaded.')\n setVerdict('ready')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, hostId, uid])\n\n /** Follows a job through the events route until it settles. */\n const watch = useCallback(async (jobId: string, org: string) => {\n watchRef.current?.abort()\n const controller = new AbortController()\n watchRef.current = controller\n let again = true\n while (again && !controller.signal.aborted) {\n again = false\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs/${encodeURIComponent(jobId)}/events?orgId=${encodeURIComponent(org)}`,\n { signal: controller.signal },\n )\n if (!response.ok || !response.body) return\n await readEventFrames(response.body, (event) => {\n if (event['type'] === 'state') setJob(event['job'] as AiJobSummary)\n else if (event['type'] === 'reconnect') again = true\n })\n } catch {\n return\n }\n }\n }, [])\n\n const propose = useCallback(async () => {\n if (!orgId || !brief.trim()) return\n setBusy(true)\n setNotice(null)\n setApplied(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'theme',\n brief: brief.trim(),\n inputs: { mode },\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(\n locked\n ? lockdownRefusalText(locked)\n : String(payload?.error ?? 'The theme proposal could not be started — try again.'),\n )\n return\n }\n const next = payload?.job as AiJobSummary | undefined\n if (!next) return\n setJob(next)\n if (!isTerminal(next)) await watch(next.id, orgId)\n } catch {\n setNotice('The theme proposal could not be started — try again.')\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, brief, mode, watch])\n\n const proposal = useMemo(() => proposalOf(job), [job])\n const after = useMemo(\n () => (proposal ? aiThemeProposalAfter(theme, proposal) : null),\n [theme, proposal],\n )\n const stale = useMemo(\n () => (proposal ? aiThemeStaleChanges(theme, proposal).length > 0 : false),\n [theme, proposal],\n )\n\n const apply = useCallback(() => {\n if (!job || !after) return\n proposeDraft(after.theme, job.id)\n setApplied(job.id)\n }, [job, after, proposeDraft])\n\n if (verdict !== 'ready') return null\n\n const running = Boolean(job && !isTerminal(job))\n\n return (\n <CardDisplay\n contentGutterX\n contentGutterY\n header=\"Theme assistant\"\n help={pluginDocsHelp('aiThemes', {\n excerpt:\n 'Describe the change and get a proposal for every control the editor has, previewed before and after. Nothing is saved until you save it in the editor.',\n })}\n >\n <Stack spacing={2}>\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n value={mode}\n onChange={(_, value: AiThemeProposalMode | null) => value && setMode(value)}\n aria-label=\"What to propose\"\n >\n <ToggleButton value=\"modify\">{'Change what I describe'}</ToggleButton>\n <ToggleButton value=\"create\">{'Design a new theme'}</ToggleButton>\n </ToggleButtonGroup>\n <TextField\n multiline\n minRows={2}\n fullWidth\n label=\"Describe the change\"\n placeholder=\"Warmer, with a deep green primary and bigger headings on mobile\"\n helperText=\"A hex color in your description is used exactly. Paste a link to borrow a page’s colors.\"\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button variant=\"contained\" disabled={busy || !brief.trim()} onClick={() => void propose()}>\n {'Propose changes'}\n </Button>\n {running ? <CircularProgress size={18} aria-label=\"Working on a proposal\" /> : null}\n </Stack>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n\n {job && job.error && !proposal ? (\n <Alert severity={job.status === 'failed' ? 'error' : 'warning'}>{job.error}</Alert>\n ) : null}\n\n {!job && recent.length ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Recent proposals for this site'}</Typography>\n {recent.map((entry) => (\n <Stack key={entry.id} direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" sx={{ flexGrow: 1, minWidth: 0 }} noWrap>\n {entry.brief}\n </Typography>\n <Button size=\"small\" onClick={() => setJob(entry)}>\n {'Show'}\n </Button>\n </Stack>\n ))}\n </Stack>\n ) : null}\n\n {proposal && after ? (\n <Stack spacing={2} aria-label=\"Theme proposal\">\n {proposal.summary ? <Typography variant=\"body1\">{proposal.summary}</Typography> : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {SOURCE_COPY[proposal.source]}\n </Typography>\n {stale ? (\n <Alert severity=\"info\">\n {'The theme has changed since this proposal was made. The preview and the editor apply it to the theme as it is now.'}\n </Alert>\n ) : null}\n\n {proposal.changes.length || proposal.components.length || proposal.resetComponents ? (\n <Box>\n <ScrollTable size=\"small\" aria-label=\"Proposed changes\">\n <TableHead>\n <TableRow>\n <TableCell>{'Control'}</TableCell>\n <TableCell>{'Now'}</TableCell>\n <TableCell>{'Proposed'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {proposal.changes.map((change) => (\n <TableRow key={`${change.control}:${change.scheme ?? ''}`}>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{changeLabel(change)}</Typography>\n {aiThemeColorToken(change.control) && change.scheme ? (\n <Chip size=\"small\" variant=\"outlined\" label={change.scheme} />\n ) : null}\n </Stack>\n </TableCell>\n <TableCell>\n <Swatch value={change.before} />\n </TableCell>\n <TableCell>\n <Swatch value={change.value} />\n </TableCell>\n </TableRow>\n ))}\n {proposal.resetComponents ? (\n <TableRow>\n <TableCell>{themeEditorControl('components').label}</TableCell>\n <TableCell>{'This site’s overrides'}</TableCell>\n <TableCell>{'Theme defaults'}</TableCell>\n </TableRow>\n ) : null}\n {proposal.components.map((leaf) => (\n <TableRow key={`${leaf.component}:${leaf.target}:${leaf.slot}:${leaf.media}:${leaf.property}`}>\n <TableCell>\n <Typography variant=\"body2\">\n {[\n leaf.component,\n leaf.target === 'defaultProps' ? 'default' : leaf.slot,\n leaf.property,\n ].join(' · ')}\n </Typography>\n {leaf.media ? <Chip size=\"small\" variant=\"outlined\" label={leaf.media} /> : null}\n </TableCell>\n <TableCell>{'—'}</TableCell>\n <TableCell>\n <Swatch value={typeof leaf.value === 'boolean' ? String(leaf.value) : leaf.value} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </Box>\n ) : (\n <Typography variant=\"body2\">{'No changes proposed.'}</Typography>\n )}\n\n {[...proposal.corrections, ...after.corrections].length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">{'Adjusted for readability'}</Typography>\n {[...proposal.corrections, ...after.corrections].map((correction) => (\n <Typography\n key={`${correction.control}:${correction.scheme}:${correction.to}`}\n variant=\"body2\"\n >\n {correctionText(correction)}\n </Typography>\n ))}\n </Stack>\n ) : null}\n\n {[...proposal.notes, ...after.notes, ...proposal.dropped].length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">{'Worth knowing'}</Typography>\n {[...proposal.notes, ...after.notes].map((line) => (\n <Typography key={`note:${line}`} variant=\"body2\" color=\"text.secondary\">\n {line}\n </Typography>\n ))}\n {proposal.dropped.map((line) => (\n <Typography key={`dropped:${line}`} variant=\"body2\" color=\"text.secondary\">\n {`Left out — ${line}`}\n </Typography>\n ))}\n </Stack>\n ) : null}\n\n <Tabs\n value={scheme}\n onChange={(_, value: HostThemeScheme) => setScheme(value)}\n aria-label=\"Preview scheme\"\n >\n <Tab label=\"Light\" value=\"light\" />\n <Tab label=\"Dark\" value=\"dark\" />\n </Tabs>\n <Grid container spacing={2}>\n <Grid size={{ xs: 12, md: 6 }}>\n <Typography variant=\"subtitle2\" gutterBottom>\n {'Before'}\n </Typography>\n <Box data-preview=\"before\">\n <ThemePreview theme={theme ?? {}} scheme={scheme} />\n </Box>\n </Grid>\n <Grid size={{ xs: 12, md: 6 }}>\n <Typography variant=\"subtitle2\" gutterBottom>\n {'After'}\n </Typography>\n <Box data-preview=\"after\">\n <ThemePreview theme={after.theme} scheme={scheme} />\n </Box>\n </Grid>\n </Grid>\n\n {applied === job?.id ? (\n <Alert severity=\"success\">\n {'In the editor below as unsaved changes. Review them there, then Save — or Discard changes to go back.'}\n </Alert>\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"contained\" onClick={apply} disabled={applied === job?.id}>\n {'Put in the editor'}\n </Button>\n <Button\n onClick={() => {\n setJob(null)\n setApplied(null)\n }}\n >\n {'Dismiss'}\n </Button>\n </Stack>\n </Stack>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nAiThemeProposalCard.displayName = 'AiThemeProposalCard'\n\nexport default AiThemeProposalCard\n"],"names":["lockdownRefusalText","parseLockdownRefusal","pluginDocsHelp","CardDisplay","ScrollTable","themeEditorControl","authorizedFetch","useUser","Alert","Box","Button","Chip","CircularProgress","Grid","Stack","Tab","TableBody","TableCell","TableHead","TableRow","Tabs","TextField","ToggleButton","ToggleButtonGroup","Typography","useCallback","useEffect","useMemo","useRef","useState","AI_JOB_TERMINAL_STATUSES","aiThemeColorToken","aiThemeProposalAfter","aiThemeStaleChanges","readAiThemeProposal","readEventFrames","RECENT_JOBS_READ","RECENT_PROPOSALS_SHOWN","BRIEF_MAX_CHARS","SOURCE_COPY","installed","preset","custom","default","isTerminal","job","includes","status","proposalOf","output","outputs","find","entry","resource","proposal","changeLabel","change","control","label","Swatch","value","variant","color","text","String","test","direction","spacing","sx","alignItems","aria-hidden","width","height","borderRadius","border","borderColor","backgroundColor","flexShrink","fontFamily","wordBreak","correctionText","correction","scheme","reason","to","required","against","length","join","ratio","AiThemeProposalCard","props","hostId","orgId","theme","themeSource","ThemePreview","proposeDraft","data","user","userRef","current","uid","verdict","setVerdict","notice","setNotice","recent","setRecent","brief","setBrief","mode","setMode","busy","setBusy","setJob","setScheme","applied","setApplied","watchRef","abort","active","response","encodeURIComponent","payload","json","catch","ok","locked","error","jobs","filter","kind","slice","watch","jobId","org","controller","AbortController","again","signal","aborted","body","event","propose","trim","method","headers","JSON","stringify","inputs","next","id","after","stale","apply","running","Boolean","contentGutterX","contentGutterY","header","help","excerpt","exclusive","size","onChange","_","aria-label","multiline","minRows","fullWidth","placeholder","helperText","target","disabled","onClick","severity","map","flexGrow","minWidth","noWrap","summary","source","changes","components","resetComponents","before","leaf","component","slot","property","media","corrections","notes","dropped","line","container","xs","md","gutterBottom","data-preview","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,mBAAmB,EAAEC,oBAAoB,QAAQ,eAAc;AACxE,SAASC,cAAc,QAAQ,mCAAkC;AAIjE,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,WAAW,QAAQ,yDAAwD;AACpF,SAASC,kBAAkB,QAAQ,kDAAiD;AACpF,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SACEC,KAAK,EACLC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,gBAAgB,EAChBC,IAAI,EACJC,KAAK,EACLC,GAAG,EACHC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,IAAI,EACJC,SAAS,EACTC,YAAY,EACZC,iBAAiB,EACjBC,UAAU,QACL,gBAAe;AACtB,SAASC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACzE,SACEC,wBAAwB,QAEnB,4BAAwB;AAC/B,SACEC,iBAAiB,EACjBC,oBAAoB,EACpBC,mBAAmB,EACnBC,mBAAmB,QAKd,gCAA4B;AACnC,SAASC,eAAe,QAAQ,oCAAgC;AAEhE;;;;;;;;;;;;;CAaC,GAED,uEAAuE,GACvE,MAAMC,mBAAmB;AAEzB,6DAA6D,GAC7D,MAAMC,yBAAyB;AAE/B,MAAMC,kBAAkB;AAExB,MAAMC,cAA+C;IACnDC,WACE;IACFC,QACE;IACFC,QACE;IACFC,SACE;AACJ;AAIA,MAAMC,aAAa,CAACC,MAAsBf,yBAAyBgB,QAAQ,CAACD,IAAIE,MAAM;AAEtF,6DAA6D,GAC7D,SAASC,WAAWH,GAAwB;IAC1C,MAAMI,SAASJ,uBAAAA,IAAKK,OAAO,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMC,QAAQ,KAAK;IAC/D,OAAOJ,SAASf,oBAAoBe,OAAOK,QAAQ,IAAI;AACzD;AAEA,wDAAwD,GACxD,SAASC,YAAYC,MAAwD;IAC3E,OAAOnD,mBAAmBmD,OAAOC,OAAO,EAAEC,KAAK;AACjD;AAEA,SAASC,OAAO,EAAEC,KAAK,EAAqC;IAC1D,IAAIA,UAAU,MAAM;QAClB,qBACE,KAACpC;YAAWqC,SAAQ;YAAUC,OAAM;sBACjC;;IAGP;IACA,MAAMC,OAAOC,OAAOJ;IACpB,MAAME,QAAQ,OAAOF,UAAU,YAAY,kBAAkBK,IAAI,CAACL;IAClE,qBACE,MAAC9C;QAAMoD,WAAU;QAAMC,SAAS;QAAGC,IAAI;YAAEC,YAAY;QAAS;;YAC3DP,sBACC,KAACrD;gBACC6D,aAAW;gBACXF,IAAI;oBACFG,OAAO;oBACPC,QAAQ;oBACRC,cAAc;oBACdC,QAAQ;oBACRC,aAAa;oBACbC,iBAAiBb;oBACjBc,YAAY;gBACd;iBAEA;0BACJ,KAACrD;gBAAWqC,SAAQ;gBAAQO,IAAI;oBAAEU,YAAY;oBAAaC,WAAW;gBAAY;0BAC/EhB;;;;AAIT;AAEA,SAASiB,eAAeC,UAA6B;QAOhDA;IANH,MAAMvB,QAAQ,GAAGH,YAAY0B,YAAY,EAAE,EAAEA,WAAWC,MAAM,CAAC,CAAC,CAAC;IACjE,IAAID,WAAWE,MAAM,KAAK,cAAc;QACtC,OAAO,GAAGzB,MAAM,QAAQ,EAAEuB,WAAWG,EAAE,CAAC,mDAAmD,CAAC;IAC9F;IACA,OACE,GAAG1B,MAAM,UAAU,EAAEuB,WAAWG,EAAE,CAAC,UAAU,EAAEH,WAAWI,QAAQ,CAAC,EAAE,CAAC,GACrEJ,CAAAA,EAAAA,sBAAAA,WAAWK,OAAO,qBAAlBL,oBAAoBM,MAAM,IAAG,CAAC,SAAS,EAAEN,WAAWK,OAAO,CAACE,IAAI,CAAC,UAAU,GAAG,EAAC,IAC/EP,CAAAA,WAAWQ,KAAK,GAAG,CAAC,SAAS,EAAER,WAAWQ,KAAK,CAAC,IAAI,CAAC,GAAG,GAAE;AAE/D;AAEA,OAAO,SAASC,oBAAoBC,KAAgC;;IAClE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAEC,WAAW,EAAEC,YAAY,EAAEC,YAAY,EAAE,GAAGN;IAC1E,MAAM,EAAEO,MAAMC,IAAI,EAAE,GAAG5F;IACvB,yEAAyE;IACzE,uCAAuC;IACvC,MAAM6F,UAAUxE,OAAOuE;IACvBC,QAAQC,OAAO,GAAGF;IAClB,MAAMG,cAAMH,wBAAAA,KAAMG,GAAG,mBAAI;IAEzB,MAAM,CAACC,SAASC,WAAW,GAAG3E,SAAkB;IAChD,MAAM,CAAC4E,QAAQC,UAAU,GAAG7E,SAAwB;IACpD,MAAM,CAAC8E,QAAQC,UAAU,GAAG/E,SAAyB,EAAE;IACvD,MAAM,CAACgF,OAAOC,SAAS,GAAGjF,SAAS;IACnC,MAAM,CAACkF,MAAMC,QAAQ,GAAGnF,SAA8B;IACtD,MAAM,CAACoF,MAAMC,QAAQ,GAAGrF,SAAS;IACjC,MAAM,CAACgB,KAAKsE,OAAO,GAAGtF,SAA8B;IACpD,MAAM,CAACqD,QAAQkC,UAAU,GAAGvF,SAA0B;IACtD,MAAM,CAACwF,SAASC,WAAW,GAAGzF,SAAwB;IACtD,MAAM0F,WAAW3F,OAA+B;IAEhDF,UAAU,IAAM;gBAAM6F;oBAAAA,oBAAAA,SAASlB,OAAO,qBAAhBkB,kBAAkBC,KAAK;WAAI,EAAE;IAEnD9F,UAAU;QACR,IAAI,CAACmE,SAAS,CAACD,UAAU,CAACU,KAAK;QAC/B,IAAImB,SAAS;QACbjB,WAAW;QACX,KAAK,AAAC,CAAA;YACJ,IAAI;gBACF,MAAMkB,WAAW,MAAMpH,gBACrB8F,QAAQC,OAAO,EACf,CAAC,mBAAmB,EAAEsB,mBAAmB9B,OAAO,OAAO,EAAEzD,kBAAkB;gBAE7E,IAAI,CAACqF,QAAQ;gBACb,gEAAgE;gBAChE,mEAAmE;gBACnE,IAAIC,SAAS3E,MAAM,KAAK,OAAO2E,SAAS3E,MAAM,KAAK,KAAK;oBACtDyD,WAAW;oBACX;gBACF;gBACA,MAAMoB,UAAU,MAAMF,SAASG,IAAI,GAAGC,KAAK,CAAC,IAAM;gBAClD,IAAI,CAACL,QAAQ;gBACb,IAAI,CAACC,SAASK,EAAE,EAAE;;oBAChB,MAAMC,SAAS/H,qBAAqByH,SAAS3E,MAAM,EAAE6E;oBACrDlB,UACEsB,SACIhI,oBAAoBgI,UACpBhE,eAAO4D,2BAAAA,QAASK,KAAK,mBAAI;gBAEjC,OAAO;;oBACL,MAAMC,gBAAQN,2BAAAA,QAASM,IAAI,oBAAI,EAAE;oBACjCtB,UACEsB,KACGC,MAAM,CAAC,CAAC/E,QAAUA,MAAMgF,IAAI,KAAK,WAAWhF,MAAMwC,MAAM,KAAKA,QAC7DuC,MAAM,CAAC,CAAC/E,QAAUJ,WAAWI,WAAW,MACxCiF,KAAK,CAAC,GAAGhG;gBAEhB;gBACAmE,WAAW;YACb,EAAE,eAAM;gBACN,IAAI,CAACiB,QAAQ;gBACbf,UAAU;gBACVF,WAAW;YACb;QACF,CAAA;QACA,OAAO;YACLiB,SAAS;QACX;IACF,GAAG;QAAC5B;QAAOD;QAAQU;KAAI;IAEvB,6DAA6D,GAC7D,MAAMgC,QAAQ7G,YAAY,OAAO8G,OAAeC;YAC9CjB;SAAAA,oBAAAA,SAASlB,OAAO,qBAAhBkB,kBAAkBC,KAAK;QACvB,MAAMiB,aAAa,IAAIC;QACvBnB,SAASlB,OAAO,GAAGoC;QACnB,IAAIE,QAAQ;QACZ,MAAOA,SAAS,CAACF,WAAWG,MAAM,CAACC,OAAO,CAAE;YAC1CF,QAAQ;YACR,IAAI;gBACF,MAAMjB,WAAW,MAAMpH,gBACrB8F,QAAQC,OAAO,EACf,CAAC,aAAa,EAAEsB,mBAAmBY,OAAO,cAAc,EAAEZ,mBAAmBa,MAAM,EACnF;oBAAEI,QAAQH,WAAWG,MAAM;gBAAC;gBAE9B,IAAI,CAAClB,SAASK,EAAE,IAAI,CAACL,SAASoB,IAAI,EAAE;gBACpC,MAAM3G,gBAAgBuF,SAASoB,IAAI,EAAE,CAACC;oBACpC,IAAIA,KAAK,CAAC,OAAO,KAAK,SAAS5B,OAAO4B,KAAK,CAAC,MAAM;yBAC7C,IAAIA,KAAK,CAAC,OAAO,KAAK,aAAaJ,QAAQ;gBAClD;YACF,EAAE,eAAM;gBACN;YACF;QACF;IACF,GAAG,EAAE;IAEL,MAAMK,UAAUvH,YAAY;QAC1B,IAAI,CAACoE,SAAS,CAACgB,MAAMoC,IAAI,IAAI;QAC7B/B,QAAQ;QACRR,UAAU;QACVY,WAAW;QACX,IAAI;YACF,MAAMI,WAAW,MAAMpH,gBAAgB8F,QAAQC,OAAO,EAAE,gBAAgB;gBACtE6C,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CL,MAAMM,KAAKC,SAAS,CAAC;oBACnBxD;oBACAD;oBACAwC,MAAM;oBACNvB,OAAOA,MAAMoC,IAAI;oBACjBK,QAAQ;wBAAEvC;oBAAK;gBACjB;YACF;YACA,MAAMa,UAAU,MAAMF,SAASG,IAAI,GAAGC,KAAK,CAAC,IAAM;YAClD,IAAI,CAACJ,SAASK,EAAE,EAAE;;gBAChB,MAAMC,SAAS/H,qBAAqByH,SAAS3E,MAAM,EAAE6E;gBACrDlB,UACEsB,SACIhI,oBAAoBgI,UACpBhE,eAAO4D,2BAAAA,QAASK,KAAK,mBAAI;gBAE/B;YACF;YACA,MAAMsB,OAAO3B,2BAAAA,QAAS/E,GAAG;YACzB,IAAI,CAAC0G,MAAM;YACXpC,OAAOoC;YACP,IAAI,CAAC3G,WAAW2G,OAAO,MAAMjB,MAAMiB,KAAKC,EAAE,EAAE3D;QAC9C,EAAE,eAAM;YACNa,UAAU;QACZ,SAAU;YACRQ,QAAQ;QACV;IACF,GAAG;QAACrB;QAAOD;QAAQiB;QAAOE;QAAMuB;KAAM;IAEtC,MAAMhF,WAAW3B,QAAQ,IAAMqB,WAAWH,MAAM;QAACA;KAAI;IACrD,MAAM4G,QAAQ9H,QACZ,IAAO2B,WAAWtB,qBAAqB8D,OAAOxC,YAAY,MAC1D;QAACwC;QAAOxC;KAAS;IAEnB,MAAMoG,QAAQ/H,QACZ,IAAO2B,WAAWrB,oBAAoB6D,OAAOxC,UAAUiC,MAAM,GAAG,IAAI,OACpE;QAACO;QAAOxC;KAAS;IAGnB,MAAMqG,QAAQlI,YAAY;QACxB,IAAI,CAACoB,OAAO,CAAC4G,OAAO;QACpBxD,aAAawD,MAAM3D,KAAK,EAAEjD,IAAI2G,EAAE;QAChClC,WAAWzE,IAAI2G,EAAE;IACnB,GAAG;QAAC3G;QAAK4G;QAAOxD;KAAa;IAE7B,IAAIM,YAAY,SAAS,OAAO;IAEhC,MAAMqD,UAAUC,QAAQhH,OAAO,CAACD,WAAWC;IAE3C,qBACI,KAAC1C;QACC2J,cAAc;QACdC,cAAc;QACdC,QAAO;QACPC,MAAM/J,eAAe,YAAY;YAC/BgK,SACE;QACJ;kBAEA,cAAA,MAACpJ;YAAMqD,SAAS;;8BACd,MAAC5C;oBACC4I,SAAS;oBACTC,MAAK;oBACLxG,OAAOmD;oBACPsD,UAAU,CAACC,GAAG1G,QAAsCA,SAASoD,QAAQpD;oBACrE2G,cAAW;;sCAEX,KAACjJ;4BAAasC,OAAM;sCAAU;;sCAC9B,KAACtC;4BAAasC,OAAM;sCAAU;;;;8BAEhC,KAACvC;oBACCmJ,SAAS;oBACTC,SAAS;oBACTC,SAAS;oBACThH,OAAM;oBACNiH,aAAY;oBACZC,YAAW;oBACXhH,OAAOiD;oBACPwD,UAAU,CAACtB,QAAUjC,SAASiC,MAAM8B,MAAM,CAACjH,KAAK,CAACyE,KAAK,CAAC,GAAG/F;oBAC1DwI,UAAU7D;;8BAEZ,MAACnG;oBAAMoD,WAAU;oBAAMC,SAAS;oBAAGC,IAAI;wBAAEC,YAAY;oBAAS;;sCAC5D,KAAC3D;4BAAOmD,SAAQ;4BAAYiH,UAAU7D,QAAQ,CAACJ,MAAMoC,IAAI;4BAAI8B,SAAS,IAAM,KAAK/B;sCAC9E;;wBAEFY,wBAAU,KAAChJ;4BAAiBwJ,MAAM;4BAAIG,cAAW;6BAA6B;;;gBAEhF9D,uBAAS,KAACjG;oBAAMwK,UAAS;8BAAWvE;qBAAkB;gBAEtD5D,OAAOA,IAAIoF,KAAK,IAAI,CAAC3E,yBACpB,KAAC9C;oBAAMwK,UAAUnI,IAAIE,MAAM,KAAK,WAAW,UAAU;8BAAYF,IAAIoF,KAAK;qBACxE;gBAEH,CAACpF,OAAO8D,OAAOpB,MAAM,iBACpB,MAACzE;oBAAMqD,SAAS;;sCACd,KAAC3C;4BAAWqC,SAAQ;sCAAa;;wBAChC8C,OAAOsE,GAAG,CAAC,CAAC7H,sBACX,MAACtC;gCAAqBoD,WAAU;gCAAMC,SAAS;gCAAGC,IAAI;oCAAEC,YAAY;gCAAS;;kDAC3E,KAAC7C;wCAAWqC,SAAQ;wCAAQO,IAAI;4CAAE8G,UAAU;4CAAGC,UAAU;wCAAE;wCAAGC,MAAM;kDACjEhI,MAAMyD,KAAK;;kDAEd,KAACnG;wCAAO0J,MAAK;wCAAQW,SAAS,IAAM5D,OAAO/D;kDACxC;;;+BALOA,MAAMoG,EAAE;;qBAUtB;gBAEHlG,YAAYmG,sBACX,MAAC3I;oBAAMqD,SAAS;oBAAGoG,cAAW;;wBAC3BjH,SAAS+H,OAAO,iBAAG,KAAC7J;4BAAWqC,SAAQ;sCAASP,SAAS+H,OAAO;6BAAiB;sCAClF,KAAC7J;4BAAWqC,SAAQ;4BAAQC,OAAM;sCAC/BvB,WAAW,CAACe,SAASgI,MAAM,CAAC;;wBAE9B5B,sBACC,KAAClJ;4BAAMwK,UAAS;sCACb;6BAED;wBAEH1H,SAASiI,OAAO,CAAChG,MAAM,IAAIjC,SAASkI,UAAU,CAACjG,MAAM,IAAIjC,SAASmI,eAAe,iBAChF,KAAChL;sCACC,cAAA,MAACL;gCAAYgK,MAAK;gCAAQG,cAAW;;kDACnC,KAACrJ;kDACC,cAAA,MAACC;;8DACC,KAACF;8DAAW;;8DACZ,KAACA;8DAAW;;8DACZ,KAACA;8DAAW;;;;;kDAGhB,MAACD;;4CACEsC,SAASiI,OAAO,CAACN,GAAG,CAAC,CAACzH;oDACeA;qEAApC,MAACrC;;sEACC,KAACF;sEACC,cAAA,MAACH;gEAAMoD,WAAU;gEAAMC,SAAS;gEAAGC,IAAI;oEAAEC,YAAY;gEAAS;;kFAC5D,KAAC7C;wEAAWqC,SAAQ;kFAASN,YAAYC;;oEACxCzB,kBAAkByB,OAAOC,OAAO,KAAKD,OAAO0B,MAAM,iBACjD,KAACvE;wEAAKyJ,MAAK;wEAAQvG,SAAQ;wEAAWH,OAAOF,OAAO0B,MAAM;yEACxD;;;;sEAGR,KAACjE;sEACC,cAAA,KAAC0C;gEAAOC,OAAOJ,OAAOkI,MAAM;;;sEAE9B,KAACzK;sEACC,cAAA,KAAC0C;gEAAOC,OAAOJ,OAAOI,KAAK;;;;mDAbhB,GAAGJ,OAAOC,OAAO,CAAC,CAAC,GAAED,iBAAAA,OAAO0B,MAAM,YAAb1B,iBAAiB,IAAI;;4CAiB1DF,SAASmI,eAAe,iBACvB,MAACtK;;kEACC,KAACF;kEAAWZ,mBAAmB,cAAcqD,KAAK;;kEAClD,KAACzC;kEAAW;;kEACZ,KAACA;kEAAW;;;iDAEZ;4CACHqC,SAASkI,UAAU,CAACP,GAAG,CAAC,CAACU,qBACxB,MAACxK;;sEACC,MAACF;;8EACC,KAACO;oEAAWqC,SAAQ;8EACjB;wEACC8H,KAAKC,SAAS;wEACdD,KAAKd,MAAM,KAAK,iBAAiB,YAAYc,KAAKE,IAAI;wEACtDF,KAAKG,QAAQ;qEACd,CAACtG,IAAI,CAAC;;gEAERmG,KAAKI,KAAK,iBAAG,KAACpL;oEAAKyJ,MAAK;oEAAQvG,SAAQ;oEAAWH,OAAOiI,KAAKI,KAAK;qEAAO;;;sEAE9E,KAAC9K;sEAAW;;sEACZ,KAACA;sEACC,cAAA,KAAC0C;gEAAOC,OAAO,OAAO+H,KAAK/H,KAAK,KAAK,YAAYI,OAAO2H,KAAK/H,KAAK,IAAI+H,KAAK/H,KAAK;;;;mDAbrE,GAAG+H,KAAKC,SAAS,CAAC,CAAC,EAAED,KAAKd,MAAM,CAAC,CAAC,EAAEc,KAAKE,IAAI,CAAC,CAAC,EAAEF,KAAKI,KAAK,CAAC,CAAC,EAAEJ,KAAKG,QAAQ,EAAE;;;;;2CAqBrG,KAACtK;4BAAWqC,SAAQ;sCAAS;;wBAG9B;+BAAIP,SAAS0I,WAAW;+BAAKvC,MAAMuC,WAAW;yBAAC,CAACzG,MAAM,iBACrD,MAACzE;4BAAMqD,SAAS;;8CACd,KAAC3C;oCAAWqC,SAAQ;8CAAa;;gCAChC;uCAAIP,SAAS0I,WAAW;uCAAKvC,MAAMuC,WAAW;iCAAC,CAACf,GAAG,CAAC,CAAChG,2BACpD,KAACzD;wCAECqC,SAAQ;kDAEPmB,eAAeC;uCAHX,GAAGA,WAAWxB,OAAO,CAAC,CAAC,EAAEwB,WAAWC,MAAM,CAAC,CAAC,EAAED,WAAWG,EAAE,EAAE;;6BAOtE;wBAEH;+BAAI9B,SAAS2I,KAAK;+BAAKxC,MAAMwC,KAAK;+BAAK3I,SAAS4I,OAAO;yBAAC,CAAC3G,MAAM,iBAC9D,MAACzE;4BAAMqD,SAAS;;8CACd,KAAC3C;oCAAWqC,SAAQ;8CAAa;;gCAChC;uCAAIP,SAAS2I,KAAK;uCAAKxC,MAAMwC,KAAK;iCAAC,CAAChB,GAAG,CAAC,CAACkB,qBACxC,KAAC3K;wCAAgCqC,SAAQ;wCAAQC,OAAM;kDACpDqI;uCADc,CAAC,KAAK,EAAEA,MAAM;gCAIhC7I,SAAS4I,OAAO,CAACjB,GAAG,CAAC,CAACkB,qBACrB,KAAC3K;wCAAmCqC,SAAQ;wCAAQC,OAAM;kDACvD,CAAC,WAAW,EAAEqI,MAAM;uCADN,CAAC,QAAQ,EAAEA,MAAM;;6BAKpC;sCAEJ,MAAC/K;4BACCwC,OAAOsB;4BACPmF,UAAU,CAACC,GAAG1G,QAA2BwD,UAAUxD;4BACnD2G,cAAW;;8CAEX,KAACxJ;oCAAI2C,OAAM;oCAAQE,OAAM;;8CACzB,KAAC7C;oCAAI2C,OAAM;oCAAOE,OAAM;;;;sCAE1B,MAAC/C;4BAAKuL,SAAS;4BAACjI,SAAS;;8CACvB,MAACtD;oCAAKuJ,MAAM;wCAAEiC,IAAI;wCAAIC,IAAI;oCAAE;;sDAC1B,KAAC9K;4CAAWqC,SAAQ;4CAAY0I,YAAY;sDACzC;;sDAEH,KAAC9L;4CAAI+L,gBAAa;sDAChB,cAAA,KAACxG;gDAAaF,KAAK,EAAEA,gBAAAA,QAAS,CAAC;gDAAGZ,QAAQA;;;;;8CAG9C,MAACrE;oCAAKuJ,MAAM;wCAAEiC,IAAI;wCAAIC,IAAI;oCAAE;;sDAC1B,KAAC9K;4CAAWqC,SAAQ;4CAAY0I,YAAY;sDACzC;;sDAEH,KAAC9L;4CAAI+L,gBAAa;sDAChB,cAAA,KAACxG;gDAAaF,OAAO2D,MAAM3D,KAAK;gDAAEZ,QAAQA;;;;;;;wBAK/CmC,aAAYxE,uBAAAA,IAAK2G,EAAE,kBAClB,KAAChJ;4BAAMwK,UAAS;sCACb;6BAED;sCACJ,MAAClK;4BAAMoD,WAAU;4BAAMC,SAAS;;8CAC9B,KAACzD;oCAAOmD,SAAQ;oCAAYkH,SAASpB;oCAAOmB,UAAUzD,aAAYxE,uBAAAA,IAAK2G,EAAE;8CACtE;;8CAEH,KAAC9I;oCACCqK,SAAS;wCACP5D,OAAO;wCACPG,WAAW;oCACb;8CAEC;;;;;qBAIL;;;;AAId;AACA5B,oBAAoB+G,WAAW,GAAG;AAElC,eAAe/G,oBAAmB"}
@@ -84,7 +84,8 @@ const ZERO_USAGE = {
84
84
  }
85
85
  const SOURCE_WORDS = {
86
86
  installed: "an installed marketplace theme; a change is stored as the site's own edits on top of it",
87
- custom: "the site's own theme",
87
+ preset: "a built-in theme; a change is stored as the site's own edits on top of it",
88
+ custom: "one of the site's own saved themes; a change is stored as edits on top of it",
88
89
  default: "the platform's default theme; a change stores only the values it sets"
89
90
  };
90
91
  /** One value as the inventory shows it: the site's own, or the default it inherits. */ const shown = (own, inherited)=>own === null || own === undefined ? `${inherited != null ? inherited : 'unset'} (default)` : `${own} (set)`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-theme-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n hostThemeSource,\n resolveSiteTheme,\n type HostThemeSource,\n type ThemeHostDocument,\n} from '@aglyn/aglyn/app-utils/marketplace-theme'\nimport type { HostTheme, HostThemeScheme } from '@aglyn/shared-data-types'\nimport {\n INHERITED_BORDER_RADIUS,\n INHERITED_FONT_FAMILY,\n INHERITED_SPACING,\n INHERITED_TOOLBAR_HEIGHTS,\n inheritedThemeColor,\n} from '@aglyn/shared-ui-theme/util/theme-editor-defaults'\nimport {\n readDarkScheme,\n readFontFamily,\n readThemeColor,\n readToolbarHeight,\n SYSTEM_FONT_VALUE,\n THEME_COLOR_FIELDS,\n THEME_EDITOR_MEDIA_QUERIES,\n} from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n buildAiThemeProposal,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport {\n AI_THEME_TOOL_NAME,\n aiThemeTool,\n parseAiThemeToolInput,\n type AiThemeToolParse,\n} from '../tools/ai-theme-tool'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_THEME_STEP_BUDGET } from './ai-job-theme-budget'\nimport {\n AI_THEME_BRAND_SOURCE_WORDS,\n gatherAiThemeBrandInputs,\n type AiThemeBrandColor,\n} from './ai-theme-brand-inputs'\n\n/**\n * The `theme` step (AGL-2938): a brief and a site's current theme in, a theme\n * proposal out — every control the theme editor exposes, and nothing else.\n *\n * The step writes nothing to the site. A site's theme is fields on its host\n * document, and writing those is the save the Theme section's editor\n * performs; the step's output is the proposal, which a person puts in the\n * editor, previews against the current theme, and saves or discards.\n *\n * The request is shaped for the cache: the rules and the tool are static and\n * sit in the cached prefix, and everything about this site — its theme, its\n * brand colors, the brief — rides in the user turn. The model answers through\n * one strict tool derived from the editor's own catalog, and the answer is\n * held to that catalog and to the doctrine (a targeted diff, dark values,\n * contrast) before it becomes a proposal.\n */\n\n/**\n * The routing table's answer for `job.theme`: the model the theme step runs on\n * when the machine hands it no `modelFor`.\n */\nexport function aiJobThemeModel(): string {\n return aiModelForStep('job.theme')\n}\n\n/**\n * The output budget, from the routing table. The largest answer the tool\n * accepts — every color in both schemes, the full run of component leaves,\n * the longest summary — is about 2,500 tokens of JSON\n * (`ai-job-theme-step.spec.ts` measures it), and the rest is room for the\n * model to think before it answers.\n */\nexport const AI_JOB_THEME_MAX_TOKENS = AI_ROUTING_TABLE['job.theme'].maxTokens\n\n/**\n * The theme's answer ceiling on the model a job runs (AGL-3035): the most\n * whose worst case — the answer, its re-ask and the brand reads — fits the\n * least time the step registers (`ai-job-theme-budget.ts`), and never more\n * than the routing table's.\n */\nexport function aiJobThemeMaxTokens(model: string): number {\n return AI_JOB_THEME_STEP_BUDGET.maxTokens(model)\n}\n\n/** How many of a site's own component override leaves the prompt lists. */\nexport const AI_JOB_THEME_INVENTORY_LEAVES = 40\n\n/** The customer-safe sentences a failed theme step leaves on the job. */\nexport const AI_JOB_THEME_NO_SITE_COPY =\n 'This theme job names a site this workspace does not have.'\nexport const AI_JOB_THEME_NO_ANSWER_COPY =\n 'The AI could not produce a theme proposal from this brief. Try describing the change differently.'\n\n/**\n * The theme step's own instructions, byte-identical on every request so they\n * cache. The acceptable-use rules are not in them: they belong to the block\n * every generator shares, which the generation call puts ahead of these.\n */\nexport const AI_JOB_THEME_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You change the theme of a website built with a site builder. You are given the ' +\n \"site's current theme — every control its theme editor has, with the value the site \" +\n 'set or the default it inherits — any brand colors that belong to the site, and a ' +\n 'brief from the person who owns it.\\n\\n' +\n `Answer by calling ${AI_THEME_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- In modify mode, name only the controls the brief is about. Every control you leave ' +\n 'out keeps its current value. \"Warmer\" is about color, not about fonts or corners.\\n' +\n '- In create mode, design a complete theme: the primary, secondary and tertiary ' +\n 'accents, the page background and paper, the text colors, and their dark values.\\n' +\n \"- Colors are hex values on the theme's own tokens. A theme is where the building rules \" +\n 'send every color, so this is the one answer that writes a color as a value. A component ' +\n 'style never carries a literal color; color belongs to the palette.\\n' +\n '- Give every color you change a dark value beside its light value unless the dark ' +\n 'scheme is off. A dark value keeps the hue and reads on a dark background.\\n' +\n '- Keep text readable: body text needs 4.5:1 against the background and the paper, and ' +\n 'the primary color 3:1 against the background.\\n' +\n '- A hex color written in the brief is used exactly as written. When the brief asks to ' +\n 'match the brand, build on the brand colors you are given.\\n' +\n '- Fonts come from the list the tool offers, and numbers stay inside the ranges it ' +\n 'states.\\n' +\n '- Write the summary in the language of the brief.',\n cacheBreakpoint: true,\n },\n]\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** `create` when the job asked for a whole design; `modify` otherwise. */\nexport function aiJobThemeMode(inputs: AiJob['inputs'] | undefined): AiThemeProposalMode {\n return inputs?.['mode'] === 'create' ? 'create' : 'modify'\n}\n\nconst SOURCE_WORDS: Record<HostThemeSource, string> = {\n installed:\n \"an installed marketplace theme; a change is stored as the site's own edits on top of it\",\n custom: \"the site's own theme\",\n default: \"the platform's default theme; a change stores only the values it sets\",\n}\n\n/** One value as the inventory shows it: the site's own, or the default it inherits. */\nconst shown = (own: string | number | null | undefined, inherited: string | number | undefined) =>\n own === null || own === undefined ? `${inherited ?? 'unset'} (default)` : `${own} (set)`\n\n/** The site's own component overrides, one bounded line a leaf. */\nfunction overrideLeaves(theme: HostTheme | undefined): string[] {\n const leaves: string[] = []\n const media = Object.fromEntries(\n Object.entries(THEME_EDITOR_MEDIA_QUERIES).map(([name, query]) => [query, name]),\n )\n const text = (value: unknown) => JSON.stringify(value)?.slice(0, 60) ?? ''\n for (const [component, override] of Object.entries(theme?.components ?? {})) {\n for (const [prop, value] of Object.entries(override?.defaultProps ?? {})) {\n leaves.push(`${component} default ${prop} = ${text(value)}`)\n }\n // `sx` leaves are marked so the model writes them back to `sx`: the same\n // number is a different length in each (AGL-3403).\n for (const [target, slots] of [\n ['', override?.styleOverrides],\n [' sx', override?.sx],\n ] as const) {\n for (const [slot, styles] of Object.entries(slots ?? {})) {\n if (!styles || typeof styles !== 'object') continue\n for (const [property, value] of Object.entries(styles as Record<string, unknown>)) {\n if (value && typeof value === 'object') {\n for (const [nested, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n leaves.push(\n `${component}${target} ${slot} ${nested} = ${text(nestedValue)} (${media[property] ?? property})`,\n )\n }\n } else {\n leaves.push(`${component}${target} ${slot} ${property} = ${text(value)}`)\n }\n }\n }\n }\n if (override?.variants?.length) {\n leaves.push(`${component} ${override.variants.length} props-matched variant style(s)`)\n }\n }\n return leaves\n}\n\n/**\n * The site's theme as the model reads it: compact, one control a line, each\n * value marked as the site's own or the default it inherits, so \"make the\n * background warmer\" starts from the color the visitor actually sees.\n */\nexport function aiJobThemeInventory(theme: HostTheme | undefined, source: HostThemeSource): string {\n const lines = [`Source: ${SOURCE_WORDS[source]}.`, `Dark scheme: ${readDarkScheme(theme)}.`]\n lines.push('Colors, light | dark:')\n for (const { token } of THEME_COLOR_FIELDS) {\n const value = (scheme: HostThemeScheme) =>\n shown(readThemeColor(theme, scheme, token), inheritedThemeColor(scheme, token))\n lines.push(`- ${token}: ${value('light')} | ${value('dark')}`)\n }\n const font = readFontFamily(theme)\n lines.push(`Font family: ${shown(font === SYSTEM_FONT_VALUE ? null : font, INHERITED_FONT_FAMILY)}`)\n lines.push(`Border radius: ${shown(theme?.shape?.borderRadius, INHERITED_BORDER_RADIUS)}`)\n lines.push(`Spacing unit: ${shown(theme?.spacing, INHERITED_SPACING)}`)\n lines.push(\n `Nav height: mobile ${shown(theme && readToolbarHeight(theme, 'xs'), INHERITED_TOOLBAR_HEIGHTS.xs)}, ` +\n `desktop ${shown(theme && readToolbarHeight(theme, 'sm'), INHERITED_TOOLBAR_HEIGHTS.sm)}`,\n )\n const leaves = overrideLeaves(theme)\n if (!leaves.length) {\n lines.push('Component overrides: none.')\n } else {\n lines.push('Component overrides:')\n for (const leaf of leaves.slice(0, AI_JOB_THEME_INVENTORY_LEAVES)) lines.push(`- ${leaf}`)\n if (leaves.length > AI_JOB_THEME_INVENTORY_LEAVES) {\n lines.push(`- and ${leaves.length - AI_JOB_THEME_INVENTORY_LEAVES} more`)\n }\n }\n return lines.join('\\n')\n}\n\n/** The user turn: the mode, the site's theme, its brand colors, and the brief. */\nexport function aiJobThemePrompt(input: {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n}): string {\n const sections = [\n input.mode === 'create'\n ? 'Mode: create. Design a complete theme over every control.'\n : 'Mode: modify. Change only what the brief is about.',\n `Current theme:\\n${aiJobThemeInventory(input.theme, input.source)}`,\n ]\n if (input.brand.length) {\n sections.push(\n `Brand colors:\\n${input.brand\n .map((color) => `- ${color.hex} (${AI_THEME_BRAND_SOURCE_WORDS[color.source]})`)\n .join('\\n')}`,\n )\n }\n sections.push(`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`)\n return sections.join('\\n\\n')\n}\n\n/**\n * The theme tool's answer held to the editor — the check the doctrine's loop\n * runs on each answer (AGL-2935), which also holds rule 13 on it. Usable when\n * anything in it survives; refused, naming what was refused, when nothing\n * does. A call that proposes no change and refuses nothing is a real answer —\n * the theme already does what the brief asks.\n */\nexport const aiJobThemeCheck: AiGenerationCheck<AiThemeToolParse> = (answer) => {\n const parse = parseAiThemeToolInput(answer)\n const nothingUsable =\n !parse.changes.length && !parse.components.length && !parse.resetComponents\n if (!nothingUsable || !parse.dropped.length) return { value: parse, violations: [] }\n return {\n value: null,\n violations: parse.dropped.map((message) => ({ rule: null, code: 'theme-control', message })),\n }\n}\n\n/** What a theme generation is asked from, once the site and its brand have been read. */\nexport interface AiJobThemeRequest {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n model: string\n signal?: AbortSignal\n}\n\n/**\n * The theme step's generation as the doctrine's loop runs it: its cached\n * block, which carries the acceptable-use rules, then this step's\n * instructions; the site's theme in the user turn rather than a site\n * inventory; the strict tool, the ceiling and the check. The eval harness\n * records a theme answer through this same call.\n */\nexport function aiJobThemeGeneration(\n request: AiJobThemeRequest,\n): AiCustomGenerationInput<AiThemeToolParse> {\n return {\n step: 'job.theme',\n model: request.model,\n instructions: AI_JOB_THEME_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobThemePrompt(request) }],\n tool: aiThemeTool(),\n maxTokens: aiJobThemeMaxTokens(request.model),\n ...(AI_ROUTING_TABLE['job.theme'].thinking ? { thinking: AI_ROUTING_TABLE['job.theme'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.theme'].effort ? { effort: AI_ROUTING_TABLE['job.theme'].effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: aiJobThemeCheck,\n }\n}\n\n/** The site a theme job changes, read and checked against the job's org. */\nasync function loadThemeSite(\n firestore: FirebaseFirestore.Firestore,\n job: AiJob,\n): Promise<\n | { host: ThemeHostDocument & Record<string, unknown>; theme: HostTheme | undefined; source: HostThemeSource }\n | { failure: string }\n> {\n if (!job.hostId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists\n ? (snapshot.data() as ThemeHostDocument & Record<string, unknown>)\n : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site it named belongs to that org. A site of some\n // other org is refused here, before anything about it reaches a prompt.\n if (!host || host['orgId'] !== job.orgId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n return { host, theme: resolveSiteTheme(host), source: hostThemeSource(host) }\n}\n\n/** How many edits a proposal makes, for its label. */\nfunction proposalSize(proposal: AiThemeProposal): number {\n return proposal.changes.length + proposal.components.length + (proposal.resetComponents ? 1 : 0)\n}\n\nexport const runAiJobThemeStep: AiJobStepRunner = async ({\n job,\n signal,\n firestore,\n org,\n modelFor,\n}) => {\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the theme step reads the site it changes and was given no Firestore')\n }\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Only a runner called\n // without a resolver asks the routing table directly.\n const model = modelFor?.('job.theme') ?? aiJobThemeModel()\n const site = await loadThemeSite(firestore, job)\n if ('failure' in site) {\n return {\n outputs: [],\n usage: ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n failure: site.failure,\n }\n }\n const mode = aiJobThemeMode(job.inputs)\n const brand = await gatherAiThemeBrandInputs({\n firestore,\n org: org ?? null,\n hostId: job.hostId as string,\n host: site.host,\n brief: job.brief,\n signal,\n })\n // A second refused answer ends in this step's own sentence, below.\n const generation = await runValidatedGeneration(\n 'theme',\n aiJobThemeGeneration({\n brief: job.brief,\n mode,\n theme: site.theme,\n source: site.source,\n brand: brand.colors,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason' | 'effort'> = {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n ...(generation.effort ? { effort: generation.effort } : {}),\n }\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') {\n return { outputs: [], ...spent, failure: AI_JOB_THEME_NO_ANSWER_COPY }\n }\n\n const answer = generation.value\n const proposal = buildAiThemeProposal({\n base: site.theme,\n source: site.source,\n mode,\n brief: job.brief,\n summary: answer.summary,\n changes: answer.changes,\n components: answer.components,\n resetComponents: answer.resetComponents,\n dropped: answer.dropped,\n notes: [...brand.notes, ...answer.notes],\n })\n const size = proposalSize(proposal)\n const output: AiJobOutput = {\n resource: 'theme',\n // A proposal has no document of its own; the id names what it is within\n // the job, as a text output's `draft` does.\n id: 'proposal',\n hostId: job.hostId ?? null,\n hostSubdomain: typeof site.host['subdomain'] === 'string' ? site.host['subdomain'] : null,\n label: `Theme proposal · ${size} ${size === 1 ? 'change' : 'changes'}`,\n // Through JSON so no `undefined` reaches the document write.\n proposal: JSON.parse(JSON.stringify(proposal)) as Record<string, unknown>,\n }\n return { outputs: [output], ...spent }\n}\n"],"names":["hostThemeSource","resolveSiteTheme","INHERITED_BORDER_RADIUS","INHERITED_FONT_FAMILY","INHERITED_SPACING","INHERITED_TOOLBAR_HEIGHTS","inheritedThemeColor","readDarkScheme","readFontFamily","readThemeColor","readToolbarHeight","SYSTEM_FONT_VALUE","THEME_COLOR_FIELDS","THEME_EDITOR_MEDIA_QUERIES","buildAiThemeProposal","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_THEME_TOOL_NAME","aiThemeTool","parseAiThemeToolInput","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_THEME_STEP_BUDGET","AI_THEME_BRAND_SOURCE_WORDS","gatherAiThemeBrandInputs","aiJobThemeModel","AI_JOB_THEME_MAX_TOKENS","maxTokens","aiJobThemeMaxTokens","model","AI_JOB_THEME_INVENTORY_LEAVES","AI_JOB_THEME_NO_SITE_COPY","AI_JOB_THEME_NO_ANSWER_COPY","AI_JOB_THEME_INSTRUCTIONS","text","cacheBreakpoint","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiJobThemeMode","inputs","SOURCE_WORDS","installed","custom","default","shown","own","inherited","undefined","overrideLeaves","theme","leaves","media","Object","fromEntries","entries","map","name","query","value","JSON","stringify","slice","component","override","components","prop","defaultProps","push","target","slots","styleOverrides","sx","slot","styles","property","nested","nestedValue","variants","length","aiJobThemeInventory","source","lines","token","scheme","font","shape","borderRadius","spacing","xs","sm","leaf","join","aiJobThemePrompt","input","sections","mode","brand","color","hex","brief","aiJobThemeCheck","answer","parse","nothingUsable","changes","resetComponents","dropped","violations","message","rule","code","aiJobThemeGeneration","request","step","instructions","messages","role","content","tool","thinking","effort","signal","check","loadThemeSite","firestore","job","hostId","failure","snapshot","collection","doc","get","host","exists","data","orgId","proposalSize","proposal","runAiJobThemeStep","org","modelFor","Error","site","outputs","usage","estCostUsd","stopReason","generation","colors","spent","status","refused","base","summary","notes","size","output","resource","id","hostSubdomain","label"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-theme-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n hostThemeSource,\n resolveSiteTheme,\n type HostThemeSource,\n type ThemeHostDocument,\n} from '@aglyn/aglyn/app-utils/marketplace-theme'\nimport type { HostTheme, HostThemeScheme } from '@aglyn/shared-data-types'\nimport {\n INHERITED_BORDER_RADIUS,\n INHERITED_FONT_FAMILY,\n INHERITED_SPACING,\n INHERITED_TOOLBAR_HEIGHTS,\n inheritedThemeColor,\n} from '@aglyn/shared-ui-theme/util/theme-editor-defaults'\nimport {\n readDarkScheme,\n readFontFamily,\n readThemeColor,\n readToolbarHeight,\n SYSTEM_FONT_VALUE,\n THEME_COLOR_FIELDS,\n THEME_EDITOR_MEDIA_QUERIES,\n} from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n buildAiThemeProposal,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport {\n AI_THEME_TOOL_NAME,\n aiThemeTool,\n parseAiThemeToolInput,\n type AiThemeToolParse,\n} from '../tools/ai-theme-tool'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_THEME_STEP_BUDGET } from './ai-job-theme-budget'\nimport {\n AI_THEME_BRAND_SOURCE_WORDS,\n gatherAiThemeBrandInputs,\n type AiThemeBrandColor,\n} from './ai-theme-brand-inputs'\n\n/**\n * The `theme` step (AGL-2938): a brief and a site's current theme in, a theme\n * proposal out — every control the theme editor exposes, and nothing else.\n *\n * The step writes nothing to the site. A site's theme is fields on its host\n * document, and writing those is the save the Theme section's editor\n * performs; the step's output is the proposal, which a person puts in the\n * editor, previews against the current theme, and saves or discards.\n *\n * The request is shaped for the cache: the rules and the tool are static and\n * sit in the cached prefix, and everything about this site — its theme, its\n * brand colors, the brief — rides in the user turn. The model answers through\n * one strict tool derived from the editor's own catalog, and the answer is\n * held to that catalog and to the doctrine (a targeted diff, dark values,\n * contrast) before it becomes a proposal.\n */\n\n/**\n * The routing table's answer for `job.theme`: the model the theme step runs on\n * when the machine hands it no `modelFor`.\n */\nexport function aiJobThemeModel(): string {\n return aiModelForStep('job.theme')\n}\n\n/**\n * The output budget, from the routing table. The largest answer the tool\n * accepts — every color in both schemes, the full run of component leaves,\n * the longest summary — is about 2,500 tokens of JSON\n * (`ai-job-theme-step.spec.ts` measures it), and the rest is room for the\n * model to think before it answers.\n */\nexport const AI_JOB_THEME_MAX_TOKENS = AI_ROUTING_TABLE['job.theme'].maxTokens\n\n/**\n * The theme's answer ceiling on the model a job runs (AGL-3035): the most\n * whose worst case — the answer, its re-ask and the brand reads — fits the\n * least time the step registers (`ai-job-theme-budget.ts`), and never more\n * than the routing table's.\n */\nexport function aiJobThemeMaxTokens(model: string): number {\n return AI_JOB_THEME_STEP_BUDGET.maxTokens(model)\n}\n\n/** How many of a site's own component override leaves the prompt lists. */\nexport const AI_JOB_THEME_INVENTORY_LEAVES = 40\n\n/** The customer-safe sentences a failed theme step leaves on the job. */\nexport const AI_JOB_THEME_NO_SITE_COPY =\n 'This theme job names a site this workspace does not have.'\nexport const AI_JOB_THEME_NO_ANSWER_COPY =\n 'The AI could not produce a theme proposal from this brief. Try describing the change differently.'\n\n/**\n * The theme step's own instructions, byte-identical on every request so they\n * cache. The acceptable-use rules are not in them: they belong to the block\n * every generator shares, which the generation call puts ahead of these.\n */\nexport const AI_JOB_THEME_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You change the theme of a website built with a site builder. You are given the ' +\n \"site's current theme — every control its theme editor has, with the value the site \" +\n 'set or the default it inherits — any brand colors that belong to the site, and a ' +\n 'brief from the person who owns it.\\n\\n' +\n `Answer by calling ${AI_THEME_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- In modify mode, name only the controls the brief is about. Every control you leave ' +\n 'out keeps its current value. \"Warmer\" is about color, not about fonts or corners.\\n' +\n '- In create mode, design a complete theme: the primary, secondary and tertiary ' +\n 'accents, the page background and paper, the text colors, and their dark values.\\n' +\n \"- Colors are hex values on the theme's own tokens. A theme is where the building rules \" +\n 'send every color, so this is the one answer that writes a color as a value. A component ' +\n 'style never carries a literal color; color belongs to the palette.\\n' +\n '- Give every color you change a dark value beside its light value unless the dark ' +\n 'scheme is off. A dark value keeps the hue and reads on a dark background.\\n' +\n '- Keep text readable: body text needs 4.5:1 against the background and the paper, and ' +\n 'the primary color 3:1 against the background.\\n' +\n '- A hex color written in the brief is used exactly as written. When the brief asks to ' +\n 'match the brand, build on the brand colors you are given.\\n' +\n '- Fonts come from the list the tool offers, and numbers stay inside the ranges it ' +\n 'states.\\n' +\n '- Write the summary in the language of the brief.',\n cacheBreakpoint: true,\n },\n]\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** `create` when the job asked for a whole design; `modify` otherwise. */\nexport function aiJobThemeMode(inputs: AiJob['inputs'] | undefined): AiThemeProposalMode {\n return inputs?.['mode'] === 'create' ? 'create' : 'modify'\n}\n\nconst SOURCE_WORDS: Record<HostThemeSource, string> = {\n installed:\n \"an installed marketplace theme; a change is stored as the site's own edits on top of it\",\n preset: \"a built-in theme; a change is stored as the site's own edits on top of it\",\n custom: \"one of the site's own saved themes; a change is stored as edits on top of it\",\n default: \"the platform's default theme; a change stores only the values it sets\",\n}\n\n/** One value as the inventory shows it: the site's own, or the default it inherits. */\nconst shown = (own: string | number | null | undefined, inherited: string | number | undefined) =>\n own === null || own === undefined ? `${inherited ?? 'unset'} (default)` : `${own} (set)`\n\n/** The site's own component overrides, one bounded line a leaf. */\nfunction overrideLeaves(theme: HostTheme | undefined): string[] {\n const leaves: string[] = []\n const media = Object.fromEntries(\n Object.entries(THEME_EDITOR_MEDIA_QUERIES).map(([name, query]) => [query, name]),\n )\n const text = (value: unknown) => JSON.stringify(value)?.slice(0, 60) ?? ''\n for (const [component, override] of Object.entries(theme?.components ?? {})) {\n for (const [prop, value] of Object.entries(override?.defaultProps ?? {})) {\n leaves.push(`${component} default ${prop} = ${text(value)}`)\n }\n // `sx` leaves are marked so the model writes them back to `sx`: the same\n // number is a different length in each (AGL-3403).\n for (const [target, slots] of [\n ['', override?.styleOverrides],\n [' sx', override?.sx],\n ] as const) {\n for (const [slot, styles] of Object.entries(slots ?? {})) {\n if (!styles || typeof styles !== 'object') continue\n for (const [property, value] of Object.entries(styles as Record<string, unknown>)) {\n if (value && typeof value === 'object') {\n for (const [nested, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n leaves.push(\n `${component}${target} ${slot} ${nested} = ${text(nestedValue)} (${media[property] ?? property})`,\n )\n }\n } else {\n leaves.push(`${component}${target} ${slot} ${property} = ${text(value)}`)\n }\n }\n }\n }\n if (override?.variants?.length) {\n leaves.push(`${component} ${override.variants.length} props-matched variant style(s)`)\n }\n }\n return leaves\n}\n\n/**\n * The site's theme as the model reads it: compact, one control a line, each\n * value marked as the site's own or the default it inherits, so \"make the\n * background warmer\" starts from the color the visitor actually sees.\n */\nexport function aiJobThemeInventory(theme: HostTheme | undefined, source: HostThemeSource): string {\n const lines = [`Source: ${SOURCE_WORDS[source]}.`, `Dark scheme: ${readDarkScheme(theme)}.`]\n lines.push('Colors, light | dark:')\n for (const { token } of THEME_COLOR_FIELDS) {\n const value = (scheme: HostThemeScheme) =>\n shown(readThemeColor(theme, scheme, token), inheritedThemeColor(scheme, token))\n lines.push(`- ${token}: ${value('light')} | ${value('dark')}`)\n }\n const font = readFontFamily(theme)\n lines.push(`Font family: ${shown(font === SYSTEM_FONT_VALUE ? null : font, INHERITED_FONT_FAMILY)}`)\n lines.push(`Border radius: ${shown(theme?.shape?.borderRadius, INHERITED_BORDER_RADIUS)}`)\n lines.push(`Spacing unit: ${shown(theme?.spacing, INHERITED_SPACING)}`)\n lines.push(\n `Nav height: mobile ${shown(theme && readToolbarHeight(theme, 'xs'), INHERITED_TOOLBAR_HEIGHTS.xs)}, ` +\n `desktop ${shown(theme && readToolbarHeight(theme, 'sm'), INHERITED_TOOLBAR_HEIGHTS.sm)}`,\n )\n const leaves = overrideLeaves(theme)\n if (!leaves.length) {\n lines.push('Component overrides: none.')\n } else {\n lines.push('Component overrides:')\n for (const leaf of leaves.slice(0, AI_JOB_THEME_INVENTORY_LEAVES)) lines.push(`- ${leaf}`)\n if (leaves.length > AI_JOB_THEME_INVENTORY_LEAVES) {\n lines.push(`- and ${leaves.length - AI_JOB_THEME_INVENTORY_LEAVES} more`)\n }\n }\n return lines.join('\\n')\n}\n\n/** The user turn: the mode, the site's theme, its brand colors, and the brief. */\nexport function aiJobThemePrompt(input: {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n}): string {\n const sections = [\n input.mode === 'create'\n ? 'Mode: create. Design a complete theme over every control.'\n : 'Mode: modify. Change only what the brief is about.',\n `Current theme:\\n${aiJobThemeInventory(input.theme, input.source)}`,\n ]\n if (input.brand.length) {\n sections.push(\n `Brand colors:\\n${input.brand\n .map((color) => `- ${color.hex} (${AI_THEME_BRAND_SOURCE_WORDS[color.source]})`)\n .join('\\n')}`,\n )\n }\n sections.push(`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`)\n return sections.join('\\n\\n')\n}\n\n/**\n * The theme tool's answer held to the editor — the check the doctrine's loop\n * runs on each answer (AGL-2935), which also holds rule 13 on it. Usable when\n * anything in it survives; refused, naming what was refused, when nothing\n * does. A call that proposes no change and refuses nothing is a real answer —\n * the theme already does what the brief asks.\n */\nexport const aiJobThemeCheck: AiGenerationCheck<AiThemeToolParse> = (answer) => {\n const parse = parseAiThemeToolInput(answer)\n const nothingUsable =\n !parse.changes.length && !parse.components.length && !parse.resetComponents\n if (!nothingUsable || !parse.dropped.length) return { value: parse, violations: [] }\n return {\n value: null,\n violations: parse.dropped.map((message) => ({ rule: null, code: 'theme-control', message })),\n }\n}\n\n/** What a theme generation is asked from, once the site and its brand have been read. */\nexport interface AiJobThemeRequest {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n model: string\n signal?: AbortSignal\n}\n\n/**\n * The theme step's generation as the doctrine's loop runs it: its cached\n * block, which carries the acceptable-use rules, then this step's\n * instructions; the site's theme in the user turn rather than a site\n * inventory; the strict tool, the ceiling and the check. The eval harness\n * records a theme answer through this same call.\n */\nexport function aiJobThemeGeneration(\n request: AiJobThemeRequest,\n): AiCustomGenerationInput<AiThemeToolParse> {\n return {\n step: 'job.theme',\n model: request.model,\n instructions: AI_JOB_THEME_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobThemePrompt(request) }],\n tool: aiThemeTool(),\n maxTokens: aiJobThemeMaxTokens(request.model),\n ...(AI_ROUTING_TABLE['job.theme'].thinking ? { thinking: AI_ROUTING_TABLE['job.theme'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.theme'].effort ? { effort: AI_ROUTING_TABLE['job.theme'].effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: aiJobThemeCheck,\n }\n}\n\n/** The site a theme job changes, read and checked against the job's org. */\nasync function loadThemeSite(\n firestore: FirebaseFirestore.Firestore,\n job: AiJob,\n): Promise<\n | { host: ThemeHostDocument & Record<string, unknown>; theme: HostTheme | undefined; source: HostThemeSource }\n | { failure: string }\n> {\n if (!job.hostId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists\n ? (snapshot.data() as ThemeHostDocument & Record<string, unknown>)\n : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site it named belongs to that org. A site of some\n // other org is refused here, before anything about it reaches a prompt.\n if (!host || host['orgId'] !== job.orgId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n return { host, theme: resolveSiteTheme(host), source: hostThemeSource(host) }\n}\n\n/** How many edits a proposal makes, for its label. */\nfunction proposalSize(proposal: AiThemeProposal): number {\n return proposal.changes.length + proposal.components.length + (proposal.resetComponents ? 1 : 0)\n}\n\nexport const runAiJobThemeStep: AiJobStepRunner = async ({\n job,\n signal,\n firestore,\n org,\n modelFor,\n}) => {\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the theme step reads the site it changes and was given no Firestore')\n }\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Only a runner called\n // without a resolver asks the routing table directly.\n const model = modelFor?.('job.theme') ?? aiJobThemeModel()\n const site = await loadThemeSite(firestore, job)\n if ('failure' in site) {\n return {\n outputs: [],\n usage: ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n failure: site.failure,\n }\n }\n const mode = aiJobThemeMode(job.inputs)\n const brand = await gatherAiThemeBrandInputs({\n firestore,\n org: org ?? null,\n hostId: job.hostId as string,\n host: site.host,\n brief: job.brief,\n signal,\n })\n // A second refused answer ends in this step's own sentence, below.\n const generation = await runValidatedGeneration(\n 'theme',\n aiJobThemeGeneration({\n brief: job.brief,\n mode,\n theme: site.theme,\n source: site.source,\n brand: brand.colors,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason' | 'effort'> = {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n ...(generation.effort ? { effort: generation.effort } : {}),\n }\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') {\n return { outputs: [], ...spent, failure: AI_JOB_THEME_NO_ANSWER_COPY }\n }\n\n const answer = generation.value\n const proposal = buildAiThemeProposal({\n base: site.theme,\n source: site.source,\n mode,\n brief: job.brief,\n summary: answer.summary,\n changes: answer.changes,\n components: answer.components,\n resetComponents: answer.resetComponents,\n dropped: answer.dropped,\n notes: [...brand.notes, ...answer.notes],\n })\n const size = proposalSize(proposal)\n const output: AiJobOutput = {\n resource: 'theme',\n // A proposal has no document of its own; the id names what it is within\n // the job, as a text output's `draft` does.\n id: 'proposal',\n hostId: job.hostId ?? null,\n hostSubdomain: typeof site.host['subdomain'] === 'string' ? site.host['subdomain'] : null,\n label: `Theme proposal · ${size} ${size === 1 ? 'change' : 'changes'}`,\n // Through JSON so no `undefined` reaches the document write.\n proposal: JSON.parse(JSON.stringify(proposal)) as Record<string, unknown>,\n }\n return { outputs: [output], ...spent }\n}\n"],"names":["hostThemeSource","resolveSiteTheme","INHERITED_BORDER_RADIUS","INHERITED_FONT_FAMILY","INHERITED_SPACING","INHERITED_TOOLBAR_HEIGHTS","inheritedThemeColor","readDarkScheme","readFontFamily","readThemeColor","readToolbarHeight","SYSTEM_FONT_VALUE","THEME_COLOR_FIELDS","THEME_EDITOR_MEDIA_QUERIES","buildAiThemeProposal","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_THEME_TOOL_NAME","aiThemeTool","parseAiThemeToolInput","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_THEME_STEP_BUDGET","AI_THEME_BRAND_SOURCE_WORDS","gatherAiThemeBrandInputs","aiJobThemeModel","AI_JOB_THEME_MAX_TOKENS","maxTokens","aiJobThemeMaxTokens","model","AI_JOB_THEME_INVENTORY_LEAVES","AI_JOB_THEME_NO_SITE_COPY","AI_JOB_THEME_NO_ANSWER_COPY","AI_JOB_THEME_INSTRUCTIONS","text","cacheBreakpoint","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiJobThemeMode","inputs","SOURCE_WORDS","installed","preset","custom","default","shown","own","inherited","undefined","overrideLeaves","theme","leaves","media","Object","fromEntries","entries","map","name","query","value","JSON","stringify","slice","component","override","components","prop","defaultProps","push","target","slots","styleOverrides","sx","slot","styles","property","nested","nestedValue","variants","length","aiJobThemeInventory","source","lines","token","scheme","font","shape","borderRadius","spacing","xs","sm","leaf","join","aiJobThemePrompt","input","sections","mode","brand","color","hex","brief","aiJobThemeCheck","answer","parse","nothingUsable","changes","resetComponents","dropped","violations","message","rule","code","aiJobThemeGeneration","request","step","instructions","messages","role","content","tool","thinking","effort","signal","check","loadThemeSite","firestore","job","hostId","failure","snapshot","collection","doc","get","host","exists","data","orgId","proposalSize","proposal","runAiJobThemeStep","org","modelFor","Error","site","outputs","usage","estCostUsd","stopReason","generation","colors","spent","status","refused","base","summary","notes","size","output","resource","id","hostSubdomain","label"],"mappings":"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"}