@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.238

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
  3. package/src/lib/components/ai-credits-prompt.component.js +84 -34
  4. package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
  5. package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
  6. package/src/lib/components/ai-job-cancel.component.js +156 -0
  7. package/src/lib/components/ai-job-cancel.component.js.map +1 -0
  8. package/src/lib/components/ai-job-plan.component.js +12 -1
  9. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  10. package/src/lib/components/ai-jobs-list-page.component.js +141 -72
  11. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  12. package/src/lib/components/ai-site-build-page.component.js +22 -2
  13. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  14. package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
  15. package/src/lib/components/ai-usage-strip.component.js +40 -9
  16. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  17. package/src/lib/components/assist-jobs-drawer.component.js +12 -16
  18. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  19. package/src/lib/components/assist-panel.component.js +87 -51
  20. package/src/lib/components/assist-panel.component.js.map +1 -1
  21. package/src/lib/jobs/ai-job-build-step.js +4 -1
  22. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  23. package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
  24. package/src/lib/jobs/ai-job-cancel.js +102 -0
  25. package/src/lib/jobs/ai-job-cancel.js.map +1 -0
  26. package/src/lib/jobs/ai-job-component-step.js +4 -1
  27. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  28. package/src/lib/jobs/ai-job-drafts.js +2 -0
  29. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  30. package/src/lib/jobs/ai-job-form-step.js +4 -1
  31. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  32. package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
  33. package/src/lib/jobs/ai-job-layout-language.js +13 -1
  34. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  35. package/src/lib/jobs/ai-job-layout-step.js +6 -1
  36. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-page-language.js +8 -0
  38. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  39. package/src/lib/jobs/ai-job-page-step.js +12 -1
  40. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  41. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -1
  42. package/src/lib/jobs/ai-job-plan-step.js +49 -9
  43. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  44. package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
  45. package/src/lib/jobs/ai-job-site-content.js +66 -7
  46. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  47. package/src/lib/jobs/ai-job-site-step.js +4 -1
  48. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  49. package/src/lib/jobs/ai-job-template-step.js +4 -1
  50. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  51. package/src/lib/jobs/ai-jobs.d.ts +37 -12
  52. package/src/lib/jobs/ai-jobs.js +184 -28
  53. package/src/lib/jobs/ai-jobs.js.map +1 -1
  54. package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
  55. package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
  56. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  57. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
  58. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
  59. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
  60. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
  61. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
  62. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
  63. package/src/lib/layout-language/ai-layout-compiler.d.ts +6 -0
  64. package/src/lib/layout-language/ai-layout-compiler.js +304 -8
  65. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  66. package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
  67. package/src/lib/layout-language/ai-layout-design.js +5 -3
  68. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  69. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  70. package/src/lib/layout-language/ai-layout-frame.js +58 -3
  71. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  72. package/src/lib/layout-language/ai-layout-listings.d.ts +13 -1
  73. package/src/lib/layout-language/ai-layout-listings.js +61 -46
  74. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  75. package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
  76. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  77. package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
  78. package/src/lib/layout-language/ai-layout-storefront.js +223 -0
  79. package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
  80. package/src/lib/model/ai-build-job.d.ts +9 -1
  81. package/src/lib/model/ai-build-job.js +19 -5
  82. package/src/lib/model/ai-build-job.js.map +1 -1
  83. package/src/lib/model/ai-build-progress.d.ts +2 -2
  84. package/src/lib/model/ai-build-progress.js +6 -4
  85. package/src/lib/model/ai-build-progress.js.map +1 -1
  86. package/src/lib/model/ai-credit-estimate.d.ts +69 -2
  87. package/src/lib/model/ai-credit-estimate.js +91 -3
  88. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  89. package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
  90. package/src/lib/model/ai-job-cancel-copy.js +53 -0
  91. package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
  92. package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
  93. package/src/lib/model/ai-job-failure-copy.js +8 -3
  94. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  95. package/src/lib/model/ai-jobs.types.d.ts +20 -0
  96. package/src/lib/model/ai-jobs.types.js.map +1 -1
  97. package/src/lib/model/ai-site-build-progress.js +7 -4
  98. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  99. package/src/lib/model/ai-site-job.d.ts +19 -2
  100. package/src/lib/model/ai-site-job.js +29 -8
  101. package/src/lib/model/ai-site-job.js.map +1 -1
  102. package/src/lib/model/ai-site-kinds.d.ts +4 -1
  103. package/src/lib/model/ai-site-kinds.js +34 -6
  104. package/src/lib/model/ai-site-kinds.js.map +1 -1
  105. package/src/lib/runtime/ai-doctrine-validators.d.ts +37 -1
  106. package/src/lib/runtime/ai-doctrine-validators.js +151 -6
  107. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  108. package/src/lib/runtime/ai-doctrine.js +4 -3
  109. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  110. package/src/lib/runtime/ai-palette.generated.js +53 -7
  111. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  112. package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
  113. package/src/lib/server/ai-jobs-cancel.js +27 -9
  114. package/src/lib/server/ai-jobs-cancel.js.map +1 -1
  115. package/src/lib/server/ai-jobs-gate.d.ts +12 -2
  116. package/src/lib/server/ai-jobs-gate.js +20 -9
  117. package/src/lib/server/ai-jobs-gate.js.map +1 -1
  118. package/src/lib/usage/ai-usage-meter.d.ts +2 -1
  119. package/src/lib/usage/ai-usage-meter.js +17 -1
  120. package/src/lib/usage/ai-usage-meter.js.map +1 -1
  121. package/src/lib/usage/ai-usage-wire.d.ts +6 -0
  122. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  123. package/src/lib/usage/assist-usage.d.ts +7 -0
  124. package/src/lib/usage/assist-usage.js +14 -4
  125. package/src/lib/usage/assist-usage.js.map +1 -1
  126. package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
  127. package/src/lib/usage/free-ai-credits-left.js +4 -2
  128. package/src/lib/usage/free-ai-credits-left.js.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-ai",
