@aglyn/plugins-ai 1.0.0-beta.218 → 1.0.0-beta.220
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/README.md +2 -2
- package/package.json +18 -18
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/activity/ai-activity-actions.js +8 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/billing/ai-month-meter.d.ts +51 -0
- package/src/lib/billing/ai-month-meter.js +89 -0
- package/src/lib/billing/ai-month-meter.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +11 -33
- package/src/lib/billing/ai-overage-close.js +4 -3
- package/src/lib/billing/ai-overage-close.js.map +1 -1
- package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
- package/src/lib/billing/ai-overage-cutover.js +1 -28
- package/src/lib/billing/ai-overage-cutover.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +1 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/billing/ai-overage-standing.js +1 -1
- package/src/lib/billing/ai-overage-standing.js.map +1 -1
- package/src/lib/components/ai-allotment-editor.component.js +1 -1
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
- package/src/lib/components/ai-assist-provider.component.js +5 -4
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +63 -0
- package/src/lib/components/ai-automation-zones.js +31 -0
- package/src/lib/components/ai-automation-zones.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.js +3 -3
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +6 -3
- package/src/lib/components/ai-describe-button.component.js +4 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-experiment-cards.component.js +1 -1
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +8 -0
- package/src/lib/components/ai-job-plan.component.js +21 -4
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
- package/src/lib/components/ai-permissions-on-host.component.js +12 -3
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
- package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
- package/src/lib/components/ai-product-import-option.component.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +124 -0
- package/src/lib/components/ai-product-zones.js +32 -0
- package/src/lib/components/ai-product-zones.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-seo-audit-card.component.js +35 -92
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +21 -15
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +2 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -2
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +1 -1
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/constants.d.ts +5 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/constants.js.map +1 -1
- package/src/lib/declarations.js +2 -0
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +24 -10
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
- package/src/lib/insights/ai-dataset-catalog.js +57 -0
- package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +6 -39
- package/src/lib/insights/ai-figure-readers.js +2 -391
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +10 -1
- package/src/lib/insights/ai-insight-digest.js +16 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +1 -1
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
- package/src/lib/jobs/ai-email-bindings.js +75 -42
- package/src/lib/jobs/ai-email-bindings.js.map +1 -1
- package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
- package/src/lib/jobs/ai-job-campaign-step.js +5 -10
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
- package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
- package/src/lib/jobs/ai-job-component-step.js +32 -9
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +11 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +1 -1
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-email-step.d.ts +5 -3
- package/src/lib/jobs/ai-job-email-step.js +11 -4
- package/src/lib/jobs/ai-job-email-step.js.map +1 -1
- package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
- package/src/lib/jobs/ai-job-form-step.js +60 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +22 -1
- package/src/lib/jobs/ai-job-generation.js +63 -3
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
- package/src/lib/jobs/ai-job-layout-step.js +46 -13
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
- package/src/lib/jobs/ai-job-page-sections.js +7 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +7 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +40 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
- package/src/lib/jobs/ai-job-plan-creations.js +199 -0
- package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +11 -2
- package/src/lib/jobs/ai-job-plan-step.js +49 -23
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
- package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-products-step.js +54 -32
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
- package/src/lib/jobs/ai-job-seo-step.js +35 -108
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
- package/src/lib/jobs/ai-job-site-step.js +6 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
- package/src/lib/jobs/ai-job-template-step.js +73 -15
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-theme-step.js +1 -1
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +17 -9
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +1 -1
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +20 -11
- package/src/lib/jobs/ai-workflow-records.js +80 -43
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-allotments.d.ts +27 -6
- package/src/lib/model/ai-allotments.js +54 -14
- package/src/lib/model/ai-allotments.js.map +1 -1
- package/src/lib/model/ai-automation-outline.d.ts +21 -2
- package/src/lib/model/ai-automation-outline.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +57 -0
- package/src/lib/model/ai-build-plan.js +116 -7
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-creations.d.ts +55 -0
- package/src/lib/model/ai-job-creations.js +85 -0
- package/src/lib/model/ai-job-creations.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +3 -2
- package/src/lib/model/ai-page-job.js +4 -3
- package/src/lib/model/ai-page-job.js.map +1 -1
- package/src/lib/model/ai-seo.d.ts +11 -51
- package/src/lib/model/ai-seo.js +0 -17
- package/src/lib/model/ai-seo.js.map +1 -1
- package/src/lib/model/ai-site-inventory.js +1 -1
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +4 -2
- package/src/lib/model/ai-site-job.js +8 -4
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +10 -0
- package/src/lib/model/ai-template-subjects.js +16 -2
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/model/ai-theme-proposal.d.ts +1 -1
- package/src/lib/model/ai-theme-proposal.js +1 -1
- package/src/lib/model/ai-theme-proposal.js.map +1 -1
- package/src/lib/model/ai-tokens.js +1 -1
- package/src/lib/model/ai-tokens.js.map +1 -1
- package/src/lib/model/ai-usage-by-user.js +1 -1
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +1 -3
- package/src/lib/model/ai-workflow-job.js +1 -2
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +149 -0
- package/src/lib/plan-entitlements.js +168 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +1 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.js +1 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.js +1 -1
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/runtime/ai-device-audit.d.ts +1 -1
- package/src/lib/runtime/ai-device-audit.js +1 -1
- package/src/lib/runtime/ai-device-audit.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
- package/src/lib/runtime/ai-doctrine-validators.js +178 -21
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +5 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +30 -15
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +9 -2
- package/src/lib/runtime/ai-eval.js +29 -2
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-gate.js +1 -1
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +3 -3
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +7 -1
- package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.js +16 -13
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +7 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/seo-audit.d.ts +13 -63
- package/src/lib/runtime/seo-audit.js +22 -207
- package/src/lib/runtime/seo-audit.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +29 -23
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-org.js +1 -1
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +1 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-allotments.js +1 -1
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-assist-prompts.js +3 -3
- package/src/lib/server/ai-assist-prompts.js.map +1 -1
- package/src/lib/server/ai-assist.js +2 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +1 -1
- package/src/lib/server/assist-chat.js +13 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-view-context.js +23 -23
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server/billing-credits.d.ts +5 -0
- package/src/lib/server/billing-credits.js +8 -2
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +2 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
- package/src/lib/testing/stand-in-automation-index.js +102 -0
- package/src/lib/testing/stand-in-automation-index.js.map +1 -0
- package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
- package/src/lib/testing/stand-in-dataset-index.js +80 -0
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
- package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
- package/src/lib/testing/stand-in-list-send-time.js +77 -0
- package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
- package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
- package/src/lib/testing/stand-in-pipeline-index.js +59 -0
- package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
- package/src/lib/testing/stand-in-product-index.d.ts +20 -0
- package/src/lib/testing/stand-in-product-index.js +105 -0
- package/src/lib/testing/stand-in-product-index.js.map +1 -0
- package/src/lib/tools/ai-component-tool.js +2 -2
- package/src/lib/tools/ai-component-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +6 -18
- package/src/lib/tools/ai-seo-tool.js +7 -38
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
- package/src/lib/tools/ai-workflow-tool.js +1 -1
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
- package/src/lib/usage/ai-allotment-alerts.js +39 -15
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
- package/src/lib/usage/ai-quota-checks.d.ts +37 -0
- package/src/lib/usage/ai-quota-checks.js +69 -0
- package/src/lib/usage/ai-quota-checks.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.js +22 -10
- package/src/lib/usage/ai-usage-alerts.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.js +1 -1
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
- package/src/lib/usage/assist-ceiling.d.ts +2 -2
- package/src/lib/usage/assist-ceiling.js +4 -4
- package/src/lib/usage/assist-ceiling.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +494 -0
- package/src/lib/usage/assist-credits.js +524 -0
- package/src/lib/usage/assist-credits.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -2
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-signal-mining.js +1 -1
- package/src/lib/usage/assist-signal-mining.js.map +1 -1
- package/src/lib/usage/assist-spend-guards.d.ts +153 -0
- package/src/lib/usage/assist-spend-guards.js +182 -0
- package/src/lib/usage/assist-spend-guards.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +1 -1
- package/src/lib/usage/assist-usage.js +1 -1
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.js +1 -1
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage-axes.d.ts +49 -0
- package/src/lib/usage-axes.js +108 -0
- package/src/lib/usage-axes.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +0 -61
- package/src/lib/runtime/seo-page-facts.js +0 -150
- package/src/lib/runtime/seo-page-facts.js.map +0 -1
|
@@ -59,9 +59,9 @@ export const AI_BRIEF_COPY = {
|
|
|
59
59
|
title: 'Describe a layout',
|
|
60
60
|
briefLabel: 'What should the layout hold?',
|
|
61
61
|
placeholder: 'A header with our name and links to Services, About and Contact, and a footer with ' + 'our address and opening hours',
|
|
62
|
-
next: 'A plan comes first, and nothing is built until you confirm it. The layout links your ' + '
|
|
62
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The layout links your ' + 'pages, and places your navigation or menu component when the site has one.',
|
|
63
63
|
submit: 'Plan the layout',
|
|
64
|
-
started: 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' + 'confirm it. The layout is built as a draft, and no
|
|
64
|
+
started: 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' + 'confirm it. The layout is built as a draft, and no page uses it until you assign it.',
|
|
65
65
|
failed: 'The layout could not be started. Try again.'
|
|
66
66
|
},
|
|
67
67
|
form: {
|
|
@@ -70,7 +70,7 @@ export const AI_BRIEF_COPY = {
|
|
|
70
70
|
placeholder: 'A quote request form for a roofing company, asking what needs fixing and the best ' + 'time to call',
|
|
71
71
|
next: 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' + 'marketing consent and where each submission goes are planned together.',
|
|
72
72
|
submit: 'Plan the form',
|
|
73
|
-
started: 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' + 'confirm it. The form is built as a draft, and it collects nothing until you place it ' + 'on a
|
|
73
|
+
started: 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' + 'confirm it. The form is built as a draft, and it collects nothing until you place it ' + 'on a page.',
|
|
74
74
|
failed: 'The form could not be started. Try again.'
|
|
75
75
|
},
|
|
76
76
|
component: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Describe it\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first and waits in\n * AI jobs, where the member confirms the plan and opens the draft once it is\n * built; nothing here builds or writes anything.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The template is built as a draft in your library, and nothing on ' +\n 'your site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'screens, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The layout is built as a draft, and no screen uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The form is built as a draft, and it collects nothing until you place it ' +\n 'on a screen.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The component is built as a draft, and no page shows it until you ' +\n 'insert it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n}\n\nexport function AiBriefDialog({ kind, open, onClose, orgId, hostId, user }: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const [started, setStarted] = useState(false)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(false)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n setStarted(true)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started ? (\n <Alert severity=\"success\">{copy.started}</Alert>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","undefined","fullWidth","maxWidth","severity","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Describe it\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first and waits in\n * AI jobs, where the member confirms the plan and opens the draft once it is\n * built; nothing here builds or writes anything.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The template is built as a draft in your library, and nothing on ' +\n 'your site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The form is built as a draft, and it collects nothing until you place it ' +\n 'on a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The component is built as a draft, and no page shows it until you ' +\n 'insert it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n}\n\nexport function AiBriefDialog({ kind, open, onClose, orgId, hostId, user }: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const [started, setStarted] = useState(false)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(false)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n setStarted(true)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started ? (\n <Alert severity=\"success\">{copy.started}</Alert>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","undefined","fullWidth","maxWidth","severity","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsoleHostAutomationsZoneProps } from '
|
|
1
|
+
import type { ConsoleHostAutomationsZoneProps } from './ai-automation-zones';
|
|
2
2
|
import { useAiJobRun } from './use-ai-job-run';
|
|
3
3
|
export declare const AI_AUTOMATION_BRIEF_COPY: {
|
|
4
4
|
readonly title: "Describe an automation";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { ConsoleHostAutomationsZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * \"Describe it\" on the Automation page's Actions (AGL-2919), beside Add action\n * and Recipes through the `hostAutomations` zone: a description becomes a\n * `workflow` job that drafts one automation, switched off.\n *\n * The dialog follows the shared brief dialog's shape — one text box, what\n * happens next, and the action that starts the job — and then follows the\n * job, because an automation's draft needs no plan to confirm: when it is\n * written, the dialog says what to fill in and opens it in the Actions editor.\n */\n\n/** The longest description a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\nexport const AI_AUTOMATION_BRIEF_COPY = {\n title: 'Describe an automation',\n briefLabel: 'What should happen, and when?',\n placeholder:\n 'When someone submits the newsletter form, add them to the newsletter list, make them ' +\n 'a lead and send them a welcome email',\n next:\n 'The automation is drafted switched off, using only the triggers and steps your plan ' +\n 'includes. Anything it needs that your site does not have yet is left for you to fill in.',\n submit: 'Draft the automation',\n started:\n 'The automation is being drafted. This can take a few minutes. You can close this ' +\n 'window: the draft appears in the list, switched off, when it is ready.',\n drafted: 'Drafted',\n review: 'Review it',\n notListed: 'The draft is saved. It appears in the list in a moment.',\n failed: 'The automation could not be drafted. Try again.',\n} as const\n\nexport interface AiAutomationBriefDialogProps {\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the requests carry. */\n user: Parameters<typeof useAiJobRun>[0]\n /** Opens a listed action in the Actions editor; `false` when the list has not read it yet. */\n openAction: (actionId: string) => boolean\n}\n\nexport function AiAutomationBriefDialog(props: AiAutomationBriefDialogProps) {\n const { open, onClose, orgId, hostId, user, openAction } = props\n const copy = AI_AUTOMATION_BRIEF_COPY\n const help = pluginDocsHelp('aiAutomations')\n const [brief, setBrief] = useState('')\n const [notListed, setNotListed] = useState(false)\n const run = useAiJobRun(user, copy.failed)\n const { job, starting, running, notice, reset } = run\n\n // Opening or closing starts over and stops following: a job still drafting\n // carries on in AI jobs, and its draft appears in the list when it is written.\n useEffect(() => {\n reset()\n setNotListed(false)\n }, [open, reset])\n\n const draft = job?.status === 'done' ? job.outputs.find((output) => output.resource === 'workflow') : undefined\n const problem = aiJobProblem(job, copy.failed) ?? (job?.status === 'done' && !draft ? copy.failed : null)\n const asking = !job || Boolean(notice)\n\n const start = async () => {\n const trimmed = brief.trim()\n if (!orgId || !trimmed) return\n setNotListed(false)\n if (await run.start({ orgId, hostId, kind: 'workflow', brief: trimmed, inputs: { mode: 'draft' } })) {\n setBrief('')\n }\n }\n\n const review = () => {\n if (!draft) return\n if (openAction(draft.id)) onClose()\n else setNotListed(true)\n }\n\n return (\n <Dialog open={open} onClose={starting ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {asking ? (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={starting}\n autoFocus\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n {running ? (\n <Alert severity=\"info\" icon={<CircularProgress size={18} aria-label=\"Drafting\" />}>\n {copy.started}\n </Alert>\n ) : null}\n {draft ? (\n <Alert severity=\"success\">\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {`${copy.drafted}: ${draft.label}`}\n </Typography>\n {draft.note ? <Typography variant=\"body2\">{draft.note}</Typography> : null}\n </Alert>\n ) : null}\n {problem ? <Alert severity={job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {notListed ? <Alert severity=\"info\">{copy.notListed}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={starting}>\n {asking ? 'Cancel' : 'Close'}\n </Button>\n {asking ? (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={starting || !orgId || !brief.trim()}\n >\n {copy.submit}\n </Button>\n ) : draft ? (\n <Button variant=\"contained\" onClick={review}>\n {copy.review}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace, and\n * nothing when it says the feature is not this workspace's.\n */\nexport function AiDescribeAutomationButton({ hostId, orgId, openAction }: ConsoleHostAutomationsZoneProps) {\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button size=\"small\" variant=\"outlined\" onClick={() => setOpen(true)}>\n {'Describe it'}\n </Button>\n <AiAutomationBriefDialog\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n openAction={openAction}\n />\n </>\n )\n}\n\nexport default AiDescribeAutomationButton\n"],"names":["pluginDocsHelp","useUser","Alert","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","TextField","Typography","useEffect","useState","aiJobProblem","useAiJobRun","useAiJobsVerdict","BRIEF_MAX_CHARS","AI_AUTOMATION_BRIEF_COPY","title","briefLabel","placeholder","next","submit","started","drafted","review","notListed","failed","AiAutomationBriefDialog","props","open","onClose","orgId","hostId","user","openAction","copy","help","brief","setBrief","setNotListed","run","job","starting","running","notice","reset","draft","status","outputs","find","output","resource","undefined","problem","asking","Boolean","start","trimmed","trim","kind","inputs","mode","id","fullWidth","maxWidth","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","variant","color","href","rel","excerpt","severity","icon","size","aria-label","fontWeight","note","onClick","AiDescribeAutomationButton","data","verdict","setOpen"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type { ConsoleHostAutomationsZoneProps } from './ai-automation-zones'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * \"Describe it\" on the Automation page's Actions (AGL-2919), beside Add action\n * and Recipes through the `hostAutomations` zone: a description becomes a\n * `workflow` job that drafts one automation, switched off.\n *\n * The dialog follows the shared brief dialog's shape — one text box, what\n * happens next, and the action that starts the job — and then follows the\n * job, because an automation's draft needs no plan to confirm: when it is\n * written, the dialog says what to fill in and opens it in the Actions editor.\n */\n\n/** The longest description a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\nexport const AI_AUTOMATION_BRIEF_COPY = {\n title: 'Describe an automation',\n briefLabel: 'What should happen, and when?',\n placeholder:\n 'When someone submits the newsletter form, add them to the newsletter list, make them ' +\n 'a lead and send them a welcome email',\n next:\n 'The automation is drafted switched off, using only the triggers and steps your plan ' +\n 'includes. Anything it needs that your site does not have yet is left for you to fill in.',\n submit: 'Draft the automation',\n started:\n 'The automation is being drafted. This can take a few minutes. You can close this ' +\n 'window: the draft appears in the list, switched off, when it is ready.',\n drafted: 'Drafted',\n review: 'Review it',\n notListed: 'The draft is saved. It appears in the list in a moment.',\n failed: 'The automation could not be drafted. Try again.',\n} as const\n\nexport interface AiAutomationBriefDialogProps {\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the requests carry. */\n user: Parameters<typeof useAiJobRun>[0]\n /** Opens a listed action in the Actions editor; `false` when the list has not read it yet. */\n openAction: (actionId: string) => boolean\n}\n\nexport function AiAutomationBriefDialog(props: AiAutomationBriefDialogProps) {\n const { open, onClose, orgId, hostId, user, openAction } = props\n const copy = AI_AUTOMATION_BRIEF_COPY\n const help = pluginDocsHelp('aiAutomations')\n const [brief, setBrief] = useState('')\n const [notListed, setNotListed] = useState(false)\n const run = useAiJobRun(user, copy.failed)\n const { job, starting, running, notice, reset } = run\n\n // Opening or closing starts over and stops following: a job still drafting\n // carries on in AI jobs, and its draft appears in the list when it is written.\n useEffect(() => {\n reset()\n setNotListed(false)\n }, [open, reset])\n\n const draft = job?.status === 'done' ? job.outputs.find((output) => output.resource === 'workflow') : undefined\n const problem = aiJobProblem(job, copy.failed) ?? (job?.status === 'done' && !draft ? copy.failed : null)\n const asking = !job || Boolean(notice)\n\n const start = async () => {\n const trimmed = brief.trim()\n if (!orgId || !trimmed) return\n setNotListed(false)\n if (await run.start({ orgId, hostId, kind: 'workflow', brief: trimmed, inputs: { mode: 'draft' } })) {\n setBrief('')\n }\n }\n\n const review = () => {\n if (!draft) return\n if (openAction(draft.id)) onClose()\n else setNotListed(true)\n }\n\n return (\n <Dialog open={open} onClose={starting ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {asking ? (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={starting}\n autoFocus\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n {running ? (\n <Alert severity=\"info\" icon={<CircularProgress size={18} aria-label=\"Drafting\" />}>\n {copy.started}\n </Alert>\n ) : null}\n {draft ? (\n <Alert severity=\"success\">\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {`${copy.drafted}: ${draft.label}`}\n </Typography>\n {draft.note ? <Typography variant=\"body2\">{draft.note}</Typography> : null}\n </Alert>\n ) : null}\n {problem ? <Alert severity={job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {notListed ? <Alert severity=\"info\">{copy.notListed}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={starting}>\n {asking ? 'Cancel' : 'Close'}\n </Button>\n {asking ? (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={starting || !orgId || !brief.trim()}\n >\n {copy.submit}\n </Button>\n ) : draft ? (\n <Button variant=\"contained\" onClick={review}>\n {copy.review}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace, and\n * nothing when it says the feature is not this workspace's.\n */\nexport function AiDescribeAutomationButton({ hostId, orgId, openAction }: ConsoleHostAutomationsZoneProps) {\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button size=\"small\" variant=\"outlined\" onClick={() => setOpen(true)}>\n {'Describe it'}\n </Button>\n <AiAutomationBriefDialog\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n openAction={openAction}\n />\n </>\n )\n}\n\nexport default AiDescribeAutomationButton\n"],"names":["pluginDocsHelp","useUser","Alert","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","TextField","Typography","useEffect","useState","aiJobProblem","useAiJobRun","useAiJobsVerdict","BRIEF_MAX_CHARS","AI_AUTOMATION_BRIEF_COPY","title","briefLabel","placeholder","next","submit","started","drafted","review","notListed","failed","AiAutomationBriefDialog","props","open","onClose","orgId","hostId","user","openAction","copy","help","brief","setBrief","setNotListed","run","job","starting","running","notice","reset","draft","status","outputs","find","output","resource","undefined","problem","asking","Boolean","start","trimmed","trim","kind","inputs","mode","id","fullWidth","maxWidth","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","variant","color","href","rel","excerpt","severity","icon","size","aria-label","fontWeight","note","onClick","AiDescribeAutomationButton","data","verdict","setOpen"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import type { ConsoleHostComponentsZoneProps,
|
|
17
|
+
import type { ConsoleHostComponentsZoneProps, ConsoleHostLayoutsZoneProps, ConsoleHostScreensZoneProps, ConsoleHostTemplatesZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
|
|
18
18
|
import { type AiBriefKind } from './ai-brief-dialog.component';
|
|
19
19
|
export interface AiDescribeButtonProps extends ConsoleHostScreensZoneProps {
|
|
20
20
|
/** The job the brief starts: what the page the button sits on lists. */
|
|
@@ -32,8 +32,11 @@ export declare function AiDescribeButton({ kind, hostId, orgId }: AiDescribeButt
|
|
|
32
32
|
export declare function AiDescribeTemplateButton(props: ConsoleHostTemplatesZoneProps): import("react").JSX.Element;
|
|
33
33
|
/** A layout from a brief, on the Layouts page (`hostLayouts`). */
|
|
34
34
|
export declare function AiDescribeLayoutButton(props: ConsoleHostLayoutsZoneProps): import("react").JSX.Element;
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A form from a brief, on the Forms page (`hostForms`, which the forms plugin
|
|
37
|
+
* hosts on the `hostScreens` contract).
|
|
38
|
+
*/
|
|
39
|
+
export declare function AiDescribeFormButton(props: ConsoleHostScreensZoneProps): import("react").JSX.Element;
|
|
37
40
|
/** A reusable component from a brief, on the Components page (`hostComponents`). */
|
|
38
41
|
export declare function AiDescribeComponentButton(props: ConsoleHostComponentsZoneProps): import("react").JSX.Element;
|
|
39
42
|
export default AiDescribeButton;
|
|
@@ -72,7 +72,10 @@ import { AiBriefDialog } from "./ai-brief-dialog.component.js";
|
|
|
72
72
|
kind: "layout"
|
|
73
73
|
}));
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* A form from a brief, on the Forms page (`hostForms`, which the forms plugin
|
|
77
|
+
* hosts on the `hostScreens` contract).
|
|
78
|
+
*/ export function AiDescribeFormButton(props) {
|
|
76
79
|
return /*#__PURE__*/ _jsx(AiDescribeButton, _extends({}, props, {
|
|
77
80
|
kind: "form"
|
|
78
81
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-button.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConsoleHostComponentsZoneProps,\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-describe-button.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConsoleHostComponentsZoneProps,\n ConsoleHostLayoutsZoneProps,\n ConsoleHostScreensZoneProps,\n ConsoleHostTemplatesZoneProps,\n} from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Button } from '@mui/material'\nimport { useEffect, useRef, useState } from 'react'\nimport { AiBriefDialog, type AiBriefKind } from './ai-brief-dialog.component'\n\n/**\n * \"Describe it\" (AGL-2907, AGL-3043, AGL-3051): a job from a brief, beside the\n * create actions of the page that lists what the job makes — a page on\n * Screens, a page template on Templates, a layout on Layouts, a form on Forms\n * and a reusable component on Components — each mounted through that page's\n * zone. The Assist panel's AI jobs opens the same dialog for a page.\n */\n\ntype Verdict = 'checking' | 'ready' | 'hidden'\n\nexport interface AiDescribeButtonProps extends ConsoleHostScreensZoneProps {\n /** The job the brief starts: what the page the button sits on lists. */\n kind: AiBriefKind\n}\n\n/**\n * Renders nothing until the jobs route has answered for this workspace: the\n * shell decided the plan, the member's permission and the site's AI switch\n * before mounting it, and the release flag is the route's to decide, so a 404\n * or a 403 there is this control staying absent. A lockdown is the start\n * door's to say, in the dialog, in its own words.\n */\nexport function AiDescribeButton({ kind, hostId, orgId }: AiDescribeButtonProps) {\n const { data: user } = useUser()\n // Held in a ref so the probe keys on WHO is signed in, never on the\n // identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const uid = user?.uid ?? null\n const [verdict, setVerdict] = useState<Verdict>('checking')\n const [open, setOpen] = useState(false)\n\n useEffect(() => {\n if (!orgId || !uid) return\n let active = true\n void (async () => {\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs?orgId=${encodeURIComponent(orgId)}&limit=1`,\n )\n if (active) {\n setVerdict(response.status === 404 || response.status === 403 ? 'hidden' : 'ready')\n }\n } catch {\n if (active) setVerdict('hidden')\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid])\n\n if (verdict !== 'ready') return null\n return (\n <>\n <Button size=\"small\" variant=\"outlined\" onClick={() => setOpen(true)}>\n {'Describe it'}\n </Button>\n <AiBriefDialog\n kind={kind}\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n hostId={hostId}\n user={user}\n />\n </>\n )\n}\n\n/** A page template from a brief, on the Templates page (`hostTemplates`). */\nexport function AiDescribeTemplateButton(props: ConsoleHostTemplatesZoneProps) {\n return <AiDescribeButton {...props} kind=\"template\" />\n}\n\n/** A layout from a brief, on the Layouts page (`hostLayouts`). */\nexport function AiDescribeLayoutButton(props: ConsoleHostLayoutsZoneProps) {\n return <AiDescribeButton {...props} kind=\"layout\" />\n}\n\n/**\n * A form from a brief, on the Forms page (`hostForms`, which the forms plugin\n * hosts on the `hostScreens` contract).\n */\nexport function AiDescribeFormButton(props: ConsoleHostScreensZoneProps) {\n return <AiDescribeButton {...props} kind=\"form\" />\n}\n\n/** A reusable component from a brief, on the Components page (`hostComponents`). */\nexport function AiDescribeComponentButton(props: ConsoleHostComponentsZoneProps) {\n return <AiDescribeButton {...props} kind=\"component\" />\n}\n\nexport default AiDescribeButton\n"],"names":["authorizedFetch","useUser","Button","useEffect","useRef","useState","AiBriefDialog","AiDescribeButton","kind","hostId","orgId","data","user","userRef","current","uid","verdict","setVerdict","open","setOpen","active","response","encodeURIComponent","status","size","variant","onClick","onClose","AiDescribeTemplateButton","props","AiDescribeLayoutButton","AiDescribeFormButton","AiDescribeComponentButton"],"mappings":"AAAA;;;AAyBA,SAASA,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,MAAM,QAAQ,gBAAe;AACtC,SAASC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACnD,SAASC,aAAa,QAA0B,iCAA6B;AAiB7E;;;;;;CAMC,GACD,OAAO,SAASC,iBAAiB,EAAEC,IAAI,EAAEC,MAAM,EAAEC,KAAK,EAAyB;;IAC7E,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAGX;IACvB,oEAAoE;IACpE,uCAAuC;IACvC,MAAMY,UAAUT,OAAOQ;IACvBC,QAAQC,OAAO,GAAGF;IAClB,MAAMG,cAAMH,wBAAAA,KAAMG,GAAG,mBAAI;IACzB,MAAM,CAACC,SAASC,WAAW,GAAGZ,SAAkB;IAChD,MAAM,CAACa,MAAMC,QAAQ,GAAGd,SAAS;IAEjCF,UAAU;QACR,IAAI,CAACO,SAAS,CAACK,KAAK;QACpB,IAAIK,SAAS;QACb,KAAK,AAAC,CAAA;YACJ,IAAI;gBACF,MAAMC,WAAW,MAAMrB,gBACrBa,QAAQC,OAAO,EACf,CAAC,mBAAmB,EAAEQ,mBAAmBZ,OAAO,QAAQ,CAAC;gBAE3D,IAAIU,QAAQ;oBACVH,WAAWI,SAASE,MAAM,KAAK,OAAOF,SAASE,MAAM,KAAK,MAAM,WAAW;gBAC7E;YACF,EAAE,eAAM;gBACN,IAAIH,QAAQH,WAAW;YACzB;QACF,CAAA;QACA,OAAO;YACLG,SAAS;QACX;IACF,GAAG;QAACV;QAAOK;KAAI;IAEf,IAAIC,YAAY,SAAS,OAAO;IAChC,qBACE;;0BACE,KAACd;gBAAOsB,MAAK;gBAAQC,SAAQ;gBAAWC,SAAS,IAAMP,QAAQ;0BAC5D;;0BAEH,KAACb;gBACCE,MAAMA;gBACNU,MAAMA;gBACNS,SAAS,IAAMR,QAAQ;gBACvBT,OAAOA;gBACPD,QAAQA;gBACRG,MAAMA;;;;AAId;AAEA,2EAA2E,GAC3E,OAAO,SAASgB,yBAAyBC,KAAoC;IAC3E,qBAAO,KAACtB,+BAAqBsB;QAAOrB,MAAK;;AAC3C;AAEA,gEAAgE,GAChE,OAAO,SAASsB,uBAAuBD,KAAkC;IACvE,qBAAO,KAACtB,+BAAqBsB;QAAOrB,MAAK;;AAC3C;AAEA;;;CAGC,GACD,OAAO,SAASuB,qBAAqBF,KAAkC;IACrE,qBAAO,KAACtB,+BAAqBsB;QAAOrB,MAAK;;AAC3C;AAEA,kFAAkF,GAClF,OAAO,SAASwB,0BAA0BH,KAAqC;IAC7E,qBAAO,KAACtB,+BAAqBsB;QAAOrB,MAAK;;AAC3C;AAEA,eAAeD,iBAAgB"}
|
|
@@ -97,7 +97,7 @@ const liftText = (value)=>value === null ? '—' : `${value >= 0 ? '+' : ''}${va
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
/** Where a proposed variant's copy lands, said before the person presses the button. */ export function aiExperimentApplyCopy(target) {
|
|
100
|
-
return target === 'email' ? 'Each variant above takes its name, subject and body. Nothing is saved until you save the experiment.' : 'Each variant above takes its name. The copy itself is a
|
|
100
|
+
return target === 'email' ? 'Each variant above takes its name, subject and body. Nothing is saved until you save the experiment.' : 'Each variant above takes its name. The copy itself is a page version — make one per variant in the editor and pin it above.';
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* "Write variants with AI", in the experiment editor beneath the variants it
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-experiment-cards.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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useHostOrgId, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Link,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n AI_EXPERIMENT_SUBJECT_MAX_CHARS,\n type AiExperimentArm,\n type AiExperimentTarget,\n} from '../model/ai-experiment'\nimport {\n aiExperimentArmIsNamed,\n aiExperimentVerdictDisplay,\n readAiExperimentExplanation,\n readAiExperimentVariants,\n type AiExperimentExplanationProposal,\n type AiExperimentVariantsProposalView,\n} from '../model/ai-experiment-proposal'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * A/B tests by AI in the console (AGL-2914): the two doors a person reaches\n * the `experiment` job through, and the surfaces that render what it answers.\n *\n * Both are widgets in zones the A/B TESTING card hosts, not surfaces of this\n * plugin's own. The test, the experiment document, the traffic split and the\n * counters belong to the plugin that draws that card, and neither plugin\n * imports the other: the zone's id and the props it hands a widget are the\n * whole contract, and the interfaces below restate the halves this widget\n * reads rather than importing the owner's.\n *\n * ## The result surface may not decide anything\n *\n * The step reached the verdict from the counts before a model was asked, and\n * cut the answer back to it. A card can undo all of that silently — a\n * confident heading, a highlighted row, a badge saying which to ship — and\n * the reader will take the layout for the claim. So {@link AiExperimentResultCard}\n * asks `aiExperimentVerdictDisplay` what the verdict permits before it draws\n * anything: an undecided result gets a neutral chip, a sentence saying it\n * settled nothing ABOVE the explanation, no marked arm and no recommendation.\n * Nothing on it is conditioned on which arm has the best rate.\n *\n * ## Neither card writes\n *\n * The variants card fills the experiment editor's own fields, unsaved, and\n * the dialog's Save is the write. The result card has nothing to apply.\n */\n\nconst VARIANTS_FAILED_COPY = 'Variants could not be written. Try again.'\nconst EXPLAIN_FAILED_COPY = 'The result could not be explained. Try again.'\n\n/** What the test varies, in the words this card's own copy uses. */\nconst TARGET_NOUN: Record<AiExperimentTarget, string> = {\n screen: 'page',\n section: 'section',\n email: 'email',\n}\n\n/** One of the experiment editor's variants, as the zone hands it over. */\nexport interface AiExperimentZoneVariant {\n id: string\n name: string\n subject: string\n body: string\n}\n\n/** What the variants zone hands this widget. */\nexport interface AiExperimentVariantsCardProps {\n hostId: string\n experimentId: string\n name: string\n target: AiExperimentTarget\n goal: string\n variants: AiExperimentZoneVariant[]\n proposeVariants: (\n variants: readonly AiExperimentZoneVariant[],\n key: string,\n ) => void\n}\n\n/** What the result zone hands this widget. */\nexport interface AiExperimentResultCardProps {\n hostId: string\n experimentId: string\n test: string\n}\n\n/** The proposal a finished experiment job carries, whichever task it ran. */\nfunction proposalOf(job: AiJobSummary | null): Record<string, unknown> | null {\n if (job?.status !== 'done') return null\n return job.outputs.find((output) => output.resource === 'experiment')?.proposal ?? null\n}\n\nconst rateText = (value: number | null): string =>\n value === null ? '—' : `${value}%`\n\nconst liftText = (value: number | null): string =>\n value === null ? '—' : `${value >= 0 ? '+' : ''}${value}%`\n\n/**\n * The copy an email test varies, as the editor holds it: the FIRST variant's\n * subject and body, which is the control. A screen or a section varies a\n * screen version, which the editor does not hold, so there is nothing to\n * prefill and the person pastes what is under test.\n */\nexport function aiExperimentSubjectFrom(\n target: AiExperimentTarget,\n variants: readonly AiExperimentZoneVariant[],\n): string {\n if (target !== 'email') return ''\n const control = variants[0]\n if (!control) return ''\n return [control.subject, control.body].filter(Boolean).join('\\n\\n')\n}\n\n/**\n * A proposal mapped onto the editor's variants, in order. Only the fields\n * that target has a place for are filled: an email varies its subject and\n * body, and a screen or a section varies a screen version, so there only the\n * name is filled and the copy is read on the card.\n */\nexport function aiExperimentVariantDrafts(\n target: AiExperimentTarget,\n variants: readonly AiExperimentZoneVariant[],\n proposal: AiExperimentVariantsProposalView,\n): AiExperimentZoneVariant[] {\n return variants.map((variant, index) => {\n const proposed = proposal.variants[index]\n if (!proposed) return variant\n return {\n id: variant.id,\n name: proposed.name,\n subject: target === 'email' ? proposed.subject : '',\n body: target === 'email' ? proposed.body : '',\n }\n })\n}\n\n/** Where a proposed variant's copy lands, said before the person presses the button. */\nexport function aiExperimentApplyCopy(target: AiExperimentTarget): string {\n return target === 'email'\n ? 'Each variant above takes its name, subject and body. Nothing is saved until you save the experiment.'\n : 'Each variant above takes its name. The copy itself is a screen version — make one per variant in the editor and pin it above.'\n}\n\n/**\n * \"Write variants with AI\", in the experiment editor beneath the variants it\n * writes for. It proposes COPY: no weight, no traffic split, no goal and no\n * schedule, and the first variant it writes is the copy as it stands so the\n * test has something to measure against.\n */\nexport function AiExperimentVariantsCard(props: AiExperimentVariantsCardProps) {\n const { hostId, target, variants, proposeVariants } = props\n // The zone hands the SITE, which is what the card it is drawn in knows. The\n // organization is this widget's to resolve, as every AI widget in another\n // plugin's zone resolves it.\n const orgId = useHostOrgId(hostId) ?? undefined\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, VARIANTS_FAILED_COPY)\n const prefill = useMemo(\n () => aiExperimentSubjectFrom(target, variants),\n [target, variants],\n )\n const [subject, setSubject] = useState(prefill)\n const [applied, setApplied] = useState<string | null>(null)\n // The control's copy follows the editor until the person edits this field,\n // so switching a draft test from a page to an email fills it rather than\n // leaving the earlier target's copy under a question about a new one.\n const [typed, setTyped] = useState(false)\n useEffect(() => {\n if (!typed) setSubject(prefill)\n }, [prefill, typed])\n\n const proposal = useMemo(\n () => readAiExperimentVariants(proposalOf(run.job)),\n [run.job],\n )\n const problem =\n aiJobProblem(run.job, VARIANTS_FAILED_COPY) ??\n (run.job?.status === 'done' && !proposal ? VARIANTS_FAILED_COPY : null)\n\n if (verdict !== 'ready') return null\n\n const busy = run.starting || run.running\n const noun = TARGET_NOUN[target]\n const variantsHelp = pluginDocsHelp('aiExperiments', {\n anchor: '#write-variants',\n excerpt:\n 'Writes two to four variants of the copy under test, the first of them your copy unchanged. They go into the editor’s fields unsaved; nothing is saved until you save the experiment.',\n })\n const write = () => {\n if (!orgId || !subject.trim()) return\n setApplied(null)\n void run.start({\n orgId,\n hostId,\n kind: 'experiment',\n brief:\n `Write variants of this ${noun} for the A/B test ` +\n `“${props.name.trim() || 'this test'}”.`,\n inputs: {\n task: 'variants',\n target,\n subject: subject.trim().slice(0, AI_EXPERIMENT_SUBJECT_MAX_CHARS),\n goal: props.goal,\n ...(props.experimentId ? { experimentId: props.experimentId } : {}),\n },\n })\n }\n\n const apply = () => {\n if (!proposal || !run.job) return\n proposeVariants(\n aiExperimentVariantDrafts(target, variants, proposal),\n run.job.id,\n )\n setApplied(run.job.id)\n }\n\n return (\n <Box\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }}\n aria-label=\"Write variants with AI\"\n >\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline' }}>\n <Typography variant=\"subtitle2\" sx={{ flexGrow: 1 }}>\n {'Write variants with AI'}\n </Typography>\n <Link\n href={variantsHelp.href}\n target=\"_blank\"\n rel=\"noopener\"\n variant=\"caption\"\n title={variantsHelp.excerpt}\n >\n {'How it works'}\n </Link>\n </Stack>\n <TextField\n size=\"small\"\n multiline\n minRows={2}\n label={`The ${noun} copy under test`}\n placeholder={\n target === 'email'\n ? 'The subject line and message as they stand'\n : 'The headline and copy as they stand on the page'\n }\n helperText={\n 'Variants are written from this. The first one it writes is this copy unchanged, so the test has a control.'\n }\n value={subject}\n onChange={(event) => {\n setTyped(true)\n setSubject(event.target.value.slice(0, AI_EXPERIMENT_SUBJECT_MAX_CHARS))\n }}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={busy || !orgId || !subject.trim()}\n onClick={write}\n >\n {run.job && !busy ? 'Write them again' : 'Write variants'}\n </Button>\n {busy ? <CircularProgress size={16} aria-label=\"Writing variants\" /> : null}\n </Stack>\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n {problem ? (\n <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>\n {problem}\n </Alert>\n ) : null}\n\n {proposal ? (\n <Stack spacing={1} aria-label=\"Proposed variants\">\n {proposal.goal ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Written to move: ${proposal.goal}`}\n </Typography>\n ) : null}\n {proposal.variants.map((variant, index) => (\n <Box\n key={`${index}:${variant.name}`}\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1 }}\n >\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">\n {variant.name || `Variant ${index + 1}`}\n </Typography>\n {variant.subject ? (\n <Typography variant=\"body2\">{`Subject: ${variant.subject}`}</Typography>\n ) : null}\n {variant.preheader ? (\n <Typography variant=\"body2\">{`Preheader: ${variant.preheader}`}</Typography>\n ) : null}\n {variant.headline ? (\n <Typography variant=\"body2\">{variant.headline}</Typography>\n ) : null}\n {variant.body ? (\n <Typography\n variant=\"body2\"\n sx={{ whiteSpace: 'pre-line', wordBreak: 'break-word' }}\n >\n {variant.body}\n </Typography>\n ) : null}\n {variant.rationale ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {variant.rationale}\n </Typography>\n ) : null}\n </Stack>\n </Box>\n ))}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {aiExperimentApplyCopy(target)}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={applied === run.job?.id}\n onClick={apply}\n >\n {'Put into the variants'}\n </Button>\n </Stack>\n {applied === run.job?.id ? (\n <Alert severity=\"success\">\n {'In the variants above. Review them, then Save the experiment — or Cancel to leave it as it was.'}\n </Alert>\n ) : null}\n </Stack>\n ) : null}\n </Stack>\n </Box>\n )\n}\nAiExperimentVariantsCard.displayName = 'AiExperimentVariantsCard'\n\n/** One arm, drawn the same way whatever the figures say about it. */\nfunction ArmRow({\n arm,\n named,\n}: {\n arm: AiExperimentArm\n named: boolean\n}) {\n return (\n <TableRow>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{arm.id}</Typography>\n {named ? <Chip size=\"small\" color=\"success\" label=\"Ahead\" /> : null}\n </Stack>\n </TableCell>\n <TableCell>{arm.exposures}</TableCell>\n <TableCell>{arm.conversions}</TableCell>\n <TableCell>{rateText(arm.rate)}</TableCell>\n <TableCell>{liftText(arm.lift)}</TableCell>\n <TableCell>{rateText(arm.confidence)}</TableCell>\n </TableRow>\n )\n}\n\n/**\n * The explanation, drawn to the verdict rather than to the numbers.\n *\n * The chip and the caveat come FIRST, above the prose: a reader who stops\n * after one line must stop on what the result settled, not on a sentence\n * about one variant. No row is marked unless the verdict names an arm, and\n * `aiExperimentArmIsNamed` is asked per row rather than `winnerId` compared\n * directly, so a stored winner beside an undecided verdict marks nothing.\n */\nexport function AiExperimentExplanation({\n explanation,\n}: {\n explanation: AiExperimentExplanationProposal\n}) {\n const display = aiExperimentVerdictDisplay(explanation.verdict)\n return (\n <Stack spacing={1} aria-label=\"What this result shows\">\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip size=\"small\" color={display.tone} label={display.status} />\n {display.namesAnArm ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Called at ${explanation.threshold}% confidence.`}\n </Typography>\n ) : null}\n </Stack>\n {display.caveat ? (\n <Typography variant=\"body2\">{display.caveat}</Typography>\n ) : null}\n <Typography variant=\"body2\" sx={{ wordBreak: 'break-word' }}>\n {explanation.headline}\n </Typography>\n {explanation.points.map((point) => (\n <Typography key={point} variant=\"body2\" color=\"text.secondary\">\n {point}\n </Typography>\n ))}\n {explanation.arms.length ? (\n <ScrollTable size=\"small\" aria-label=\"The figures this reads\">\n <TableHead>\n <TableRow>\n <TableCell>{'Variant'}</TableCell>\n <TableCell>{'Shown'}</TableCell>\n <TableCell>{'Conversions'}</TableCell>\n <TableCell>{'Rate'}</TableCell>\n <TableCell>{'Lift'}</TableCell>\n <TableCell>{'Confidence'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {explanation.arms.map((arm) => (\n <ArmRow\n key={arm.id}\n arm={arm}\n named={aiExperimentArmIsNamed(explanation, arm.id)}\n />\n ))}\n </TableBody>\n </ScrollTable>\n ) : null}\n {explanation.next ? (\n <Typography variant=\"body2\">{explanation.next}</Typography>\n ) : null}\n </Stack>\n )\n}\nAiExperimentExplanation.displayName = 'AiExperimentExplanation'\n\n/**\n * \"Explain this result\", below one test's figures. It starts an `experiment`\n * job, which reads the same figures the dialog above it shows, decides what\n * they support in code, and asks a model only for the words.\n */\nexport function AiExperimentResultCard(props: AiExperimentResultCardProps) {\n const { hostId, test } = props\n const orgId = useHostOrgId(hostId) ?? undefined\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, EXPLAIN_FAILED_COPY)\n const explanation = useMemo(\n () => readAiExperimentExplanation(proposalOf(run.job)),\n [run.job],\n )\n const problem =\n aiJobProblem(run.job, EXPLAIN_FAILED_COPY) ??\n (run.job?.status === 'done' && !explanation ? EXPLAIN_FAILED_COPY : null)\n\n if (verdict !== 'ready') return null\n\n const busy = run.starting || run.running\n const resultHelp = pluginDocsHelp('aiExperiments', {\n anchor: '#the-verdict',\n excerpt:\n 'What the figures support is worked out from the counts before a model is asked anything, and the explanation is held to it — so an undecided test reads as undecided and no variant is named.',\n })\n const explain = () => {\n if (!orgId || !test) return\n void run.start({\n orgId,\n hostId,\n kind: 'experiment',\n brief: `Explain the result of the A/B test “${test}” in plain words.`,\n inputs: {\n task: 'explain',\n test,\n ...(props.experimentId ? { experimentId: props.experimentId } : {}),\n },\n })\n }\n\n return (\n <Box\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5, mt: 2 }}\n aria-label=\"Explain this result with AI\"\n >\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {'Read these figures in plain words, including whether they settle anything yet.'}\n </Typography>\n <Link\n href={resultHelp.href}\n target=\"_blank\"\n rel=\"noopener\"\n variant=\"caption\"\n title={resultHelp.excerpt}\n >\n {'How it works'}\n </Link>\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={busy || !orgId || !test}\n onClick={explain}\n >\n {run.job && !busy ? 'Ask again' : 'Explain this result'}\n </Button>\n </Stack>\n {busy ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={16} aria-label=\"Reading the result\" />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Reading the result.'}\n </Typography>\n </Stack>\n ) : null}\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n {problem ? (\n <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>\n {problem}\n </Alert>\n ) : null}\n {explanation ? <AiExperimentExplanation explanation={explanation} /> : null}\n </Stack>\n </Box>\n )\n}\nAiExperimentResultCard.displayName = 'AiExperimentResultCard'\n"],"names":["pluginDocsHelp","ScrollTable","useHostOrgId","useUser","Alert","Box","Button","Chip","CircularProgress","Link","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useEffect","useMemo","useState","AI_EXPERIMENT_SUBJECT_MAX_CHARS","aiExperimentArmIsNamed","aiExperimentVerdictDisplay","readAiExperimentExplanation","readAiExperimentVariants","aiJobProblem","useAiJobRun","useAiJobsVerdict","VARIANTS_FAILED_COPY","EXPLAIN_FAILED_COPY","TARGET_NOUN","screen","section","email","proposalOf","job","status","outputs","find","output","resource","proposal","rateText","value","liftText","aiExperimentSubjectFrom","target","variants","control","subject","body","filter","Boolean","join","aiExperimentVariantDrafts","map","variant","index","proposed","id","name","aiExperimentApplyCopy","AiExperimentVariantsCard","props","run","hostId","proposeVariants","orgId","undefined","data","user","verdict","prefill","setSubject","applied","setApplied","typed","setTyped","problem","busy","starting","running","noun","variantsHelp","anchor","excerpt","write","trim","start","kind","brief","inputs","task","slice","goal","experimentId","apply","sx","border","borderColor","borderRadius","p","aria-label","spacing","direction","alignItems","flexGrow","href","rel","title","size","multiline","minRows","label","placeholder","helperText","onChange","event","disabled","onClick","notice","severity","color","preheader","headline","whiteSpace","wordBreak","rationale","displayName","ArmRow","arm","named","exposures","conversions","rate","lift","confidence","AiExperimentExplanation","explanation","display","tone","namesAnArm","threshold","caveat","points","point","arms","length","next","AiExperimentResultCard","test","resultHelp","explain","mt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-experiment-cards.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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useHostOrgId, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Link,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n AI_EXPERIMENT_SUBJECT_MAX_CHARS,\n type AiExperimentArm,\n type AiExperimentTarget,\n} from '../model/ai-experiment'\nimport {\n aiExperimentArmIsNamed,\n aiExperimentVerdictDisplay,\n readAiExperimentExplanation,\n readAiExperimentVariants,\n type AiExperimentExplanationProposal,\n type AiExperimentVariantsProposalView,\n} from '../model/ai-experiment-proposal'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict } from './use-ai-job-run'\n\n/**\n * A/B tests by AI in the console (AGL-2914): the two doors a person reaches\n * the `experiment` job through, and the surfaces that render what it answers.\n *\n * Both are widgets in zones the A/B TESTING card hosts, not surfaces of this\n * plugin's own. The test, the experiment document, the traffic split and the\n * counters belong to the plugin that draws that card, and neither plugin\n * imports the other: the zone's id and the props it hands a widget are the\n * whole contract, and the interfaces below restate the halves this widget\n * reads rather than importing the owner's.\n *\n * ## The result surface may not decide anything\n *\n * The step reached the verdict from the counts before a model was asked, and\n * cut the answer back to it. A card can undo all of that silently — a\n * confident heading, a highlighted row, a badge saying which to ship — and\n * the reader will take the layout for the claim. So {@link AiExperimentResultCard}\n * asks `aiExperimentVerdictDisplay` what the verdict permits before it draws\n * anything: an undecided result gets a neutral chip, a sentence saying it\n * settled nothing ABOVE the explanation, no marked arm and no recommendation.\n * Nothing on it is conditioned on which arm has the best rate.\n *\n * ## Neither card writes\n *\n * The variants card fills the experiment editor's own fields, unsaved, and\n * the dialog's Save is the write. The result card has nothing to apply.\n */\n\nconst VARIANTS_FAILED_COPY = 'Variants could not be written. Try again.'\nconst EXPLAIN_FAILED_COPY = 'The result could not be explained. Try again.'\n\n/** What the test varies, in the words this card's own copy uses. */\nconst TARGET_NOUN: Record<AiExperimentTarget, string> = {\n screen: 'page',\n section: 'section',\n email: 'email',\n}\n\n/** One of the experiment editor's variants, as the zone hands it over. */\nexport interface AiExperimentZoneVariant {\n id: string\n name: string\n subject: string\n body: string\n}\n\n/** What the variants zone hands this widget. */\nexport interface AiExperimentVariantsCardProps {\n hostId: string\n experimentId: string\n name: string\n target: AiExperimentTarget\n goal: string\n variants: AiExperimentZoneVariant[]\n proposeVariants: (\n variants: readonly AiExperimentZoneVariant[],\n key: string,\n ) => void\n}\n\n/** What the result zone hands this widget. */\nexport interface AiExperimentResultCardProps {\n hostId: string\n experimentId: string\n test: string\n}\n\n/** The proposal a finished experiment job carries, whichever task it ran. */\nfunction proposalOf(job: AiJobSummary | null): Record<string, unknown> | null {\n if (job?.status !== 'done') return null\n return job.outputs.find((output) => output.resource === 'experiment')?.proposal ?? null\n}\n\nconst rateText = (value: number | null): string =>\n value === null ? '—' : `${value}%`\n\nconst liftText = (value: number | null): string =>\n value === null ? '—' : `${value >= 0 ? '+' : ''}${value}%`\n\n/**\n * The copy an email test varies, as the editor holds it: the FIRST variant's\n * subject and body, which is the control. A screen or a section varies a\n * screen version, which the editor does not hold, so there is nothing to\n * prefill and the person pastes what is under test.\n */\nexport function aiExperimentSubjectFrom(\n target: AiExperimentTarget,\n variants: readonly AiExperimentZoneVariant[],\n): string {\n if (target !== 'email') return ''\n const control = variants[0]\n if (!control) return ''\n return [control.subject, control.body].filter(Boolean).join('\\n\\n')\n}\n\n/**\n * A proposal mapped onto the editor's variants, in order. Only the fields\n * that target has a place for are filled: an email varies its subject and\n * body, and a screen or a section varies a screen version, so there only the\n * name is filled and the copy is read on the card.\n */\nexport function aiExperimentVariantDrafts(\n target: AiExperimentTarget,\n variants: readonly AiExperimentZoneVariant[],\n proposal: AiExperimentVariantsProposalView,\n): AiExperimentZoneVariant[] {\n return variants.map((variant, index) => {\n const proposed = proposal.variants[index]\n if (!proposed) return variant\n return {\n id: variant.id,\n name: proposed.name,\n subject: target === 'email' ? proposed.subject : '',\n body: target === 'email' ? proposed.body : '',\n }\n })\n}\n\n/** Where a proposed variant's copy lands, said before the person presses the button. */\nexport function aiExperimentApplyCopy(target: AiExperimentTarget): string {\n return target === 'email'\n ? 'Each variant above takes its name, subject and body. Nothing is saved until you save the experiment.'\n : 'Each variant above takes its name. The copy itself is a page version — make one per variant in the editor and pin it above.'\n}\n\n/**\n * \"Write variants with AI\", in the experiment editor beneath the variants it\n * writes for. It proposes COPY: no weight, no traffic split, no goal and no\n * schedule, and the first variant it writes is the copy as it stands so the\n * test has something to measure against.\n */\nexport function AiExperimentVariantsCard(props: AiExperimentVariantsCardProps) {\n const { hostId, target, variants, proposeVariants } = props\n // The zone hands the SITE, which is what the card it is drawn in knows. The\n // organization is this widget's to resolve, as every AI widget in another\n // plugin's zone resolves it.\n const orgId = useHostOrgId(hostId) ?? undefined\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, VARIANTS_FAILED_COPY)\n const prefill = useMemo(\n () => aiExperimentSubjectFrom(target, variants),\n [target, variants],\n )\n const [subject, setSubject] = useState(prefill)\n const [applied, setApplied] = useState<string | null>(null)\n // The control's copy follows the editor until the person edits this field,\n // so switching a draft test from a page to an email fills it rather than\n // leaving the earlier target's copy under a question about a new one.\n const [typed, setTyped] = useState(false)\n useEffect(() => {\n if (!typed) setSubject(prefill)\n }, [prefill, typed])\n\n const proposal = useMemo(\n () => readAiExperimentVariants(proposalOf(run.job)),\n [run.job],\n )\n const problem =\n aiJobProblem(run.job, VARIANTS_FAILED_COPY) ??\n (run.job?.status === 'done' && !proposal ? VARIANTS_FAILED_COPY : null)\n\n if (verdict !== 'ready') return null\n\n const busy = run.starting || run.running\n const noun = TARGET_NOUN[target]\n const variantsHelp = pluginDocsHelp('aiExperiments', {\n anchor: '#write-variants',\n excerpt:\n 'Writes two to four variants of the copy under test, the first of them your copy unchanged. They go into the editor’s fields unsaved; nothing is saved until you save the experiment.',\n })\n const write = () => {\n if (!orgId || !subject.trim()) return\n setApplied(null)\n void run.start({\n orgId,\n hostId,\n kind: 'experiment',\n brief:\n `Write variants of this ${noun} for the A/B test ` +\n `“${props.name.trim() || 'this test'}”.`,\n inputs: {\n task: 'variants',\n target,\n subject: subject.trim().slice(0, AI_EXPERIMENT_SUBJECT_MAX_CHARS),\n goal: props.goal,\n ...(props.experimentId ? { experimentId: props.experimentId } : {}),\n },\n })\n }\n\n const apply = () => {\n if (!proposal || !run.job) return\n proposeVariants(\n aiExperimentVariantDrafts(target, variants, proposal),\n run.job.id,\n )\n setApplied(run.job.id)\n }\n\n return (\n <Box\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }}\n aria-label=\"Write variants with AI\"\n >\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline' }}>\n <Typography variant=\"subtitle2\" sx={{ flexGrow: 1 }}>\n {'Write variants with AI'}\n </Typography>\n <Link\n href={variantsHelp.href}\n target=\"_blank\"\n rel=\"noopener\"\n variant=\"caption\"\n title={variantsHelp.excerpt}\n >\n {'How it works'}\n </Link>\n </Stack>\n <TextField\n size=\"small\"\n multiline\n minRows={2}\n label={`The ${noun} copy under test`}\n placeholder={\n target === 'email'\n ? 'The subject line and message as they stand'\n : 'The headline and copy as they stand on the page'\n }\n helperText={\n 'Variants are written from this. The first one it writes is this copy unchanged, so the test has a control.'\n }\n value={subject}\n onChange={(event) => {\n setTyped(true)\n setSubject(event.target.value.slice(0, AI_EXPERIMENT_SUBJECT_MAX_CHARS))\n }}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={busy || !orgId || !subject.trim()}\n onClick={write}\n >\n {run.job && !busy ? 'Write them again' : 'Write variants'}\n </Button>\n {busy ? <CircularProgress size={16} aria-label=\"Writing variants\" /> : null}\n </Stack>\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n {problem ? (\n <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>\n {problem}\n </Alert>\n ) : null}\n\n {proposal ? (\n <Stack spacing={1} aria-label=\"Proposed variants\">\n {proposal.goal ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Written to move: ${proposal.goal}`}\n </Typography>\n ) : null}\n {proposal.variants.map((variant, index) => (\n <Box\n key={`${index}:${variant.name}`}\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1 }}\n >\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">\n {variant.name || `Variant ${index + 1}`}\n </Typography>\n {variant.subject ? (\n <Typography variant=\"body2\">{`Subject: ${variant.subject}`}</Typography>\n ) : null}\n {variant.preheader ? (\n <Typography variant=\"body2\">{`Preheader: ${variant.preheader}`}</Typography>\n ) : null}\n {variant.headline ? (\n <Typography variant=\"body2\">{variant.headline}</Typography>\n ) : null}\n {variant.body ? (\n <Typography\n variant=\"body2\"\n sx={{ whiteSpace: 'pre-line', wordBreak: 'break-word' }}\n >\n {variant.body}\n </Typography>\n ) : null}\n {variant.rationale ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {variant.rationale}\n </Typography>\n ) : null}\n </Stack>\n </Box>\n ))}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {aiExperimentApplyCopy(target)}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={applied === run.job?.id}\n onClick={apply}\n >\n {'Put into the variants'}\n </Button>\n </Stack>\n {applied === run.job?.id ? (\n <Alert severity=\"success\">\n {'In the variants above. Review them, then Save the experiment — or Cancel to leave it as it was.'}\n </Alert>\n ) : null}\n </Stack>\n ) : null}\n </Stack>\n </Box>\n )\n}\nAiExperimentVariantsCard.displayName = 'AiExperimentVariantsCard'\n\n/** One arm, drawn the same way whatever the figures say about it. */\nfunction ArmRow({\n arm,\n named,\n}: {\n arm: AiExperimentArm\n named: boolean\n}) {\n return (\n <TableRow>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{arm.id}</Typography>\n {named ? <Chip size=\"small\" color=\"success\" label=\"Ahead\" /> : null}\n </Stack>\n </TableCell>\n <TableCell>{arm.exposures}</TableCell>\n <TableCell>{arm.conversions}</TableCell>\n <TableCell>{rateText(arm.rate)}</TableCell>\n <TableCell>{liftText(arm.lift)}</TableCell>\n <TableCell>{rateText(arm.confidence)}</TableCell>\n </TableRow>\n )\n}\n\n/**\n * The explanation, drawn to the verdict rather than to the numbers.\n *\n * The chip and the caveat come FIRST, above the prose: a reader who stops\n * after one line must stop on what the result settled, not on a sentence\n * about one variant. No row is marked unless the verdict names an arm, and\n * `aiExperimentArmIsNamed` is asked per row rather than `winnerId` compared\n * directly, so a stored winner beside an undecided verdict marks nothing.\n */\nexport function AiExperimentExplanation({\n explanation,\n}: {\n explanation: AiExperimentExplanationProposal\n}) {\n const display = aiExperimentVerdictDisplay(explanation.verdict)\n return (\n <Stack spacing={1} aria-label=\"What this result shows\">\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip size=\"small\" color={display.tone} label={display.status} />\n {display.namesAnArm ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Called at ${explanation.threshold}% confidence.`}\n </Typography>\n ) : null}\n </Stack>\n {display.caveat ? (\n <Typography variant=\"body2\">{display.caveat}</Typography>\n ) : null}\n <Typography variant=\"body2\" sx={{ wordBreak: 'break-word' }}>\n {explanation.headline}\n </Typography>\n {explanation.points.map((point) => (\n <Typography key={point} variant=\"body2\" color=\"text.secondary\">\n {point}\n </Typography>\n ))}\n {explanation.arms.length ? (\n <ScrollTable size=\"small\" aria-label=\"The figures this reads\">\n <TableHead>\n <TableRow>\n <TableCell>{'Variant'}</TableCell>\n <TableCell>{'Shown'}</TableCell>\n <TableCell>{'Conversions'}</TableCell>\n <TableCell>{'Rate'}</TableCell>\n <TableCell>{'Lift'}</TableCell>\n <TableCell>{'Confidence'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {explanation.arms.map((arm) => (\n <ArmRow\n key={arm.id}\n arm={arm}\n named={aiExperimentArmIsNamed(explanation, arm.id)}\n />\n ))}\n </TableBody>\n </ScrollTable>\n ) : null}\n {explanation.next ? (\n <Typography variant=\"body2\">{explanation.next}</Typography>\n ) : null}\n </Stack>\n )\n}\nAiExperimentExplanation.displayName = 'AiExperimentExplanation'\n\n/**\n * \"Explain this result\", below one test's figures. It starts an `experiment`\n * job, which reads the same figures the dialog above it shows, decides what\n * they support in code, and asks a model only for the words.\n */\nexport function AiExperimentResultCard(props: AiExperimentResultCardProps) {\n const { hostId, test } = props\n const orgId = useHostOrgId(hostId) ?? undefined\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, EXPLAIN_FAILED_COPY)\n const explanation = useMemo(\n () => readAiExperimentExplanation(proposalOf(run.job)),\n [run.job],\n )\n const problem =\n aiJobProblem(run.job, EXPLAIN_FAILED_COPY) ??\n (run.job?.status === 'done' && !explanation ? EXPLAIN_FAILED_COPY : null)\n\n if (verdict !== 'ready') return null\n\n const busy = run.starting || run.running\n const resultHelp = pluginDocsHelp('aiExperiments', {\n anchor: '#the-verdict',\n excerpt:\n 'What the figures support is worked out from the counts before a model is asked anything, and the explanation is held to it — so an undecided test reads as undecided and no variant is named.',\n })\n const explain = () => {\n if (!orgId || !test) return\n void run.start({\n orgId,\n hostId,\n kind: 'experiment',\n brief: `Explain the result of the A/B test “${test}” in plain words.`,\n inputs: {\n task: 'explain',\n test,\n ...(props.experimentId ? { experimentId: props.experimentId } : {}),\n },\n })\n }\n\n return (\n <Box\n sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5, mt: 2 }}\n aria-label=\"Explain this result with AI\"\n >\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {'Read these figures in plain words, including whether they settle anything yet.'}\n </Typography>\n <Link\n href={resultHelp.href}\n target=\"_blank\"\n rel=\"noopener\"\n variant=\"caption\"\n title={resultHelp.excerpt}\n >\n {'How it works'}\n </Link>\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={busy || !orgId || !test}\n onClick={explain}\n >\n {run.job && !busy ? 'Ask again' : 'Explain this result'}\n </Button>\n </Stack>\n {busy ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={16} aria-label=\"Reading the result\" />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Reading the result.'}\n </Typography>\n </Stack>\n ) : null}\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n {problem ? (\n <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>\n {problem}\n </Alert>\n ) : null}\n {explanation ? <AiExperimentExplanation explanation={explanation} /> : null}\n </Stack>\n </Box>\n )\n}\nAiExperimentResultCard.displayName = 'AiExperimentResultCard'\n"],"names":["pluginDocsHelp","ScrollTable","useHostOrgId","useUser","Alert","Box","Button","Chip","CircularProgress","Link","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useEffect","useMemo","useState","AI_EXPERIMENT_SUBJECT_MAX_CHARS","aiExperimentArmIsNamed","aiExperimentVerdictDisplay","readAiExperimentExplanation","readAiExperimentVariants","aiJobProblem","useAiJobRun","useAiJobsVerdict","VARIANTS_FAILED_COPY","EXPLAIN_FAILED_COPY","TARGET_NOUN","screen","section","email","proposalOf","job","status","outputs","find","output","resource","proposal","rateText","value","liftText","aiExperimentSubjectFrom","target","variants","control","subject","body","filter","Boolean","join","aiExperimentVariantDrafts","map","variant","index","proposed","id","name","aiExperimentApplyCopy","AiExperimentVariantsCard","props","run","hostId","proposeVariants","orgId","undefined","data","user","verdict","prefill","setSubject","applied","setApplied","typed","setTyped","problem","busy","starting","running","noun","variantsHelp","anchor","excerpt","write","trim","start","kind","brief","inputs","task","slice","goal","experimentId","apply","sx","border","borderColor","borderRadius","p","aria-label","spacing","direction","alignItems","flexGrow","href","rel","title","size","multiline","minRows","label","placeholder","helperText","onChange","event","disabled","onClick","notice","severity","color","preheader","headline","whiteSpace","wordBreak","rationale","displayName","ArmRow","arm","named","exposures","conversions","rate","lift","confidence","AiExperimentExplanation","explanation","display","tone","namesAnArm","threshold","caveat","points","point","arms","length","next","AiExperimentResultCard","test","resultHelp","explain","mt"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsoleAutomationEditorZoneProps, ConsoleAutomationRunZoneProps } from '
|
|
1
|
+
import type { ConsoleAutomationEditorZoneProps, ConsoleAutomationRunZoneProps } from './ai-automation-zones';
|
|
2
2
|
/**
|
|
3
3
|
* "Explain it" in the editor of a saved automation, and "Why did this fail?"
|
|
4
4
|
* on a failed run in its run history (AGL-2919), through the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-explain-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport type {\n ConsoleAutomationEditorZoneProps,\n ConsoleAutomationRunZoneProps,\n} from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useState } from 'react'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict, type AiJobRun } from './use-ai-job-run'\n\n/**\n * \"Explain it\" in the editor of a saved automation, and \"Why did this fail?\"\n * on a failed run in its run history (AGL-2919), through the\n * `automationEditor` and `automationRun` zones. Each starts a `workflow` job\n * that reads the automation as it is saved — and, for a run, what the run\n * history recorded — and answers in plain words. Nothing is changed.\n */\n\nexport const AI_AUTOMATION_EXPLAIN_COPY = {\n explain: {\n action: 'Explain it',\n intro: 'Get a plain-words account of what this automation does, and anything in it worth checking.',\n saved: 'It reads the automation as it is saved, not changes you have not saved yet.',\n running: 'Reading the automation. This can take a few minutes.',\n again: 'Explain it again',\n failed: 'The automation could not be explained. Try again.',\n brief: (name: string) => `Explain what “${name}” does, and anything in it worth checking.`,\n },\n diagnose: {\n action: 'Why did this fail?',\n title: 'Why this run failed',\n running: 'Reading the run and the automation. This can take a few minutes.',\n again: 'Ask again',\n failed: 'The run could not be explained. Try again.',\n brief: (name: string) => `Why did a run of “${name}” fail, and how can it be fixed?`,\n },\n} as const\n\n/** The explanation a finished job wrote. */\nfunction explanationOf(job: AiJobSummary | null): string | null {\n if (job?.status !== 'done') return null\n return job.outputs.find((output) => output.resource === 'text' && output.text)?.text ?? null\n}\n\n/** Progress, the explanation, or why there is none. */\nfunction Answer({ run, running, failed }: { run: AiJobRun; running: string; failed: string }) {\n const text = explanationOf(run.job)\n const problem =\n aiJobProblem(run.job, failed) ?? (run.job?.status === 'done' && !text ? failed : null)\n return (\n <>\n {run.running ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={16} aria-label=\"Explaining\" />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {running}\n </Typography>\n </Stack>\n ) : null}\n {text ? (\n <Typography variant=\"body2\" component=\"div\" sx={{ whiteSpace: 'pre-line', wordBreak: 'break-word' }}>\n {text}\n </Typography>\n ) : null}\n {problem ? <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n </>\n )\n}\n\n/** \"Explain it\", at the top of the editor of a saved action or workflow. */\nexport function AiExplainAutomation({ hostId, orgId, target }: ConsoleAutomationEditorZoneProps) {\n const copy = AI_AUTOMATION_EXPLAIN_COPY.explain\n const help = pluginDocsHelp('aiAutomations', { anchor: '#explain' })\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, copy.failed)\n\n if (verdict !== 'ready') return null\n const asked = Boolean(run.job || run.notice)\n const busy = run.starting || run.running\n\n const explain = () => {\n if (!orgId) return\n void run.start({\n orgId,\n hostId,\n kind: 'workflow',\n brief: copy.brief(target.name),\n inputs: { mode: 'explain', targetType: target.type, targetId: target.id },\n })\n }\n\n return (\n <Box sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }} aria-label=\"Explain this automation\">\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {asked ? copy.saved : copy.intro}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n <Button size=\"small\" variant=\"outlined\" disabled={busy || !orgId} onClick={explain}>\n {asked && !busy ? copy.again : copy.action}\n </Button>\n </Stack>\n <Answer run={run} running={copy.running} failed={copy.failed} />\n </Stack>\n </Box>\n )\n}\n\n/** \"Why did this fail?\", on a failed run: the answer opens over the run history. */\nexport function AiExplainRunFailure({ hostId, orgId, target, runId }: ConsoleAutomationRunZoneProps) {\n const copy = AI_AUTOMATION_EXPLAIN_COPY.diagnose\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, copy.failed)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n const busy = run.starting || run.running\n\n const ask = () => {\n if (!orgId) return\n void run.start({\n orgId,\n hostId,\n kind: 'workflow',\n brief: copy.brief(target.name),\n inputs: { mode: 'diagnose', targetType: target.type, targetId: target.id, runId },\n })\n }\n\n const openAndAsk = () => {\n setOpen(true)\n // A run already explained, or being explained, is shown again rather than asked again.\n if (!run.job) ask()\n }\n\n const close = () => {\n setOpen(false)\n // A refusal is not kept: asking again later should ask the route again.\n if (run.notice) run.reset()\n }\n\n return (\n <>\n <Button size=\"small\" variant=\"text\" sx={{ px: 0.5, minWidth: 0 }} onClick={openAndAsk}>\n {copy.action}\n </Button>\n <Dialog open={open} onClose={close} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <Answer run={run} running={copy.running} failed={copy.failed} />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Close'}</Button>\n {!busy && (run.notice || aiJobProblem(run.job, copy.failed)) ? (\n <Button variant=\"outlined\" onClick={ask} disabled={!orgId}>\n {copy.again}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n </>\n )\n}\n"],"names":["pluginDocsHelp","useUser","Alert","Box","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","Typography","useState","aiJobProblem","useAiJobRun","useAiJobsVerdict","AI_AUTOMATION_EXPLAIN_COPY","explain","action","intro","saved","running","again","failed","brief","name","diagnose","title","explanationOf","job","status","outputs","find","output","resource","text","Answer","run","problem","direction","spacing","sx","alignItems","size","aria-label","variant","color","component","whiteSpace","wordBreak","severity","notice","AiExplainAutomation","hostId","orgId","target","copy","help","anchor","data","user","verdict","asked","Boolean","busy","starting","start","kind","inputs","mode","targetType","type","targetId","id","border","borderColor","borderRadius","p","flexGrow","href","rel","excerpt","disabled","onClick","AiExplainRunFailure","runId","open","setOpen","ask","openAndAsk","close","reset","px","minWidth","onClose","fullWidth","maxWidth","pt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-explain-automation.component.tsx"],"sourcesContent":["'use client'\n\n/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Link,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useState } from 'react'\nimport type { AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobProblem, useAiJobRun, useAiJobsVerdict, type AiJobRun } from './use-ai-job-run'\nimport type {\n ConsoleAutomationEditorZoneProps,\n ConsoleAutomationRunZoneProps,\n} from './ai-automation-zones'\n\n/**\n * \"Explain it\" in the editor of a saved automation, and \"Why did this fail?\"\n * on a failed run in its run history (AGL-2919), through the\n * `automationEditor` and `automationRun` zones. Each starts a `workflow` job\n * that reads the automation as it is saved — and, for a run, what the run\n * history recorded — and answers in plain words. Nothing is changed.\n */\n\nexport const AI_AUTOMATION_EXPLAIN_COPY = {\n explain: {\n action: 'Explain it',\n intro: 'Get a plain-words account of what this automation does, and anything in it worth checking.',\n saved: 'It reads the automation as it is saved, not changes you have not saved yet.',\n running: 'Reading the automation. This can take a few minutes.',\n again: 'Explain it again',\n failed: 'The automation could not be explained. Try again.',\n brief: (name: string) => `Explain what “${name}” does, and anything in it worth checking.`,\n },\n diagnose: {\n action: 'Why did this fail?',\n title: 'Why this run failed',\n running: 'Reading the run and the automation. This can take a few minutes.',\n again: 'Ask again',\n failed: 'The run could not be explained. Try again.',\n brief: (name: string) => `Why did a run of “${name}” fail, and how can it be fixed?`,\n },\n} as const\n\n/** The explanation a finished job wrote. */\nfunction explanationOf(job: AiJobSummary | null): string | null {\n if (job?.status !== 'done') return null\n return job.outputs.find((output) => output.resource === 'text' && output.text)?.text ?? null\n}\n\n/** Progress, the explanation, or why there is none. */\nfunction Answer({ run, running, failed }: { run: AiJobRun; running: string; failed: string }) {\n const text = explanationOf(run.job)\n const problem =\n aiJobProblem(run.job, failed) ?? (run.job?.status === 'done' && !text ? failed : null)\n return (\n <>\n {run.running ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <CircularProgress size={16} aria-label=\"Explaining\" />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {running}\n </Typography>\n </Stack>\n ) : null}\n {text ? (\n <Typography variant=\"body2\" component=\"div\" sx={{ whiteSpace: 'pre-line', wordBreak: 'break-word' }}>\n {text}\n </Typography>\n ) : null}\n {problem ? <Alert severity={run.job?.status === 'failed' ? 'error' : 'warning'}>{problem}</Alert> : null}\n {run.notice ? <Alert severity=\"warning\">{run.notice}</Alert> : null}\n </>\n )\n}\n\n/** \"Explain it\", at the top of the editor of a saved action or workflow. */\nexport function AiExplainAutomation({ hostId, orgId, target }: ConsoleAutomationEditorZoneProps) {\n const copy = AI_AUTOMATION_EXPLAIN_COPY.explain\n const help = pluginDocsHelp('aiAutomations', { anchor: '#explain' })\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, copy.failed)\n\n if (verdict !== 'ready') return null\n const asked = Boolean(run.job || run.notice)\n const busy = run.starting || run.running\n\n const explain = () => {\n if (!orgId) return\n void run.start({\n orgId,\n hostId,\n kind: 'workflow',\n brief: copy.brief(target.name),\n inputs: { mode: 'explain', targetType: target.type, targetId: target.id },\n })\n }\n\n return (\n <Box sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }} aria-label=\"Explain this automation\">\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {asked ? copy.saved : copy.intro}{' '}\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Typography>\n <Button size=\"small\" variant=\"outlined\" disabled={busy || !orgId} onClick={explain}>\n {asked && !busy ? copy.again : copy.action}\n </Button>\n </Stack>\n <Answer run={run} running={copy.running} failed={copy.failed} />\n </Stack>\n </Box>\n )\n}\n\n/** \"Why did this fail?\", on a failed run: the answer opens over the run history. */\nexport function AiExplainRunFailure({ hostId, orgId, target, runId }: ConsoleAutomationRunZoneProps) {\n const copy = AI_AUTOMATION_EXPLAIN_COPY.diagnose\n const { data: user } = useUser()\n const verdict = useAiJobsVerdict(user, orgId)\n const run = useAiJobRun(user, copy.failed)\n const [open, setOpen] = useState(false)\n\n if (verdict !== 'ready') return null\n const busy = run.starting || run.running\n\n const ask = () => {\n if (!orgId) return\n void run.start({\n orgId,\n hostId,\n kind: 'workflow',\n brief: copy.brief(target.name),\n inputs: { mode: 'diagnose', targetType: target.type, targetId: target.id, runId },\n })\n }\n\n const openAndAsk = () => {\n setOpen(true)\n // A run already explained, or being explained, is shown again rather than asked again.\n if (!run.job) ask()\n }\n\n const close = () => {\n setOpen(false)\n // A refusal is not kept: asking again later should ask the route again.\n if (run.notice) run.reset()\n }\n\n return (\n <>\n <Button size=\"small\" variant=\"text\" sx={{ px: 0.5, minWidth: 0 }} onClick={openAndAsk}>\n {copy.action}\n </Button>\n <Dialog open={open} onClose={close} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <Answer run={run} running={copy.running} failed={copy.failed} />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Close'}</Button>\n {!busy && (run.notice || aiJobProblem(run.job, copy.failed)) ? (\n <Button variant=\"outlined\" onClick={ask} disabled={!orgId}>\n {copy.again}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n </>\n )\n}\n"],"names":["pluginDocsHelp","useUser","Alert","Box","Button","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","Link","Stack","Typography","useState","aiJobProblem","useAiJobRun","useAiJobsVerdict","AI_AUTOMATION_EXPLAIN_COPY","explain","action","intro","saved","running","again","failed","brief","name","diagnose","title","explanationOf","job","status","outputs","find","output","resource","text","Answer","run","problem","direction","spacing","sx","alignItems","size","aria-label","variant","color","component","whiteSpace","wordBreak","severity","notice","AiExplainAutomation","hostId","orgId","target","copy","help","anchor","data","user","verdict","asked","Boolean","busy","starting","start","kind","inputs","mode","targetType","type","targetId","id","border","borderColor","borderRadius","p","flexGrow","href","rel","excerpt","disabled","onClick","AiExplainRunFailure","runId","open","setOpen","ask","openAndAsk","close","reset","px","minWidth","onClose","fullWidth","maxWidth","pt"],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
import { type AiBuildPlanEmbed } from '../model/ai-build-plan';
|
|
17
18
|
import type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types';
|
|
18
19
|
/**
|
|
19
20
|
* A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer:
|
|
@@ -42,5 +43,12 @@ export interface AiJobPlanProps {
|
|
|
42
43
|
* Grid's layout as written, and the names of what it sets and holds.
|
|
43
44
|
*/
|
|
44
45
|
export declare function aiJobReviewDetails(review: AiJobReview | null): string[];
|
|
46
|
+
/**
|
|
47
|
+
* A third-party player the plan lists, with what it costs the page, read
|
|
48
|
+
* before the member confirms it (rule 16, AGL-3433). The cost is the
|
|
49
|
+
* platform's sentence, never the model's: the player is framed lazily, so its
|
|
50
|
+
* host's code loads when a visitor reaches it, whether or not they press play.
|
|
51
|
+
*/
|
|
52
|
+
export declare function aiPlanEmbedLine(embed: AiBuildPlanEmbed, named: (ref: string) => string): string;
|
|
45
53
|
export declare function AiJobPlan({ job, onResume, busy, staff, }: AiJobPlanProps): JSX.Element | null;
|
|
46
54
|
export default AiJobPlan;
|