@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
- package/src/lib/components/ai-credits-prompt.component.js +84 -34
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
- package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
- package/src/lib/components/ai-job-cancel.component.js +156 -0
- package/src/lib/components/ai-job-cancel.component.js.map +1 -0
- package/src/lib/components/ai-job-links.js +8 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +12 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +141 -72
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +12 -0
- package/src/lib/components/ai-jobs-store.js +15 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +22 -2
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
- package/src/lib/components/ai-usage-meter-refresh.js +63 -0
- package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
- package/src/lib/components/ai-usage-strip.component.js +40 -9
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +12 -16
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +91 -51
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
- package/src/lib/components/use-ai-usage-meter.js +8 -3
- package/src/lib/components/use-ai-usage-meter.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +33 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
- package/src/lib/jobs/ai-job-cancel.js +102 -0
- package/src/lib/jobs/ai-job-cancel.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.js +4 -1
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +7 -2
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +2 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
- package/src/lib/jobs/ai-job-form-dataset.js +161 -0
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
- package/src/lib/jobs/ai-job-form-step.js +41 -10
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-layout-language.js +13 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +6 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +45 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
- package/src/lib/jobs/ai-job-plan-step.js +90 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
- package/src/lib/jobs/ai-job-site-content.js +66 -7
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
- package/src/lib/jobs/ai-job-site-datasets.js +499 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
- package/src/lib/jobs/ai-job-site-step.js +152 -30
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +4 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +37 -12
- package/src/lib/jobs/ai-jobs.js +184 -28
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
- package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
- package/src/lib/layout-language/ai-layout-compiler.js +397 -9
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +5 -3
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-frame.js +58 -3
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +17 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
- package/src/lib/layout-language/ai-layout-listings.js +118 -50
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
- package/src/lib/layout-language/ai-layout-storefront.js +223 -0
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +11 -1
- package/src/lib/model/ai-build-job.js +35 -6
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +23 -3
- package/src/lib/model/ai-build-plan.js +47 -6
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +2 -2
- package/src/lib/model/ai-build-progress.js +6 -4
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +69 -2
- package/src/lib/model/ai-credit-estimate.js +91 -3
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
- package/src/lib/model/ai-job-cancel-copy.js +53 -0
- package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
- package/src/lib/model/ai-job-failure-copy.js +8 -3
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +20 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.js +7 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +38 -4
- package/src/lib/model/ai-site-job.js +50 -14
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +4 -1
- package/src/lib/model/ai-site-kinds.js +34 -6
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
- package/src/lib/runtime/ai-dataset-generation.js +127 -0
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
- package/src/lib/runtime/ai-doctrine-validators.js +280 -6
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +10 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +2 -0
- package/src/lib/runtime/ai-node-tree.js +16 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +53 -7
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
- package/src/lib/server/ai-jobs-cancel.js +27 -9
- package/src/lib/server/ai-jobs-cancel.js.map +1 -1
- package/src/lib/server/ai-jobs-gate.d.ts +12 -2
- package/src/lib/server/ai-jobs-gate.js +20 -9
- package/src/lib/server/ai-jobs-gate.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
- package/src/lib/tools/ai-dataset-tool.js +231 -0
- package/src/lib/tools/ai-dataset-tool.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +2 -1
- package/src/lib/usage/ai-usage-meter.js +17 -1
- package/src/lib/usage/ai-usage-meter.js.map +1 -1
- package/src/lib/usage/ai-usage-wire.d.ts +6 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +7 -0
- package/src/lib/usage/assist-usage.js +14 -4
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
- package/src/lib/usage/free-ai-credits-left.js +30 -4
- 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.
|
|
3
|
+
"version": "1.0.0-beta.239",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.239",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.239",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.239",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.239",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.239",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.239",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.239",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.239",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.239",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.239",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.239",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.239",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.239",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.239",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.239",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.239",
|
|
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
|
|
21
|
-
* about what it costs, what is left,
|
|
22
|
-
* and its
|
|
23
|
-
* starts anything; nothing is started by
|
|
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 {
|
|
19
|
-
import {
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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 {
|
|
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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"}
|
|
@@ -72,6 +72,14 @@ import { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from
|
|
|
72
72
|
host
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
+
if (output.resource === 'draft' && output.draftResource) {
|
|
76
|
+
// A draft another plugin wrote — a dataset a site start or a build made
|
|
77
|
+
// (AGL-3616) — opens where its owner lists it, or nowhere it is not loaded.
|
|
78
|
+
return pluginRecordListHref(output.draftResource, {
|
|
79
|
+
orgSlug,
|
|
80
|
+
host
|
|
81
|
+
});
|
|
82
|
+
}
|
|
75
83
|
if (output.resource === 'form') {
|
|
76
84
|
// A new form has no version for the besigner to open: its own page — the
|
|
77
85
|
// forms plugin's address for it — mints the first one, and holds the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'entry') {\n // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.\n const collectionSlug = output.proposal?.['collectionSlug']\n return typeof collectionSlug === 'string' && collectionSlug\n ? buildRoute(Route.HOST_CONTENT_COLLECTION, { orgSlug, host, collectionSlug })\n : buildRoute(Route.HOST_CONTENT, { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n\n/**\n * Where a person gets more AI credits (AGL-3660): the workspace's Billing\n * page at its plans, where an upgrade and the AI add-on are both sold — the\n * door the AI credits meter's own upgrade link opens.\n */\nexport function aiCreditsBillingHref(orgSlug: string): string {\n return `${buildRoute(Route.MANAGE_BILLING, { orgSlug })}#plans`\n}\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","collectionSlug","proposal","HOST_CONTENT_COLLECTION","HOST_CONTENT","HOST_SETUP_THEME","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages","aiCreditsBillingHref","MANAGE_BILLING"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-links.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n pluginRecordHref,\n pluginRecordListHref,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { AI_JOB_BESIGNER_SEGMENT as BESIGNER_SEGMENT, AI_SITE_BUILD_HREF } from '../model/ai-job-notice'\nimport type { AiJobOutput, AiJobSummary } from '../model/ai-jobs.types'\n\n// Where a job's work opens in the console (AGL-2904), shared by the AI jobs\n// drawer and every dialog that follows the job it started (AGL-3593).\n\n/**\n * Where \"open draft\" goes, or `null` when the output has no page of its\n * own — a `text` output carries its copy on the job and is shown inline.\n * A versioned resource opens in the besigner on the version the job wrote;\n * one the console lists without a detail page opens its list; a `theme`\n * proposal opens the site's Theme section, where it is put in the editor.\n *\n * A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves\n * through the member's host projection by subdomain, never by document id.\n * So an output that carries no `hostSubdomain` gets no link rather than one\n * that opens no site.\n */\nexport function aiJobOutputHref(output: AiJobOutput, orgSlug: string): string | null {\n const host = output.hostSubdomain\n if (!orgSlug || !host) return null\n const segment = BESIGNER_SEGMENT[output.resource]\n if (segment) {\n const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`\n return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base\n }\n if (output.resource === 'product') {\n // The catalog a drafted product lands in: the commerce plugin's address\n // for it (AGL-3080), or no link where commerce is not loaded.\n return pluginRecordListHref('product', { orgSlug, host })\n }\n if (output.resource === 'workflow') {\n // A drafted automation is an action (AGL-2919), listed switched off on\n // the workflows plugin's list of actions, or no link where it is not loaded.\n return pluginRecordListHref('action', { orgSlug, host })\n }\n if (output.resource === 'entry') {\n // A post a guided start wrote (AGL-3676) opens in its blog's list in Content.\n const collectionSlug = output.proposal?.['collectionSlug']\n return typeof collectionSlug === 'string' && collectionSlug\n ? buildRoute(Route.HOST_CONTENT_COLLECTION, { orgSlug, host, collectionSlug })\n : buildRoute(Route.HOST_CONTENT, { orgSlug, host })\n }\n if (output.resource === 'theme') {\n return buildRoute(Route.HOST_SETUP_THEME, { orgSlug, host })\n }\n if (output.resource === 'draft' && output.draftResource) {\n // A draft another plugin wrote — a dataset a site start or a build made\n // (AGL-3616) — opens where its owner lists it, or nowhere it is not loaded.\n return pluginRecordListHref(output.draftResource, { orgSlug, host })\n }\n if (output.resource === 'form') {\n // A new form has no version for the besigner to open: its own page — the\n // forms plugin's address for it — mints the first one, and holds the\n // routing and consent it declares.\n return pluginRecordHref('form', { orgSlug, host }, output.id)\n }\n if (output.resource === 'campaign') {\n // A campaign's page is the marketing plugin's, under the site: its\n // address for the campaign (AGL-3080), or no link where it is not loaded.\n return pluginRecordHref('campaign', { orgSlug, host }, output.id)\n }\n return null\n}\n\n/**\n * The ONE action a finished job's row, its notification and the dialog that\n * started it lead with (AGL-3593): what the job built. Several pages from one\n * job — a site's — open the site's Pages list, where they wait as drafts; one\n * output with a page of its own opens that page; anything else has no single\n * place to go, and the row's own links stand. `null` until the job is done.\n */\nexport function aiJobPrimaryLink(\n job: Pick<AiJobSummary, 'status' | 'outputs'>,\n orgSlug: string,\n): { href: string; label: string } | null {\n if (job.status !== 'done' || !orgSlug) return null\n const screens = job.outputs.filter(\n (output) => output.resource === 'screen' && output.hostSubdomain,\n )\n if (screens.length > 1) {\n return {\n href: buildRoute(Route.HOST_SCREENS, { orgSlug, host: String(screens[0].hostSubdomain) }),\n label: 'Open your draft pages',\n }\n }\n for (const output of job.outputs) {\n const href = aiJobOutputHref(output, orgSlug)\n if (href) {\n return {\n href,\n label: output.resource === 'screen' ? 'Open the draft page' : `Open ${output.label}`,\n }\n }\n }\n return null\n}\n\nexport { AI_SITE_BUILD_HREF }\n\n/**\n * The \"Building your site\" page for one site job (AGL-3594): linkable, so the\n * guided start lands on it, a reload keeps it and a notification opens it.\n * `null` without the org slug or the site's subdomain a console URL needs.\n */\nexport function aiSiteBuildHref(\n orgSlug: string | null | undefined,\n hostSubdomain: string | null | undefined,\n jobId: string,\n): string | null {\n if (!orgSlug || !hostSubdomain || !jobId) return null\n return `/${orgSlug}/hosts/${hostSubdomain}${AI_SITE_BUILD_HREF}/${encodeURIComponent(jobId)}`\n}\n\n/**\n * A finished site's two ways in (AGL-3594): its first page's draft, opened in\n * the console's preview of that version, and the Pages list every draft waits\n * in. Either is `null` where the job reported nothing to open.\n */\nexport function aiSiteBuildDoneLinks(\n job: Pick<AiJobSummary, 'outputs'>,\n orgSlug: string,\n): { view: string | null; pages: string | null } {\n const screens = job.outputs.filter((output) => output.resource === 'screen' && output.hostSubdomain)\n const first = screens[0]\n if (!first || !orgSlug) return { view: null, pages: null }\n const host = String(first.hostSubdomain)\n return {\n view: first.versionId\n ? `/${orgSlug}/hosts/${host}/screens/${first.id}/versions/${first.versionId}/view`\n : null,\n pages: buildRoute(Route.HOST_SCREENS, { orgSlug, host }),\n }\n}\n\n/**\n * Where a person gets more AI credits (AGL-3660): the workspace's Billing\n * page at its plans, where an upgrade and the AI add-on are both sold — the\n * door the AI credits meter's own upgrade link opens.\n */\nexport function aiCreditsBillingHref(orgSlug: string): string {\n return `${buildRoute(Route.MANAGE_BILLING, { orgSlug })}#plans`\n}\n"],"names":["buildRoute","Route","pluginRecordHref","pluginRecordListHref","AI_JOB_BESIGNER_SEGMENT","BESIGNER_SEGMENT","AI_SITE_BUILD_HREF","aiJobOutputHref","output","orgSlug","host","hostSubdomain","segment","resource","base","id","versionId","collectionSlug","proposal","HOST_CONTENT_COLLECTION","HOST_CONTENT","HOST_SETUP_THEME","draftResource","aiJobPrimaryLink","job","status","screens","outputs","filter","length","href","HOST_SCREENS","String","label","aiSiteBuildHref","jobId","encodeURIComponent","aiSiteBuildDoneLinks","first","view","pages","aiCreditsBillingHref","MANAGE_BILLING"],"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",
|