3
- "version": "1.0.0-beta.237",
3
+ "version": "1.0.0-beta.238",
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.237",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.237",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.237",
31
- "@aglyn/shared-data-types": "1.0.0-beta.237",
32
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.237",
33
- "@aglyn/shared-ui-jsx": "1.0.0-beta.237",
34
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.237",
35
- "@aglyn/shared-ui-theme": "1.0.0-beta.237",
36
- "@aglyn/shared-util-email": "1.0.0-beta.237",
37
- "@aglyn/shared-util-errors": "1.0.0-beta.237",
38
- "@aglyn/shared-util-http": "1.0.0-beta.237",
39
- "@aglyn/shared-util-timestamp": "1.0.0-beta.237",
40
- "@aglyn/shared-util-tools": "1.0.0-beta.237",
41
- "@aglyn/tenant-data-admin": "1.0.0-beta.237",
42
- "@aglyn/tenant-feature-instance": "1.0.0-beta.237",
43
- "@aglyn/tenant-runtime": "1.0.0-beta.237",
28
+ "@aglyn/aglyn": "1.0.0-beta.238",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.238",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.238",
31
+ "@aglyn/shared-data-types": "1.0.0-beta.238",
32
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.238",
33
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.238",
34
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.238",
35
+ "@aglyn/shared-ui-theme": "1.0.0-beta.238",
36
+ "@aglyn/shared-util-email": "1.0.0-beta.238",
37
+ "@aglyn/shared-util-errors": "1.0.0-beta.238",
38
+ "@aglyn/shared-util-http": "1.0.0-beta.238",
39
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.238",
40
+ "@aglyn/shared-util-tools": "1.0.0-beta.238",
41
+ "@aglyn/tenant-data-admin": "1.0.0-beta.238",
42
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.238",
43
+ "@aglyn/tenant-runtime": "1.0.0-beta.238",
44
44
  "@swc/helpers": "0.5.23"
45
45
  },
46
46
  "peerDependencies": {
@@ -17,10 +17,17 @@
17
17
  import { type AiCreditsPrompt } from '../model/ai-credit-estimate';
18
18
  /**
19
19
  * What a Free job past what is left asks before it starts (AGL-3722), on the
20
- * Assist build plan card and in the guided start alike: the plain sentence —
21
- * about what it costs, what is left, that it builds what it can and pauses —
22
- * and its three ways on. Only "Build what fits" (or the smaller first build)
23
- * starts anything; nothing is started by showing this.
20
+ * Assist build plan card, in the guided start, on a build's page and in the
21
+ * jobs drawer alike: the plain sentence — about what it costs, what is left,
22
+ * that it builds what it can and pauses — and its ways on. Only "Build what
23
+ * fits" (or the smaller first build) starts anything; nothing is started by
24
+ * showing this.
25
+ *
26
+ * The ways on are one set of option buttons (Zach, 2026-10-10): the same
27
+ * shape and height, full width so a long label never wraps beside another,
28
+ * sentence case, a short label with its figure under it, and the recommended
29
+ * one drawn as the primary button. Theme tokens only, so it reads the same in
30
+ * the Assist panel's narrow column and in light or dark.
24
31
  */
25
32
  export interface AiCreditsPromptProps {
26
33
  prompt: AiCreditsPrompt;
@@ -14,50 +14,100 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
17
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
- import { Alert, Button, Stack } from "@mui/material";
19
- import { AI_CREDITS_BUILD_WHAT_FITS, aiCreditsPromptText, aiCreditsSmallerText } from "../model/ai-credit-estimate.js";
19
+ import { Box, Button, Stack, Typography } from "@mui/material";
20
+ import { useId } from "react";
21
+ import { aiCreditsChoices, aiCreditsPromptText } from "../model/ai-credit-estimate.js";
20
22
  import { aiCreditsBillingHref } from "./ai-job-links.js";
21
- export function AiCreditsPromptNotice({ prompt, noun, orgSlug, busy = false, onBuildWhatFits, onSmaller }) {
22
- return /*#__PURE__*/ _jsxs(Alert, {
23
- severity: "warning",
23
+ function AiCreditsChoiceButton({ choice, busy, href, onClick }) {
24
+ const detailId = `${useId()}-detail`;
25
+ return /*#__PURE__*/ _jsxs(Button, _extends({
26
+ fullWidth: true,
27
+ size: "medium",
28
+ variant: choice.recommended ? 'contained' : 'outlined',
29
+ color: "primary",
30
+ disabled: busy && choice.key !== 'upgrade',
31
+ "aria-label": choice.label,
32
+ "aria-describedby": detailId,
33
+ "data-choice": choice.key
34
+ }, href ? {
35
+ href
36
+ } : {
37
+ onClick
38
+ }, {
24
39
  sx: {
25
- mt: 1
40
+ minHeight: 56,
41
+ px: 1.5,
42
+ py: 1,
43
+ display: 'flex',
44
+ flexDirection: 'column',
45
+ alignItems: 'flex-start',
46
+ justifyContent: 'center',
47
+ textAlign: 'left',
48
+ textTransform: 'none',
49
+ lineHeight: 1.3
26
50
  },
51
+ children: [
52
+ /*#__PURE__*/ _jsx(Typography, {
53
+ component: "span",
54
+ variant: "body2",
55
+ sx: {
56
+ fontWeight: 600,
57
+ color: 'inherit'
58
+ },
59
+ children: choice.label
60
+ }),
61
+ /*#__PURE__*/ _jsx(Typography, {
62
+ id: detailId,
63
+ component: "span",
64
+ variant: "caption",
65
+ sx: {
66
+ color: 'inherit',
67
+ opacity: 0.8
68
+ },
69
+ children: choice.detail
70
+ })
71
+ ]
72
+ }));
73
+ }
74
+ export function AiCreditsPromptNotice({ prompt, noun, orgSlug, busy = false, onBuildWhatFits, onSmaller }) {
75
+ const choices = aiCreditsChoices(prompt, {
76
+ smaller: Boolean(onSmaller),
77
+ upgrade: Boolean(orgSlug)
78
+ });
79
+ return /*#__PURE__*/ _jsxs(Box, {
27
80
  role: "alert",
28
81
  "aria-label": "More than your credits left",
82
+ sx: {
83
+ mt: 1,
84
+ p: 1.5,
85
+ border: 1,
86
+ borderColor: 'warning.main',
87
+ borderRadius: 1,
88
+ bgcolor: 'background.paper'
89
+ },
29
90
  children: [
30
- aiCreditsPromptText(prompt, noun),
31
- /*#__PURE__*/ _jsxs(Stack, {
32
- direction: "row",
91
+ /*#__PURE__*/ _jsx(Typography, {
92
+ variant: "body2",
93
+ sx: {
94
+ color: 'text.primary'
95
+ },
96
+ children: aiCreditsPromptText(prompt, noun)
97
+ }),
98
+ /*#__PURE__*/ _jsx(Stack, {
33
99
  spacing: 1,
34
- useFlexGap: true,
35
100
  sx: {
36
- mt: 1,
37
- flexWrap: 'wrap'
101
+ mt: 1.5
38
102
  },
39
- children: [
40
- /*#__PURE__*/ _jsx(Button, {
41
- size: "small",
42
- variant: "contained",
43
- disabled: busy,
44
- onClick: onBuildWhatFits,
45
- children: AI_CREDITS_BUILD_WHAT_FITS
46
- }),
47
- prompt.smaller && onSmaller ? /*#__PURE__*/ _jsx(Button, {
48
- size: "small",
49
- variant: "outlined",
50
- disabled: busy,
51
- onClick: onSmaller,
52
- children: aiCreditsSmallerText(prompt.smaller)
53
- }) : null,
54
- orgSlug ? /*#__PURE__*/ _jsx(Button, {
55
- size: "small",
56
- color: "inherit",
57
- href: aiCreditsBillingHref(orgSlug),
58
- children: 'Upgrade'
59
- }) : null
60
- ]
103
+ children: choices.map((choice)=>/*#__PURE__*/ _jsx(AiCreditsChoiceButton, _extends({
104
+ choice: choice,
105
+ busy: busy
106
+ }, choice.key === 'upgrade' && orgSlug ? {
107
+ href: aiCreditsBillingHref(orgSlug)
108
+ } : {
109
+ onClick: choice.key === 'smaller' ? onSmaller : onBuildWhatFits
110
+ }), choice.key))
61
111
  })
62
112
  ]
63
113
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-credits-prompt.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { Alert, Button, Stack } from '@mui/material'\nimport {\n AI_CREDITS_BUILD_WHAT_FITS,\n aiCreditsPromptText,\n aiCreditsSmallerText,\n type AiCreditsPrompt,\n} from '../model/ai-credit-estimate'\nimport { aiCreditsBillingHref } from './ai-job-links'\n\n/**\n * What a Free job past what is left asks before it starts (AGL-3722), on the\n * Assist build plan card and in the guided start alike: the plain sentence —\n * about what it costs, what is left, that it builds what it can and pauses —\n * and its three ways on. Only \"Build what fits\" (or the smaller first build)\n * starts anything; nothing is started by showing this.\n */\nexport interface AiCreditsPromptProps {\n prompt: AiCreditsPrompt\n noun: 'build' | 'site'\n /** The workspace's path slug, for Upgrade; Upgrade is left out without it. */\n orgSlug?: string | null\n busy?: boolean\n /** \"Build what fits\": start it, to pause where the credits run out. */\n onBuildWhatFits: () => void\n /** The smaller first build; offered only when the prompt has one and this is given. */\n onSmaller?: () => void\n}\n\nexport function AiCreditsPromptNotice({\n prompt,\n noun,\n orgSlug,\n busy = false,\n onBuildWhatFits,\n onSmaller,\n}: AiCreditsPromptProps): JSX.Element {\n return (\n <Alert severity=\"warning\" sx={{ mt: 1 }} role=\"alert\" aria-label=\"More than your credits left\">\n {aiCreditsPromptText(prompt, noun)}\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ mt: 1, flexWrap: 'wrap' }}>\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={onBuildWhatFits}>\n {AI_CREDITS_BUILD_WHAT_FITS}\n </Button>\n {prompt.smaller && onSmaller ? (\n <Button size=\"small\" variant=\"outlined\" disabled={busy} onClick={onSmaller}>\n {aiCreditsSmallerText(prompt.smaller)}\n </Button>\n ) : null}\n {orgSlug ? (\n <Button size=\"small\" color=\"inherit\" href={aiCreditsBillingHref(orgSlug)}>\n {'Upgrade'}\n </Button>\n ) : null}\n </Stack>\n </Alert>\n )\n}\n\nexport default AiCreditsPromptNotice\n"],"names":["Alert","Button","Stack","AI_CREDITS_BUILD_WHAT_FITS","aiCreditsPromptText","aiCreditsSmallerText","aiCreditsBillingHref","AiCreditsPromptNotice","prompt","noun","orgSlug","busy","onBuildWhatFits","onSmaller","severity","sx","mt","role","aria-label","direction","spacing","useFlexGap","flexWrap","size","variant","disabled","onClick","smaller","color","href"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,KAAK,EAAEC,MAAM,EAAEC,KAAK,QAAQ,gBAAe;AACpD,SACEC,0BAA0B,EAC1BC,mBAAmB,EACnBC,oBAAoB,QAEf,iCAA6B;AACpC,SAASC,oBAAoB,QAAQ,oBAAgB;AAqBrD,OAAO,SAASC,sBAAsB,EACpCC,MAAM,EACNC,IAAI,EACJC,OAAO,EACPC,OAAO,KAAK,EACZC,eAAe,EACfC,SAAS,EACY;IACrB,qBACE,MAACb;QAAMc,UAAS;QAAUC,IAAI;YAAEC,IAAI;QAAE;QAAGC,MAAK;QAAQC,cAAW;;YAC9Dd,oBAAoBI,QAAQC;0BAC7B,MAACP;gBAAMiB,WAAU;gBAAMC,SAAS;gBAAGC,UAAU;gBAACN,IAAI;oBAAEC,IAAI;oBAAGM,UAAU;gBAAO;;kCAC1E,KAACrB;wBAAOsB,MAAK;wBAAQC,SAAQ;wBAAYC,UAAUd;wBAAMe,SAASd;kCAC/DT;;oBAEFK,OAAOmB,OAAO,IAAId,0BACjB,KAACZ;wBAAOsB,MAAK;wBAAQC,SAAQ;wBAAWC,UAAUd;wBAAMe,SAASb;kCAC9DR,qBAAqBG,OAAOmB,OAAO;yBAEpC;oBACHjB,wBACC,KAACT;wBAAOsB,MAAK;wBAAQK,OAAM;wBAAUC,MAAMvB,qBAAqBI;kCAC7D;yBAED;;;;;AAIZ;AAEA,eAAeH,sBAAqB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-credits-prompt.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { Box, Button, Stack, Typography } from '@mui/material'\nimport { useId } from 'react'\nimport {\n aiCreditsChoices,\n aiCreditsPromptText,\n type AiCreditsChoice,\n type AiCreditsPrompt,\n} from '../model/ai-credit-estimate'\nimport { aiCreditsBillingHref } from './ai-job-links'\n\n/**\n * What a Free job past what is left asks before it starts (AGL-3722), on the\n * Assist build plan card, in the guided start, on a build's page and in the\n * jobs drawer alike: the plain sentence — about what it costs, what is left,\n * that it builds what it can and pauses — and its ways on. Only \"Build what\n * fits\" (or the smaller first build) starts anything; nothing is started by\n * showing this.\n *\n * The ways on are one set of option buttons (Zach, 2026-10-10): the same\n * shape and height, full width so a long label never wraps beside another,\n * sentence case, a short label with its figure under it, and the recommended\n * one drawn as the primary button. Theme tokens only, so it reads the same in\n * the Assist panel's narrow column and in light or dark.\n */\nexport interface AiCreditsPromptProps {\n prompt: AiCreditsPrompt\n noun: 'build' | 'site'\n /** The workspace's path slug, for Upgrade; Upgrade is left out without it. */\n orgSlug?: string | null\n busy?: boolean\n /** \"Build what fits\": start it, to pause where the credits run out. */\n onBuildWhatFits: () => void\n /** The smaller first build; offered only when the prompt has one and this is given. */\n onSmaller?: () => void\n}\n\nfunction AiCreditsChoiceButton({\n choice,\n busy,\n href,\n onClick,\n}: {\n choice: AiCreditsChoice\n busy: boolean\n href?: string\n onClick?: () => void\n}): JSX.Element {\n const detailId = `${useId()}-detail`\n return (\n <Button\n fullWidth\n size=\"medium\"\n variant={choice.recommended ? 'contained' : 'outlined'}\n color=\"primary\"\n disabled={busy && choice.key !== 'upgrade'}\n aria-label={choice.label}\n aria-describedby={detailId}\n data-choice={choice.key}\n {...(href ? { href } : { onClick })}\n sx={{\n minHeight: 56,\n px: 1.5,\n py: 1,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-start',\n justifyContent: 'center',\n textAlign: 'left',\n textTransform: 'none',\n lineHeight: 1.3,\n }}\n >\n <Typography component=\"span\" variant=\"body2\" sx={{ fontWeight: 600, color: 'inherit' }}>\n {choice.label}\n </Typography>\n <Typography id={detailId} component=\"span\" variant=\"caption\" sx={{ color: 'inherit', opacity: 0.8 }}>\n {choice.detail}\n </Typography>\n </Button>\n )\n}\n\nexport function AiCreditsPromptNotice({\n prompt,\n noun,\n orgSlug,\n busy = false,\n onBuildWhatFits,\n onSmaller,\n}: AiCreditsPromptProps): JSX.Element {\n const choices = aiCreditsChoices(prompt, { smaller: Boolean(onSmaller), upgrade: Boolean(orgSlug) })\n return (\n <Box\n role=\"alert\"\n aria-label=\"More than your credits left\"\n sx={{\n mt: 1,\n p: 1.5,\n border: 1,\n borderColor: 'warning.main',\n borderRadius: 1,\n bgcolor: 'background.paper',\n }}\n >\n <Typography variant=\"body2\" sx={{ color: 'text.primary' }}>\n {aiCreditsPromptText(prompt, noun)}\n </Typography>\n <Stack spacing={1} sx={{ mt: 1.5 }}>\n {choices.map((choice) => (\n <AiCreditsChoiceButton\n key={choice.key}\n choice={choice}\n busy={busy}\n {...(choice.key === 'upgrade' && orgSlug\n ? { href: aiCreditsBillingHref(orgSlug) }\n : { onClick: choice.key === 'smaller' ? onSmaller : onBuildWhatFits })}\n />\n ))}\n </Stack>\n </Box>\n )\n}\n\nexport default AiCreditsPromptNotice\n"],"names":["Box","Button","Stack","Typography","useId","aiCreditsChoices","aiCreditsPromptText","aiCreditsBillingHref","AiCreditsChoiceButton","choice","busy","href","onClick","detailId","fullWidth","size","variant","recommended","color","disabled","key","aria-label","label","aria-describedby","data-choice","sx","minHeight","px","py","display","flexDirection","alignItems","justifyContent","textAlign","textTransform","lineHeight","component","fontWeight","id","opacity","detail","AiCreditsPromptNotice","prompt","noun","orgSlug","onBuildWhatFits","onSmaller","choices","smaller","Boolean","upgrade","role","mt","p","border","borderColor","borderRadius","bgcolor","spacing","map"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;AAEA,SAASA,GAAG,EAAEC,MAAM,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AAC9D,SAASC,KAAK,QAAQ,QAAO;AAC7B,SACEC,gBAAgB,EAChBC,mBAAmB,QAGd,iCAA6B;AACpC,SAASC,oBAAoB,QAAQ,oBAAgB;AA4BrD,SAASC,sBAAsB,EAC7BC,MAAM,EACNC,IAAI,EACJC,IAAI,EACJC,OAAO,EAMR;IACC,MAAMC,WAAW,GAAGT,QAAQ,OAAO,CAAC;IACpC,qBACE,MAACH;QACCa,SAAS;QACTC,MAAK;QACLC,SAASP,OAAOQ,WAAW,GAAG,cAAc;QAC5CC,OAAM;QACNC,UAAUT,QAAQD,OAAOW,GAAG,KAAK;QACjCC,cAAYZ,OAAOa,KAAK;QACxBC,oBAAkBV;QAClBW,eAAaf,OAAOW,GAAG;OAClBT,OAAO;QAAEA;IAAK,IAAI;QAAEC;IAAQ;QACjCa,IAAI;YACFC,WAAW;YACXC,IAAI;YACJC,IAAI;YACJC,SAAS;YACTC,eAAe;YACfC,YAAY;YACZC,gBAAgB;YAChBC,WAAW;YACXC,eAAe;YACfC,YAAY;QACd;;0BAEA,KAAChC;gBAAWiC,WAAU;gBAAOpB,SAAQ;gBAAQS,IAAI;oBAAEY,YAAY;oBAAKnB,OAAO;gBAAU;0BAClFT,OAAOa,KAAK;;0BAEf,KAACnB;gBAAWmC,IAAIzB;gBAAUuB,WAAU;gBAAOpB,SAAQ;gBAAUS,IAAI;oBAAEP,OAAO;oBAAWqB,SAAS;gBAAI;0BAC/F9B,OAAO+B,MAAM;;;;AAItB;AAEA,OAAO,SAASC,sBAAsB,EACpCC,MAAM,EACNC,IAAI,EACJC,OAAO,EACPlC,OAAO,KAAK,EACZmC,eAAe,EACfC,SAAS,EACY;IACrB,MAAMC,UAAU1C,iBAAiBqC,QAAQ;QAAEM,SAASC,QAAQH;QAAYI,SAASD,QAAQL;IAAS;IAClG,qBACE,MAAC5C;QACCmD,MAAK;QACL9B,cAAW;QACXI,IAAI;YACF2B,IAAI;YACJC,GAAG;YACHC,QAAQ;YACRC,aAAa;YACbC,cAAc;YACdC,SAAS;QACX;;0BAEA,KAACtD;gBAAWa,SAAQ;gBAAQS,IAAI;oBAAEP,OAAO;gBAAe;0BACrDZ,oBAAoBoC,QAAQC;;0BAE/B,KAACzC;gBAAMwD,SAAS;gBAAGjC,IAAI;oBAAE2B,IAAI;gBAAI;0BAC9BL,QAAQY,GAAG,CAAC,CAAClD,uBACZ,KAACD;wBAECC,QAAQA;wBACRC,MAAMA;uBACDD,OAAOW,GAAG,KAAK,aAAawB,UAC7B;wBAAEjC,MAAMJ,qBAAqBqC;oBAAS,IACtC;wBAAEhC,SAASH,OAAOW,GAAG,KAAK,YAAY0B,YAAYD;oBAAgB,IALjEpC,OAAOW,GAAG;;;;AAW3B;AAEA,eAAeqB,sBAAqB"}
@@ -0,0 +1,38 @@
1
+ import type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token';
2
+ import { type ButtonProps } from '@mui/material';
3
+ import { type ReactNode } from 'react';
4
+ import { type AiJobSummary } from '../model/ai-jobs.types';
5
+ /**
6
+ * Cancel an AI job, after saying what it does (AGL-3616). One confirm dialog
7
+ * and one request for every surface that offers Cancel — the job's page, a
8
+ * row of the site's AI jobs list, and the AI jobs list the top-bar chip
9
+ * opens — through the cancel door the Assist panel's list always used.
10
+ *
11
+ * `useAiJobCancel` owns the dialog: `ask(job)` opens it, Keep building
12
+ * closes it, and Cancel job posts the cancel and hands the job the door
13
+ * answered with to `onJob`, so the surface shows "Stopping…" or "Canceled"
14
+ * without waiting for its stream. A refusal stays in the dialog, in the
15
+ * door's own words.
16
+ */
17
+ export declare function useAiJobCancel({ user, orgId, onJob, }: {
18
+ user: MaybeTokenSource;
19
+ orgId: string | null;
20
+ onJob?: (job: AiJobSummary) => void;
21
+ }): {
22
+ ask: (job: AiJobSummary) => void;
23
+ busyJobId: string | null;
24
+ dialog: ReactNode;
25
+ };
26
+ /**
27
+ * The Cancel button: enabled while the job can still be stopped, and
28
+ * disabled with the reason as its tooltip once it is done, failed, canceled
29
+ * or already stopping.
30
+ */
31
+ export declare function AiJobCancelButton({ job, user, orgId, onJob, size, variant, }: {
32
+ job: AiJobSummary;
33
+ user: MaybeTokenSource;
34
+ orgId: string | null;
35
+ onJob?: (job: AiJobSummary) => void;
36
+ size?: ButtonProps['size'];
37
+ variant?: ButtonProps['variant'];
38
+ }): import("react").JSX.Element;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
+ import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Tooltip } from "@mui/material";
19
+ import { useCallback, useRef, useState } from "react";
20
+ import { AI_JOB_CANCEL_CONFIRM_COPY, AI_JOB_CANCEL_TITLE, aiJobCancelBlockedReason } from "../model/ai-job-cancel-copy.js";
21
+ import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
22
+ import { cancelAiJobRequest } from "./ai-job-requests.js";
23
+ /**
24
+ * Cancel an AI job, after saying what it does (AGL-3616). One confirm dialog
25
+ * and one request for every surface that offers Cancel — the job's page, a
26
+ * row of the site's AI jobs list, and the AI jobs list the top-bar chip
27
+ * opens — through the cancel door the Assist panel's list always used.
28
+ *
29
+ * `useAiJobCancel` owns the dialog: `ask(job)` opens it, Keep building
30
+ * closes it, and Cancel job posts the cancel and hands the job the door
31
+ * answered with to `onJob`, so the surface shows "Stopping…" or "Canceled"
32
+ * without waiting for its stream. A refusal stays in the dialog, in the
33
+ * door's own words.
34
+ */ export function useAiJobCancel({ user, orgId, onJob }) {
35
+ var _ref;
36
+ const [target, setTarget] = useState(null);
37
+ const [busy, setBusy] = useState(false);
38
+ const [error, setError] = useState(null);
39
+ const onJobRef = useRef(onJob);
40
+ onJobRef.current = onJob;
41
+ const ask = useCallback((job)=>{
42
+ if (aiJobCancelBlockedReason(job)) return;
43
+ setError(null);
44
+ setTarget(job);
45
+ }, []);
46
+ const close = useCallback(()=>{
47
+ if (busy) return;
48
+ setTarget(null);
49
+ setError(null);
50
+ }, [
51
+ busy
52
+ ]);
53
+ const confirm = useCallback(async ()=>{
54
+ if (!target || !orgId) return;
55
+ setBusy(true);
56
+ setError(null);
57
+ const { job, error: refused } = await cancelAiJobRequest(user, orgId, target.id);
58
+ setBusy(false);
59
+ if (job) onJobRef.current == null ? void 0 : onJobRef.current.call(onJobRef, job);
60
+ if (refused) {
61
+ setError(refused);
62
+ return;
63
+ }
64
+ setTarget(null);
65
+ }, [
66
+ target,
67
+ orgId,
68
+ user
69
+ ]);
70
+ const dialog = /*#__PURE__*/ _jsxs(Dialog, {
71
+ open: target !== null,
72
+ onClose: close,
73
+ "aria-labelledby": "ai-job-cancel-title",
74
+ maxWidth: "xs",
75
+ fullWidth: true,
76
+ children: [
77
+ /*#__PURE__*/ _jsx(DialogTitle, {
78
+ id: "ai-job-cancel-title",
79
+ children: AI_JOB_CANCEL_TITLE
80
+ }),
81
+ /*#__PURE__*/ _jsxs(DialogContent, {
82
+ children: [
83
+ /*#__PURE__*/ _jsx(DialogContentText, {
84
+ children: AI_JOB_CANCEL_CONFIRM_COPY
85
+ }),
86
+ error ? /*#__PURE__*/ _jsx(Alert, {
87
+ severity: "warning",
88
+ sx: {
89
+ mt: 2
90
+ },
91
+ children: error
92
+ }) : null
93
+ ]
94
+ }),
95
+ /*#__PURE__*/ _jsxs(DialogActions, {
96
+ children: [
97
+ /*#__PURE__*/ _jsx(Button, {
98
+ onClick: close,
99
+ disabled: busy,
100
+ children: 'Keep building'
101
+ }),
102
+ /*#__PURE__*/ _jsx(Button, {
103
+ color: "error",
104
+ variant: "contained",
105
+ onClick: ()=>void confirm(),
106
+ disabled: busy || !orgId,
107
+ children: busy ? 'Canceling…' : 'Cancel job'
108
+ })
109
+ ]
110
+ })
111
+ ]
112
+ });
113
+ return {
114
+ ask,
115
+ busyJobId: busy ? (_ref = target == null ? void 0 : target.id) != null ? _ref : null : null,
116
+ dialog
117
+ };
118
+ }
119
+ /**
120
+ * The Cancel button: enabled while the job can still be stopped, and
121
+ * disabled with the reason as its tooltip once it is done, failed, canceled
122
+ * or already stopping.
123
+ */ export function AiJobCancelButton({ job, user, orgId, onJob, size = 'medium', variant = 'outlined' }) {
124
+ const { ask, busyJobId, dialog } = useAiJobCancel({
125
+ user,
126
+ orgId,
127
+ onJob
128
+ });
129
+ const blocked = aiJobCancelBlockedReason(job);
130
+ const stopping = Boolean(job.cancelRequested) && !AI_JOB_TERMINAL_STATUSES.includes(job.status);
131
+ const button = /*#__PURE__*/ _jsx(Button, {
132
+ size: size,
133
+ variant: variant,
134
+ color: "error",
135
+ disabled: Boolean(blocked) || busyJobId === job.id || !orgId,
136
+ onClick: ()=>ask(job),
137
+ children: stopping ? 'Stopping…' : 'Cancel'
138
+ });
139
+ return /*#__PURE__*/ _jsxs(_Fragment, {
140
+ children: [
141
+ blocked ? /*#__PURE__*/ _jsx(Tooltip, {
142
+ title: blocked,
143
+ children: /*#__PURE__*/ _jsx(Box, {
144
+ component: "span",
145
+ sx: {
146
+ display: 'inline-flex'
147
+ },
148
+ children: button
149
+ })
150
+ }) : button,
151
+ dialog
152
+ ]
153
+ });
154
+ }
155
+
156
+ //# sourceMappingURL=ai-job-cancel.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-cancel.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 type { MaybeTokenSource } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Box,\n Button,\n type ButtonProps,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Tooltip,\n} from '@mui/material'\nimport { type ReactNode, useCallback, useRef, useState } from 'react'\nimport {\n AI_JOB_CANCEL_CONFIRM_COPY,\n AI_JOB_CANCEL_TITLE,\n aiJobCancelBlockedReason,\n} from '../model/ai-job-cancel-copy'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { cancelAiJobRequest } from './ai-job-requests'\n\n/**\n * Cancel an AI job, after saying what it does (AGL-3616). One confirm dialog\n * and one request for every surface that offers Cancel — the job's page, a\n * row of the site's AI jobs list, and the AI jobs list the top-bar chip\n * opens — through the cancel door the Assist panel's list always used.\n *\n * `useAiJobCancel` owns the dialog: `ask(job)` opens it, Keep building\n * closes it, and Cancel job posts the cancel and hands the job the door\n * answered with to `onJob`, so the surface shows \"Stopping…\" or \"Canceled\"\n * without waiting for its stream. A refusal stays in the dialog, in the\n * door's own words.\n */\nexport function useAiJobCancel({\n user,\n orgId,\n onJob,\n}: {\n user: MaybeTokenSource\n orgId: string | null\n onJob?: (job: AiJobSummary) => void\n}): { ask: (job: AiJobSummary) => void; busyJobId: string | null; dialog: ReactNode } {\n const [target, setTarget] = useState<AiJobSummary | null>(null)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const onJobRef = useRef(onJob)\n onJobRef.current = onJob\n\n const ask = useCallback((job: AiJobSummary) => {\n if (aiJobCancelBlockedReason(job)) return\n setError(null)\n setTarget(job)\n }, [])\n const close = useCallback(() => {\n if (busy) return\n setTarget(null)\n setError(null)\n }, [busy])\n const confirm = useCallback(async () => {\n if (!target || !orgId) return\n setBusy(true)\n setError(null)\n const { job, error: refused } = await cancelAiJobRequest(user, orgId, target.id)\n setBusy(false)\n if (job) onJobRef.current?.(job)\n if (refused) {\n setError(refused)\n return\n }\n setTarget(null)\n }, [target, orgId, user])\n\n const dialog = (\n <Dialog open={target !== null} onClose={close} aria-labelledby=\"ai-job-cancel-title\" maxWidth=\"xs\" fullWidth>\n <DialogTitle id=\"ai-job-cancel-title\">{AI_JOB_CANCEL_TITLE}</DialogTitle>\n <DialogContent>\n <DialogContentText>{AI_JOB_CANCEL_CONFIRM_COPY}</DialogContentText>\n {error ? (\n <Alert severity=\"warning\" sx={{ mt: 2 }}>\n {error}\n </Alert>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={close} disabled={busy}>\n {'Keep building'}\n </Button>\n <Button color=\"error\" variant=\"contained\" onClick={() => void confirm()} disabled={busy || !orgId}>\n {busy ? 'Canceling…' : 'Cancel job'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n return { ask, busyJobId: busy ? (target?.id ?? null) : null, dialog }\n}\n\n/**\n * The Cancel button: enabled while the job can still be stopped, and\n * disabled with the reason as its tooltip once it is done, failed, canceled\n * or already stopping.\n */\nexport function AiJobCancelButton({\n job,\n user,\n orgId,\n onJob,\n size = 'medium',\n variant = 'outlined',\n}: {\n job: AiJobSummary\n user: MaybeTokenSource\n orgId: string | null\n onJob?: (job: AiJobSummary) => void\n size?: ButtonProps['size']\n variant?: ButtonProps['variant']\n}) {\n const { ask, busyJobId, dialog } = useAiJobCancel({ user, orgId, onJob })\n const blocked = aiJobCancelBlockedReason(job)\n const stopping = Boolean(job.cancelRequested) && !AI_JOB_TERMINAL_STATUSES.includes(job.status)\n const button = (\n <Button\n size={size}\n variant={variant}\n color=\"error\"\n disabled={Boolean(blocked) || busyJobId === job.id || !orgId}\n onClick={() => ask(job)}\n >\n {stopping ? 'Stopping…' : 'Cancel'}\n </Button>\n )\n return (\n <>\n {blocked ? (\n <Tooltip title={blocked}>\n {/* A disabled button fires no pointer events; the span carries the tooltip. */}\n <Box component=\"span\" sx={{ display: 'inline-flex' }}>\n {button}\n </Box>\n </Tooltip>\n ) : (\n button\n )}\n {dialog}\n </>\n )\n}\n"],"names":["Alert","Box","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Tooltip","useCallback","useRef","useState","AI_JOB_CANCEL_CONFIRM_COPY","AI_JOB_CANCEL_TITLE","aiJobCancelBlockedReason","AI_JOB_TERMINAL_STATUSES","cancelAiJobRequest","useAiJobCancel","user","orgId","onJob","target","setTarget","busy","setBusy","error","setError","onJobRef","current","ask","job","close","confirm","refused","id","dialog","open","onClose","aria-labelledby","maxWidth","fullWidth","severity","sx","mt","onClick","disabled","color","variant","busyJobId","AiJobCancelButton","size","blocked","stopping","Boolean","cancelRequested","includes","status","button","title","component","display"],"mappings":"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"}
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */ import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, isAiPlanNewRef } from "../model/ai-build-plan.js";
19
- import { aiBuildSmaller, aiJobCreditRange } from "../model/ai-build-job.js";
19
+ import { aiBuildFreePageCapNote, aiBuildSmaller, aiJobCreditRange } from "../model/ai-build-job.js";
20
20
  import { aiCreditRangeText, aiCreditsPromptFor } from "../model/ai-credit-estimate.js";
21
21
  import { aiFreeCreditsResetLabel } from "../model/ai-site-job.js";
22
22
  import { AiCreditsPromptNotice } from "./ai-credits-prompt.component.js";
@@ -96,6 +96,8 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user, or
96
96
  // the home page first, or upgrade — never a refusal with no way on.
97
97
  const freeCredits = (_ref = review == null ? void 0 : review.freeCredits) != null ? _ref : null;
98
98
  const prompt = plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null;
99
+ // A Free build at its page cap says so (AGL-3722): pages past it were left for a later request.
100
+ const pageCapNote = plan && waiting && review.reason === 'plan' && job.kind === 'build' ? aiBuildFreePageCapNote(plan, Boolean(freeCredits)) : null;
99
101
  const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan';
100
102
  const publishing = offersPublish && publish ? {
101
103
  publish: true
@@ -229,6 +231,15 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false, user, or
229
231
  freeCredits && !prompt ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.` : ''
230
232
  ]
231
233
  }),
234
+ pageCapNote && /*#__PURE__*/ _jsx(Typography, {
235
+ variant: "caption",
236
+ color: "text.secondary",
237
+ sx: {
238
+ display: 'block',
239
+ mt: 0.5
240
+ },
241
+ children: pageCapNote
242
+ }),
232
243
  waiting && review.retryRefusal && review.reason !== 'plan' && /*#__PURE__*/ _jsx(Typography, {
233
244
  variant: "caption",
234
245
  color: "text.secondary",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiBuildSmaller, aiJobCreditRange } from '../model/ai-build-job'\nimport { aiCreditRangeText, aiCreditsPromptFor } from '../model/ai-credit-estimate'\nimport { aiFreeCreditsResetLabel } from '../model/ai-site-job'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport type { AiJobResumeOptions } from './ai-job-requests'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n /** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */\n orgSlug?: string | null\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n orgSlug,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one: what it is likely to cost — what builds like it measured\n // — and its ceiling, every pass at its reserve (AGL-3722). A page job\n // counts the creations it builds before its page (AGL-3031).\n const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null\n // A Free build past what is left asks first (AGL-3722): build what fits,\n // the home page first, or upgrade — never a refusal with no way on.\n const freeCredits = review?.freeCredits ?? null\n const prompt =\n plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n const publishing = offersPublish && publish ? { publish: true } : {}\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {range && range.ceiling > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`}\n {freeCredits && !prompt\n ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.`\n : ''}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"build\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => onResume(job, { ...publishing, creditsConfirmed: true })}\n onSmaller={() => onResume(job, { ...publishing, creditsConfirmed: true, reduce: 'first-page' })}\n />\n )}\n {waiting && !prompt && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? publishing : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiBuildSmaller","aiJobCreditRange","aiCreditRangeText","aiCreditsPromptFor","aiFreeCreditsResetLabel","AiCreditsPromptNotice","aiBuildOpNoun","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","orgSlug","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","range","kind","freeCredits","prompt","offersPublish","publishAsked","publishing","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","ceiling","display","left","toLocaleString","resetsOn","retryRefusal","control","checked","onChange","event","target","label","onBuildWhatFits","creditsConfirmed","onSmaller","reduce","disabled","Boolean","undefined"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiBuildFreePageCapNote, aiBuildSmaller, aiJobCreditRange } from '../model/ai-build-job'\nimport { aiCreditRangeText, aiCreditsPromptFor } from '../model/ai-credit-estimate'\nimport { aiFreeCreditsResetLabel } from '../model/ai-site-job'\nimport { AiCreditsPromptNotice } from './ai-credits-prompt.component'\nimport type { AiJobResumeOptions } from './ai-job-requests'\nimport { aiBuildOpNoun } from '../model/ai-build-progress'\nimport { aiJobConfirmingOwnPlan } from '../model/ai-job-activity'\nimport { aiSiteStarterFallbackOffered } from '../model/ai-job-failure-copy'\nimport { AiSiteStarterFallback } from './ai-site-starter-fallback.component'\nimport { Box, Button, Checkbox, Collapse, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer\n * and the dialog that started the job (AGL-3593): what the job reuses from\n * the site, what it creates and why, the screens it builds — and, while the\n * job waits for a person, the one button that confirms the plan or tries a\n * refused step again. Both send the same request (`resumeAiJobRequest`);\n * this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /**\n * Confirms the plan, or tries the refused step again. A build whose request\n * asked to publish confirms with `publish` when its box is ticked (AGL-3616).\n */\n onResume: (job: AiJobSummary, options?: AiJobResumeOptions) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n /**\n * The signed-in user, for \"Use the starter site instead\" on a guided start\n * that did not work out (AGL-3594); the action is drawn only when given.\n */\n user?: Parameters<typeof AiSiteStarterFallback>[0]['user']\n /** The workspace's path slug, for the Upgrade a Free build past what is left offers (AGL-3722). */\n orgSlug?: string | null\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n user,\n orgSlug,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n // Unticked until the person ticks it: a build publishes only when its\n // request asked AND they confirm it here (AGL-3616).\n const [publish, setPublish] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n // A plan the machine is confirming itself is not one to confirm (AGL-3596).\n const waiting = job.status === 'needs_review' && review !== null && !aiJobConfirmingOwnPlan(job)\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one: what it is likely to cost — what builds like it measured\n // — and its ceiling, every pass at its reserve (AGL-3722). A page job\n // counts the creations it builds before its page (AGL-3031).\n const range = plan && waiting && review.reason === 'plan' ? aiJobCreditRange(job.kind, plan) : null\n // A Free build past what is left asks first (AGL-3722): build what fits,\n // the home page first, or upgrade — never a refusal with no way on.\n const freeCredits = review?.freeCredits ?? null\n const prompt =\n plan && range && job.kind === 'build' ? aiCreditsPromptFor(range, freeCredits, aiBuildSmaller(plan)) : null\n // A Free build at its page cap says so (AGL-3722): pages past it were left for a later request.\n const pageCapNote =\n plan && waiting && review.reason === 'plan' && job.kind === 'build'\n ? aiBuildFreePageCapNote(plan, Boolean(freeCredits))\n : null\n const offersPublish = job.kind === 'build' && job.publishAsked === true && waiting && review.reason === 'plan'\n const publishing = offersPublish && publish ? { publish: true } : {}\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n {screen.record\n ? `Builds the record template ${screen.title}, a page per record of ${named(screen.record.dataset)} at /${screen.record.base}/…`\n : `Builds the page ${screen.title} at ${screen.slug}`}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.items ?? []).map((item) => (\n <Typography key={`item-${item.slot}`} variant=\"body2\" role=\"listitem\">\n {`Makes the ${aiBuildOpNoun(item.op).toLowerCase()} ${item.name}`}\n {item.why ? ` — ${item.why}` : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {/*\n A review written for the member (AGL-3594) carries the checks' own\n sentence as `detail`: its findings are then staff reading too, and the\n member reads the job's one sentence alone.\n */}\n {staff && review?.detail && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 0.5 }}>\n {review.detail}\n </Typography>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (staff || !review.detail) && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {range && range.ceiling > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: ${aiCreditRangeText(range)}. What it costs is what its steps spend.`}\n {freeCredits && !prompt\n ? ` You have ${freeCredits.left.toLocaleString('en-US')} of your free AI credits left this month, until ${aiFreeCreditsResetLabel(freeCredits.resetsOn)}.`\n : ''}\n </Typography>\n )}\n {pageCapNote && (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mt: 0.5 }}>\n {pageCapNote}\n </Typography>\n )}\n {waiting && review.retryRefusal && review.reason !== 'plan' && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\" sx={{ mt: 1 }}>\n {review.retryRefusal}\n </Typography>\n )}\n {offersPublish && (\n <FormControlLabel\n sx={{ display: 'flex', mt: 1 }}\n control={<Checkbox size=\"small\" checked={publish} onChange={(event) => setPublish(event.target.checked)} />}\n label={<Typography variant=\"body2\">{'Publish the new pages when they are built'}</Typography>}\n />\n )}\n {waiting && prompt && (\n <AiCreditsPromptNotice\n prompt={prompt}\n noun=\"build\"\n orgSlug={orgSlug}\n busy={busy}\n onBuildWhatFits={() => onResume(job, { ...publishing, creditsConfirmed: true })}\n onSmaller={() => onResume(job, { ...publishing, creditsConfirmed: true, reduce: 'first-page' })}\n />\n )}\n {waiting && !prompt && (\n <Button\n size=\"small\"\n variant=\"contained\"\n // Disabled with its reason above when the Free allowance left\n // cannot pay for another try (AGL-3594).\n disabled={busy || (review.reason !== 'plan' && Boolean(review.retryRefusal))}\n onClick={() => onResume(job, offersPublish && publish ? publishing : undefined)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n {/* A guided start that did not work out can take the starter instead (AGL-3594). */}\n {waiting && user && aiSiteStarterFallbackOffered(job) && <AiSiteStarterFallback job={job} user={user} />}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiBuildFreePageCapNote","aiBuildSmaller","aiJobCreditRange","aiCreditRangeText","aiCreditsPromptFor","aiFreeCreditsResetLabel","AiCreditsPromptNotice","aiBuildOpNoun","aiJobConfirmingOwnPlan","aiSiteStarterFallbackOffered","AiSiteStarterFallback","Box","Button","Checkbox","Collapse","FormControlLabel","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","user","orgSlug","plan","detailsOpen","setDetailsOpen","publish","setPublish","details","ref","slice","labels","waiting","status","range","kind","freeCredits","prompt","pageCapNote","Boolean","offersPublish","publishAsked","publishing","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","record","title","dataset","base","slug","layout","sections","section","items","item","op","toLowerCase","slot","embeds","detail","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","ceiling","display","left","toLocaleString","resetsOn","retryRefusal","control","checked","onChange","event","target","label","onBuildWhatFits","creditsConfirmed","onSmaller","reduce","disabled","undefined"],"mappings":"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"}