@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
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import { AI_BUILD_PLAN_CREATION_NOUNS, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
17
|
+
*/ import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
18
18
|
import { aiJobPlanCreditEstimate } from "../model/ai-site-job.js";
|
|
19
19
|
import { Box, Button, Collapse, Stack, Typography } from "@mui/material";
|
|
20
20
|
import { useState } from "react";
|
|
@@ -52,7 +52,19 @@ import { useState } from "react";
|
|
|
52
52
|
}
|
|
53
53
|
return lines;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* A third-party player the plan lists, with what it costs the page, read
|
|
57
|
+
* before the member confirms it (rule 16, AGL-3433). The cost is the
|
|
58
|
+
* platform's sentence, never the model's: the player is framed lazily, so its
|
|
59
|
+
* host's code loads when a visitor reaches it, whether or not they press play.
|
|
60
|
+
*/ export function aiPlanEmbedLine(embed, named) {
|
|
61
|
+
const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host];
|
|
62
|
+
const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where;
|
|
63
|
+
const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste';
|
|
64
|
+
return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`;
|
|
65
|
+
}
|
|
55
66
|
export function AiJobPlan({ job, onResume, busy = false, staff = false }) {
|
|
67
|
+
var _plan_embeds;
|
|
56
68
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
57
69
|
const { plan, review } = job;
|
|
58
70
|
if (!plan && !review) return null;
|
|
@@ -88,7 +100,7 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false }) {
|
|
|
88
100
|
role: "listitem",
|
|
89
101
|
children: [
|
|
90
102
|
"Reuses the ",
|
|
91
|
-
entry.kind,
|
|
103
|
+
entry.kind === 'screen' ? 'page' : entry.kind,
|
|
92
104
|
" ",
|
|
93
105
|
named(entry.id),
|
|
94
106
|
" — ",
|
|
@@ -113,7 +125,7 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false }) {
|
|
|
113
125
|
variant: "body2",
|
|
114
126
|
role: "listitem",
|
|
115
127
|
children: [
|
|
116
|
-
"Builds the
|
|
128
|
+
"Builds the page ",
|
|
117
129
|
screen.title,
|
|
118
130
|
" at ",
|
|
119
131
|
screen.slug,
|
|
@@ -121,7 +133,12 @@ export function AiJobPlan({ job, onResume, busy = false, staff = false }) {
|
|
|
121
133
|
screen.layout ? ` in ${named(screen.layout)}` : '',
|
|
122
134
|
screen.sections.length ? `: ${screen.sections.map((section)=>section.name).join(', ')}` : ''
|
|
123
135
|
]
|
|
124
|
-
}, `screen-${index}`))
|
|
136
|
+
}, `screen-${index}`)),
|
|
137
|
+
((_plan_embeds = plan.embeds) != null ? _plan_embeds : []).map((embed, index)=>/*#__PURE__*/ _jsx(Typography, {
|
|
138
|
+
variant: "body2",
|
|
139
|
+
role: "listitem",
|
|
140
|
+
children: aiPlanEmbedLine(embed, named)
|
|
141
|
+
}, `embed-${index}`))
|
|
125
142
|
]
|
|
126
143
|
}),
|
|
127
144
|
(review == null ? void 0 : review.reason) === 'doctrine' && review.findings.length > 0 && /*#__PURE__*/ _jsx(Box, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_BUILD_PLAN_CREATION_NOUNS, isAiPlanNewRef } from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobPlanCreditEstimate } from '../model/ai-site-job'\nimport { Box, Button, Collapse, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer:\n * what the job reuses from the site, what it creates and why, the screens it\n * builds — and, while the job waits for a person, the one button that\n * confirms the plan or tries a refused step again. The drawer owns the\n * request; this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /** Confirms the plan, or tries the refused step again. */\n onResume: (job: AiJobSummary) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n const waiting = job.status === 'needs_review' && review !== null\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one — the plan's own passes at the nominal credits a step\n // holds, where what a step really costs is its model's tokens. A page job\n // counts the creations it builds before its page (AGL-3031).\n const estimate =\n plan && waiting && review.reason === 'plan' ? aiJobPlanCreditEstimate(job.kind, plan) : 0\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n Builds the screen {screen.title} at {screen.slug}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n </Stack>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {estimate > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: about ${estimate.toLocaleString('en-US')} credits. What it costs is what its steps spend.`}\n </Typography>\n )}\n {waiting && (\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={busy}\n onClick={() => onResume(job)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","isAiPlanNewRef","aiJobPlanCreditEstimate","Box","Button","Collapse","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","AiJobPlan","job","onResume","busy","staff","detailsOpen","setDetailsOpen","plan","details","named","ref","slice","labels","waiting","status","estimate","kind","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","title","slug","layout","sections","section","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","display","toLocaleString","disabled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-job-plan.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATION_NOUNS,\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n isAiPlanNewRef,\n type AiBuildPlanEmbed,\n} from '../model/ai-build-plan'\nimport type { AiJobReview, AiJobSummary } from '../model/ai-jobs.types'\nimport { aiJobPlanCreditEstimate } from '../model/ai-site-job'\nimport { Box, Button, Collapse, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\n\n/**\n * A job's plan and what it waits for (AGL-2935), inside the AI jobs drawer:\n * what the job reuses from the site, what it creates and why, the screens it\n * builds — and, while the job waits for a person, the one button that\n * confirms the plan or tries a refused step again. The drawer owns the\n * request; this renders.\n */\nexport interface AiJobPlanProps {\n job: AiJobSummary\n /** Confirms the plan, or tries the refused step again. */\n onResume: (job: AiJobSummary) => void\n /** A resume for this job is in flight. */\n busy?: boolean\n /**\n * The viewer is staff (AGL-3078), who can open where a refused answer broke\n * its rules and the outline of those parts, to tell a shape the rules should\n * allow from an answer that ignored its re-ask. A member reads the findings.\n */\n staff?: boolean\n}\n\n/**\n * Where a refused answer broke its rules, and the outline of those parts, as\n * lines staff read (AGL-3078): each finding with the nodes or plan entries it\n * names, then each outlined node indented by its depth, with its element, a\n * Grid's layout as written, and the names of what it sets and holds.\n */\nexport function aiJobReviewDetails(review: AiJobReview | null): string[] {\n if (review?.reason !== 'doctrine') return []\n const lines = review.findings.flatMap((finding) => {\n const where = finding.nodeIds?.length\n ? `nodes ${finding.nodeIds.join(', ')}`\n : finding.paths?.length\n ? `at ${finding.paths.join(', ')}`\n : null\n return where ? [`${finding.rule === null ? '' : `Rule ${finding.rule} `}${finding.code}: ${where}`] : []\n })\n for (const node of review.outline ?? []) {\n lines.push(\n [\n `${' '.repeat(node.depth)}${node.id} ${node.componentId}`,\n ...Object.entries(node.grid ?? {}).map(([name, value]) => `${name}=${JSON.stringify(value)}`),\n ...(node.props.length ? [`props ${node.props.join(', ')}`] : []),\n ...(node.sx?.length ? [`sx ${node.sx.join(', ')}`] : []),\n ...(node.children.length ? [`holds ${node.children.join(', ')}`] : []),\n ].join(' · '),\n )\n }\n return lines\n}\n\n/**\n * A third-party player the plan lists, with what it costs the page, read\n * before the member confirms it (rule 16, AGL-3433). The cost is the\n * platform's sentence, never the model's: the player is framed lazily, so its\n * host's code loads when a visitor reaches it, whether or not they press play.\n */\nexport function aiPlanEmbedLine(\n embed: AiBuildPlanEmbed,\n named: (ref: string) => string,\n): string {\n const host = AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]\n const where = isAiPlanNewRef(embed.where) ? `the component ${named(embed.where)}` : embed.where\n const link = embed.url ? ` playing ${embed.url}` : ', its link left for you to paste'\n return `Embeds a ${host} player on ${where}${link}, as you asked (“${embed.asked}”). It loads ${host}’s own code when a visitor reaches it, whether or not they press play.`\n}\n\nexport function AiJobPlan({\n job,\n onResume,\n busy = false,\n staff = false,\n}: AiJobPlanProps): JSX.Element | null {\n const [detailsOpen, setDetailsOpen] = useState(false)\n const { plan, review } = job\n if (!plan && !review) return null\n const details = staff ? aiJobReviewDetails(review) : []\n /** A reference as a person reads it: the record's name, or what the plan creates. */\n const named = (ref: string | null): string =>\n !ref\n ? ''\n : isAiPlanNewRef(ref)\n ? ref.slice('new:'.length)\n : (plan?.labels[ref] ?? ref)\n const waiting = job.status === 'needs_review' && review !== null\n // The guard rail (AGL-2911): what the plan is estimated to cost is read\n // before it is confirmed, not after it has been spent. An estimate, and\n // said to be one — the plan's own passes at the nominal credits a step\n // holds, where what a step really costs is its model's tokens. A page job\n // counts the creations it builds before its page (AGL-3031).\n const estimate =\n plan && waiting && review.reason === 'plan' ? aiJobPlanCreditEstimate(job.kind, plan) : 0\n return (\n <Box sx={{ mt: 1 }}>\n {plan && (\n <Stack spacing={0.5} role=\"list\" aria-label=\"Plan\">\n <Typography variant=\"caption\" color=\"text.secondary\">\n {plan.status === 'confirmed' ? 'Confirmed plan' : 'Proposed plan'}\n </Typography>\n {plan.reuse.map((entry, index) => (\n <Typography key={`reuse-${index}`} variant=\"body2\" role=\"listitem\">\n Reuses the {entry.kind === 'screen' ? 'page' : entry.kind} {named(entry.id)} — {entry.purpose}\n </Typography>\n ))}\n {plan.create.map((entry, index) => (\n <Typography key={`create-${index}`} variant=\"body2\" role=\"listitem\">\n Creates the {AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun} {entry.name}\n {entry.duplicateOf\n ? `, from a copy of ${named(entry.duplicateOf)}`\n : ''}{' '}\n — {entry.why}\n </Typography>\n ))}\n {plan.screens.map((screen, index) => (\n <Typography key={`screen-${index}`} variant=\"body2\" role=\"listitem\">\n Builds the page {screen.title} at {screen.slug}\n {screen.duplicateOf\n ? `, from a copy of ${named(screen.duplicateOf)}`\n : ''}\n {screen.layout ? ` in ${named(screen.layout)}` : ''}\n {screen.sections.length\n ? `: ${screen.sections.map((section) => section.name).join(', ')}`\n : ''}\n </Typography>\n ))}\n {(plan.embeds ?? []).map((embed, index) => (\n <Typography key={`embed-${index}`} variant=\"body2\" role=\"listitem\">\n {aiPlanEmbedLine(embed, named)}\n </Typography>\n ))}\n </Stack>\n )}\n {review?.reason === 'doctrine' && review.findings.length > 0 && (\n <Box\n component=\"ul\"\n sx={{ my: 0.5, pl: 2.5 }}\n aria-label=\"Building rules broken\"\n >\n {review.findings.map((finding, index) => (\n <Typography\n key={`finding-${index}`}\n component=\"li\"\n variant=\"caption\"\n >\n {finding.message}\n </Typography>\n ))}\n </Box>\n )}\n {details.length > 0 && (\n <Box sx={{ mt: 0.5 }}>\n <Button\n size=\"small\"\n aria-expanded={detailsOpen}\n onClick={() => setDetailsOpen((prior) => !prior)}\n >\n {detailsOpen ? 'Hide what was refused' : 'Show what was refused'}\n </Button>\n <Collapse in={detailsOpen} unmountOnExit>\n <Typography\n variant=\"caption\"\n component=\"pre\"\n aria-label=\"What was refused\"\n sx={{ m: 0, fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto' }}\n >\n {details.join('\\n')}\n </Typography>\n </Collapse>\n </Box>\n )}\n {estimate > 0 && (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: 'block', mt: 1 }}\n >\n {`Estimated cost: about ${estimate.toLocaleString('en-US')} credits. What it costs is what its steps spend.`}\n </Typography>\n )}\n {waiting && (\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={busy}\n onClick={() => onResume(job)}\n sx={{ mt: 1 }}\n >\n {review.reason === 'plan' ? 'Confirm plan' : 'Try again'}\n </Button>\n )}\n </Box>\n )\n}\n\nexport default AiJobPlan\n"],"names":["AI_BUILD_PLAN_CREATION_NOUNS","AI_BUILD_PLAN_EMBED_HOST_NAMES","isAiPlanNewRef","aiJobPlanCreditEstimate","Box","Button","Collapse","Stack","Typography","useState","aiJobReviewDetails","review","reason","lines","findings","flatMap","finding","where","nodeIds","length","join","paths","rule","code","node","outline","push","repeat","depth","id","componentId","Object","entries","grid","map","name","value","JSON","stringify","props","sx","children","aiPlanEmbedLine","embed","named","host","link","url","asked","AiJobPlan","job","onResume","busy","staff","plan","detailsOpen","setDetailsOpen","details","ref","slice","labels","waiting","status","estimate","kind","mt","spacing","role","aria-label","variant","color","reuse","entry","index","purpose","create","noun","duplicateOf","why","screens","screen","title","slug","layout","sections","section","embeds","component","my","pl","message","size","aria-expanded","onClick","prior","in","unmountOnExit","m","fontFamily","whiteSpace","overflowX","display","toLocaleString","disabled"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConsoleDockZoneProps } from '@aglyn/aglyn';
|
|
1
2
|
import { type ComponentProps } from 'react';
|
|
2
3
|
import { AiAssistProvider } from './ai-assist-provider.component';
|
|
3
4
|
import { AssistPanelComponent } from './assist-panel.component';
|
|
@@ -44,10 +45,19 @@ export declare function AiAssistProviderOnHost(props: ProviderProps): import("re
|
|
|
44
45
|
export declare namespace AiAssistProviderOnHost {
|
|
45
46
|
var displayName: string;
|
|
46
47
|
}
|
|
47
|
-
type PanelProps = Omit<ComponentProps<typeof AssistPanelComponent>, 'aiPermissions'> & {
|
|
48
|
+
type PanelProps = Omit<ComponentProps<typeof AssistPanelComponent>, 'aiPermissions' | 'assistVisible' | 'assistStaffPreview' | 'generativeVisible'> & {
|
|
48
49
|
permissionsOnHost?: ShellPermissionsOnHost;
|
|
50
|
+
/**
|
|
51
|
+
* The shell's verdict for any release flag (the `consoleDock` zone's
|
|
52
|
+
* `releaseVerdict`): the assistant asks for its own two, so the shell names
|
|
53
|
+
* no AI flag.
|
|
54
|
+
*/
|
|
55
|
+
releaseVerdict: ConsoleDockZoneProps['releaseVerdict'];
|
|
49
56
|
};
|
|
50
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The assistant, drawn in the console dock, holding on the shell's
|
|
59
|
+
* permission answer and asking the shell for its own release flags.
|
|
60
|
+
*/
|
|
51
61
|
export declare function AssistPanelOnHost(props: PanelProps): import("react").JSX.Element;
|
|
52
62
|
export declare namespace AssistPanelOnHost {
|
|
53
63
|
var displayName: string;
|
|
@@ -76,13 +76,22 @@ const HELD = {
|
|
|
76
76
|
}));
|
|
77
77
|
}
|
|
78
78
|
AiAssistProviderOnHost.displayName = 'AiAssistProviderOnHost';
|
|
79
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* The assistant, drawn in the console dock, holding on the shell's
|
|
81
|
+
* permission answer and asking the shell for its own release flags.
|
|
82
|
+
*/ export function AssistPanelOnHost(props) {
|
|
80
83
|
var _useAiPermissionsOnHost;
|
|
81
|
-
const { permissionsOnHost } = props, rest = _object_without_properties_loose(props, [
|
|
82
|
-
"permissionsOnHost"
|
|
84
|
+
const { permissionsOnHost, releaseVerdict } = props, rest = _object_without_properties_loose(props, [
|
|
85
|
+
"permissionsOnHost",
|
|
86
|
+
"releaseVerdict"
|
|
83
87
|
]);
|
|
84
88
|
const aiPermissions = (_useAiPermissionsOnHost = useAiPermissionsOnHost(permissionsOnHost)) != null ? _useAiPermissionsOnHost : HELD;
|
|
89
|
+
const assist = releaseVerdict('release_assist');
|
|
90
|
+
const generative = releaseVerdict('release_ai_generative');
|
|
85
91
|
return /*#__PURE__*/ _jsx(AssistPanelComponent, _extends({}, rest, {
|
|
92
|
+
assistVisible: assist.visible,
|
|
93
|
+
assistStaffPreview: assist.staffPreview,
|
|
94
|
+
generativeVisible: generative.visible,
|
|
86
95
|
aiPermissions: aiPermissions
|
|
87
96
|
}));
|
|
88
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-permissions-on-host.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 { useMemo, type ComponentProps } from 'react'\nimport { AI_PLUGIN_ID } from '../constants'\nimport { aiPermissionsOf } from '../model/ai-permissions'\nimport { AiAssistProvider } from './ai-assist-provider.component'\nimport { AssistPanelComponent } from './assist-panel.component'\n\n/**\n * What the console shell hands a provider and the assistant dock (AGL-2984):\n * the reader's verdict for every catalog key a plugin declared, on the site\n * in view, with `loaded` false until every read behind it has answered.\n */\nexport interface ShellPermissionsOnHost {\n loaded: boolean\n granted: Readonly<Record<string, boolean>>\n}\n\n/** The AI half of that answer, in the shape the AI surfaces hold on. */\nexport interface AiPermissionsAnswer {\n loaded: boolean\n use: boolean\n generate: boolean\n}\n\nconst HELD: AiPermissionsAnswer = { loaded: false, use: false, generate: false }\n\n/**\n * The shell's answer read for the two AI keys. `undefined` while the shell\n * has handed nothing, which the provider reads as pending; one object per\n * verdict, so the callbacks keyed on it are not rebuilt on every render.\n */\nexport function useAiPermissionsOnHost(\n answer: ShellPermissionsOnHost | undefined,\n): AiPermissionsAnswer | undefined {\n const present = answer !== undefined\n const loaded = answer?.loaded === true\n const verdict = aiPermissionsOf(loaded ? answer?.granted : null)\n const use = verdict['ai.use']\n const generate = verdict['ai.generate']\n return useMemo(\n () => (present ? { loaded, use, generate } : undefined),\n [present, loaded, use, generate],\n )\n}\n\ntype ProviderProps = Omit<ComponentProps<typeof AiAssistProvider>, 'aiPermissions'> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The plugins that run on the site in view, as the shell resolved them\n * (AGL-3028). Absent where the shell hands none, which reads as no site\n * narrowing at all.\n */\n enabledPluginIds?: readonly string[]\n}\n\n/**\n * Every AI door closed, as a settled answer: what the provider holds on a site\n * that switched AI off. One object, so the callbacks keyed on it are stable.\n */\nconst OFF_FOR_SITE: AiPermissionsAnswer = { loaded: true, use: false, generate: false }\n\n/**\n * The besigner copy assistant's provider, holding on the shell's permission\n * answer.\n *\n * On a site with AI switched off (AGL-3028) it stays MOUNTED — it wraps every\n * console page, and a provider that came and went per site would remount the\n * whole tree beneath it — and opens nothing: a settled refusal publishes no\n * callback, so no control can reach its dialogs.\n */\nexport function AiAssistProviderOnHost(props: ProviderProps) {\n const { permissionsOnHost, enabledPluginIds, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost)\n const offForSite = Boolean(enabledPluginIds) && !enabledPluginIds?.includes(AI_PLUGIN_ID)\n return (\n <AiAssistProvider {...rest} aiPermissions={offForSite ? OFF_FOR_SITE : aiPermissions} />\n )\n}\nAiAssistProviderOnHost.displayName = 'AiAssistProviderOnHost'\n\ntype PanelProps = Omit
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-permissions-on-host.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { ConsoleDockZoneProps } from '@aglyn/aglyn'\nimport { useMemo, type ComponentProps } from 'react'\nimport { AI_PLUGIN_ID } from '../constants'\nimport { aiPermissionsOf } from '../model/ai-permissions'\nimport { AiAssistProvider } from './ai-assist-provider.component'\nimport { AssistPanelComponent } from './assist-panel.component'\n\n/**\n * What the console shell hands a provider and the assistant dock (AGL-2984):\n * the reader's verdict for every catalog key a plugin declared, on the site\n * in view, with `loaded` false until every read behind it has answered.\n */\nexport interface ShellPermissionsOnHost {\n loaded: boolean\n granted: Readonly<Record<string, boolean>>\n}\n\n/** The AI half of that answer, in the shape the AI surfaces hold on. */\nexport interface AiPermissionsAnswer {\n loaded: boolean\n use: boolean\n generate: boolean\n}\n\nconst HELD: AiPermissionsAnswer = { loaded: false, use: false, generate: false }\n\n/**\n * The shell's answer read for the two AI keys. `undefined` while the shell\n * has handed nothing, which the provider reads as pending; one object per\n * verdict, so the callbacks keyed on it are not rebuilt on every render.\n */\nexport function useAiPermissionsOnHost(\n answer: ShellPermissionsOnHost | undefined,\n): AiPermissionsAnswer | undefined {\n const present = answer !== undefined\n const loaded = answer?.loaded === true\n const verdict = aiPermissionsOf(loaded ? answer?.granted : null)\n const use = verdict['ai.use']\n const generate = verdict['ai.generate']\n return useMemo(\n () => (present ? { loaded, use, generate } : undefined),\n [present, loaded, use, generate],\n )\n}\n\ntype ProviderProps = Omit<ComponentProps<typeof AiAssistProvider>, 'aiPermissions'> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The plugins that run on the site in view, as the shell resolved them\n * (AGL-3028). Absent where the shell hands none, which reads as no site\n * narrowing at all.\n */\n enabledPluginIds?: readonly string[]\n}\n\n/**\n * Every AI door closed, as a settled answer: what the provider holds on a site\n * that switched AI off. One object, so the callbacks keyed on it are stable.\n */\nconst OFF_FOR_SITE: AiPermissionsAnswer = { loaded: true, use: false, generate: false }\n\n/**\n * The besigner copy assistant's provider, holding on the shell's permission\n * answer.\n *\n * On a site with AI switched off (AGL-3028) it stays MOUNTED — it wraps every\n * console page, and a provider that came and went per site would remount the\n * whole tree beneath it — and opens nothing: a settled refusal publishes no\n * callback, so no control can reach its dialogs.\n */\nexport function AiAssistProviderOnHost(props: ProviderProps) {\n const { permissionsOnHost, enabledPluginIds, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost)\n const offForSite = Boolean(enabledPluginIds) && !enabledPluginIds?.includes(AI_PLUGIN_ID)\n return (\n <AiAssistProvider {...rest} aiPermissions={offForSite ? OFF_FOR_SITE : aiPermissions} />\n )\n}\nAiAssistProviderOnHost.displayName = 'AiAssistProviderOnHost'\n\ntype PanelProps = Omit<\n ComponentProps<typeof AssistPanelComponent>,\n 'aiPermissions' | 'assistVisible' | 'assistStaffPreview' | 'generativeVisible'\n> & {\n permissionsOnHost?: ShellPermissionsOnHost\n /**\n * The shell's verdict for any release flag (the `consoleDock` zone's\n * `releaseVerdict`): the assistant asks for its own two, so the shell names\n * no AI flag.\n */\n releaseVerdict: ConsoleDockZoneProps['releaseVerdict']\n}\n\n/**\n * The assistant, drawn in the console dock, holding on the shell's\n * permission answer and asking the shell for its own release flags.\n */\nexport function AssistPanelOnHost(props: PanelProps) {\n const { permissionsOnHost, releaseVerdict, ...rest } = props\n const aiPermissions = useAiPermissionsOnHost(permissionsOnHost) ?? HELD\n const assist = releaseVerdict('release_assist')\n const generative = releaseVerdict('release_ai_generative')\n return (\n <AssistPanelComponent\n {...rest}\n assistVisible={assist.visible}\n assistStaffPreview={assist.staffPreview}\n generativeVisible={generative.visible}\n aiPermissions={aiPermissions}\n />\n )\n}\nAssistPanelOnHost.displayName = 'AssistPanelOnHost'\n"],"names":["useMemo","AI_PLUGIN_ID","aiPermissionsOf","AiAssistProvider","AssistPanelComponent","HELD","loaded","use","generate","useAiPermissionsOnHost","answer","present","undefined","verdict","granted","OFF_FOR_SITE","AiAssistProviderOnHost","props","permissionsOnHost","enabledPluginIds","rest","aiPermissions","offForSite","Boolean","includes","displayName","AssistPanelOnHost","releaseVerdict","assist","generative","assistVisible","visible","assistStaffPreview","staffPreview","generativeVisible"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAGA,SAASA,OAAO,QAA6B,QAAO;AACpD,SAASC,YAAY,QAAQ,kBAAc;AAC3C,SAASC,eAAe,QAAQ,6BAAyB;AACzD,SAASC,gBAAgB,QAAQ,oCAAgC;AACjE,SAASC,oBAAoB,QAAQ,8BAA0B;AAmB/D,MAAMC,OAA4B;IAAEC,QAAQ;IAAOC,KAAK;IAAOC,UAAU;AAAM;AAE/E;;;;CAIC,GACD,OAAO,SAASC,uBACdC,MAA0C;IAE1C,MAAMC,UAAUD,WAAWE;IAC3B,MAAMN,SAASI,CAAAA,0BAAAA,OAAQJ,MAAM,MAAK;IAClC,MAAMO,UAAUX,gBAAgBI,SAASI,0BAAAA,OAAQI,OAAO,GAAG;IAC3D,MAAMP,MAAMM,OAAO,CAAC,SAAS;IAC7B,MAAML,WAAWK,OAAO,CAAC,cAAc;IACvC,OAAOb,QACL,IAAOW,UAAU;YAAEL;YAAQC;YAAKC;QAAS,IAAII,WAC7C;QAACD;QAASL;QAAQC;QAAKC;KAAS;AAEpC;AAYA;;;CAGC,GACD,MAAMO,eAAoC;IAAET,QAAQ;IAAMC,KAAK;IAAOC,UAAU;AAAM;AAEtF;;;;;;;;CAQC,GACD,OAAO,SAASQ,uBAAuBC,KAAoB;IACzD,MAAM,EAAEC,iBAAiB,EAAEC,gBAAgB,EAAW,GAAGF,OAATG,wCAASH;;;;IACzD,MAAMI,gBAAgBZ,uBAAuBS;IAC7C,MAAMI,aAAaC,QAAQJ,qBAAqB,EAACA,oCAAAA,iBAAkBK,QAAQ,CAACvB;IAC5E,qBACE,KAACE,+BAAqBiB;QAAMC,eAAeC,aAAaP,eAAeM;;AAE3E;AACAL,uBAAuBS,WAAW,GAAG;AAerC;;;CAGC,GACD,OAAO,SAASC,kBAAkBT,KAAiB;QAE3BR;IADtB,MAAM,EAAES,iBAAiB,EAAES,cAAc,EAAW,GAAGV,OAATG,wCAASH;;;;IACvD,MAAMI,iBAAgBZ,0BAAAA,uBAAuBS,8BAAvBT,0BAA6CJ;IACnE,MAAMuB,SAASD,eAAe;IAC9B,MAAME,aAAaF,eAAe;IAClC,qBACE,KAACvB,mCACKgB;QACJU,eAAeF,OAAOG,OAAO;QAC7BC,oBAAoBJ,OAAOK,YAAY;QACvCC,mBAAmBL,WAAWE,OAAO;QACrCV,eAAeA;;AAGrB;AACAK,kBAAkBD,WAAW,GAAG"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsoleProductCopyValues, ConsoleProductEditorZoneProps } from '
|
|
1
|
+
import type { ConsoleProductCopyValues, ConsoleProductEditorZoneProps } from './ai-product-zones';
|
|
2
2
|
import { type AiProductCopyProposal } from '../model/ai-products';
|
|
3
3
|
/**
|
|
4
4
|
* "Write with AI" in the commerce product editor (AGL-2916), through the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-product-copy-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { seoListingFieldCount, seoListingFieldTooLong } from '@aglyn/aglyn/app-utils/seo-listing-fields'\nimport type {\n ConsoleProductCopyValues,\n ConsoleProductEditorZoneProps,\n} from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { Alert, Box, Button, Chip, CircularProgress, Link, Stack, Typography } from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport {\n aiProductCopyProposals,\n aiProductsJobInputs,\n type AiProductCopyProposal,\n} from '../model/ai-products'\nimport { isAiJobMoving, useAiProductsJobs } from './use-ai-products-jobs'\n\n/**\n * \"Write with AI\" in the commerce product editor (AGL-2916), through the\n * `productEditor` zone. A `products` job writes the product's description,\n * search listing, tags, categories and clearer option names from what the\n * editor holds: its name, text, tags and options, and its first photo for a\n * model that reads pictures. The proposal is shown in full, with any fact the\n * copy could not know marked for the person to fill in, and \"Put in the\n * fields\" stages it in the editor as unsaved changes. Save product is the\n * write; this card never writes a product.\n */\n\n/** The brief a product copy job carries, which the jobs list shows. */\nexport const aiProductCopyBrief = (name: string) => `Write the storefront copy for ${name.trim()}`\n\n/** The values a proposal stages, in the editor's terms. */\nexport function aiProductCopyValues(proposal: AiProductCopyProposal): ConsoleProductCopyValues {\n const values = proposal.values\n if (!values) return {}\n return {\n description: values.description,\n tags: values.tags,\n categoryIds: values.categoryIds,\n seoTitle: values.seoTitle,\n seoDescription: values.seoDescription,\n ...(values.optionNames.length ? { optionNames: values.optionNames } : {}),\n }\n}\n\nfunction Field(props: { label: string; children: ReactNode; count?: ReactNode }) {\n return (\n <Stack spacing={0.25}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {props.label}\n </Typography>\n {props.count}\n </Stack>\n {props.children}\n </Stack>\n )\n}\n\n/** The proposed copy, field by field. */\nexport function AiProductCopyProposalView(props: { proposal: AiProductCopyProposal }) {\n const { proposal } = props\n const values = proposal.values\n if (!values) return null\n const renamed = values.optionNames\n .map((name, index) => ({ before: proposal.optionNamesBefore[index] ?? '', after: name }))\n .filter((pair) => pair.before !== pair.after)\n return (\n <Stack spacing={1}>\n <Field label=\"Description\">\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {values.description}\n </Typography>\n </Field>\n {values.tags.length ? (\n <Field label=\"Tags\">\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {values.tags.map((tag) => (\n <Chip key={tag} size=\"small\" variant=\"outlined\" label={tag} />\n ))}\n </Stack>\n </Field>\n ) : null}\n {proposal.categories.length ? (\n <Field label=\"Categories\">\n <Typography variant=\"body2\">{proposal.categories.map((category) => category.name).join(', ')}</Typography>\n </Field>\n ) : null}\n {renamed.length ? (\n <Field label=\"Option names\">\n <Typography variant=\"body2\">\n {renamed.map((pair) => `${pair.before || 'Unnamed'} → ${pair.after}`).join(', ')}\n </Typography>\n </Field>\n ) : null}\n <Field\n label=\"Search title\"\n count={\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={seoListingFieldTooLong('title', values.seoTitle) ? 'error' : 'default'}\n label={seoListingFieldCount('title', values.seoTitle)}\n />\n }\n >\n <Typography variant=\"body2\">{values.seoTitle}</Typography>\n </Field>\n <Field\n label=\"Search description\"\n count={\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={seoListingFieldTooLong('description', values.seoDescription) ? 'error' : 'default'}\n label={seoListingFieldCount('description', values.seoDescription)}\n />\n }\n >\n <Typography variant=\"body2\">{values.seoDescription}</Typography>\n </Field>\n {proposal.gaps.length ? (\n <Alert severity=\"warning\">\n {`Fill in what the copy could not know before you save: ${proposal.gaps.map((gap) => `[${gap}]`).join(', ')}.`}\n </Alert>\n ) : null}\n {proposal.notes.map((note) => (\n <Typography key={note} variant=\"caption\" color=\"text.secondary\">\n {note}\n </Typography>\n ))}\n </Stack>\n )\n}\n\nexport function AiProductCopyCard(props: ConsoleProductEditorZoneProps) {\n const { hostId, product, proposeValues } = props\n const { orgId, verdict, jobs, notice, start } = useAiProductsJobs({\n hostId,\n orgId: props.orgId,\n follows: (job) => job.brief === aiProductCopyBrief(product.name),\n })\n const [startedId, setStartedId] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [staged, setStaged] = useState<string | null>(null)\n\n const write = useCallback(async () => {\n setBusy(true)\n setStaged(null)\n try {\n const job = await start(\n aiProductCopyBrief(product.name),\n aiProductsJobInputs({\n target: 'product',\n product: {\n id: product.id,\n name: product.name,\n type: product.type,\n text: product.description,\n tags: product.tags,\n categoryIds: product.categoryIds,\n options: product.options,\n imageUrl: product.mediaUrls[0] ?? null,\n seoTitle: product.seoTitle,\n seoDescription: product.seoDescription,\n },\n }),\n )\n if (job) setStartedId(job.id)\n } finally {\n setBusy(false)\n }\n }, [start, product])\n\n if (verdict !== 'ready') return null\n\n // The job this card started, or the latest one that wrote this saved product's copy.\n const job = startedId\n ? (jobs.find((entry) => entry.id === startedId) ?? null)\n : product.id\n ? (jobs.find((entry) => aiProductCopyProposals(entry).some((proposal) => proposal.product.id === product.id)) ??\n null)\n : null\n const proposal = job\n ? (aiProductCopyProposals(job).find((entry) => entry.product.id === product.id) ?? aiProductCopyProposals(job)[0] ?? null)\n : null\n const running = isAiJobMoving(job)\n const unnamed = !product.name.trim()\n const help = pluginDocsHelp('aiProducts', {\n excerpt:\n 'Write a product’s description, search listing and tags from what it says and shows. It is put in the fields as unsaved changes; nothing is saved until you save.',\n })\n\n return (\n <Box sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }} aria-label=\"Product copy assistant\">\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline' }}>\n <Typography variant=\"subtitle2\" sx={{ flexGrow: 1 }}>\n {'Write with AI'}\n </Typography>\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" variant=\"caption\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Writes the description, search listing and tags, suggests categories and clearer option names, ' +\n 'from the product’s name, text, tags and options and its first photo. Prices are never written.'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button size=\"small\" variant=\"outlined\" disabled={busy || running || unnamed || !orgId} onClick={() => void write()}>\n {proposal ? 'Write again' : 'Write copy'}\n </Button>\n {busy || running ? <CircularProgress size={16} aria-label=\"Writing the copy\" /> : null}\n {unnamed ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Name the product first.'}\n </Typography>\n ) : null}\n </Stack>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n {job?.error && !proposal?.values ? (\n <Alert severity={job.status === 'failed' ? 'error' : 'warning'}>{job.error}</Alert>\n ) : null}\n {proposal?.skipped ? <Alert severity=\"info\">{proposal.skipped}</Alert> : null}\n {proposal?.values && job ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Proposed copy'}</Typography>\n <AiProductCopyProposalView proposal={proposal} />\n <Button\n size=\"small\"\n variant=\"contained\"\n sx={{ alignSelf: 'flex-start' }}\n disabled={staged === job.id}\n onClick={() => {\n proposeValues(aiProductCopyValues(proposal), job.id)\n setStaged(job.id)\n }}\n >\n {'Put in the fields'}\n </Button>\n </Stack>\n ) : null}\n {staged ? (\n <Alert severity=\"success\">{'In the fields as unsaved changes. Review them, then press Save product.'}</Alert>\n ) : null}\n </Stack>\n </Box>\n )\n}\nAiProductCopyCard.displayName = 'AiProductCopyCard'\n\nexport default AiProductCopyCard\n"],"names":["pluginDocsHelp","seoListingFieldCount","seoListingFieldTooLong","Alert","Box","Button","Chip","CircularProgress","Link","Stack","Typography","useCallback","useState","aiProductCopyProposals","aiProductsJobInputs","isAiJobMoving","useAiProductsJobs","aiProductCopyBrief","name","trim","aiProductCopyValues","proposal","values","description","tags","categoryIds","seoTitle","seoDescription","optionNames","length","Field","props","spacing","direction","sx","alignItems","variant","color","flexGrow","label","count","children","AiProductCopyProposalView","renamed","map","index","before","optionNamesBefore","after","filter","pair","whiteSpace","wordBreak","useFlexGap","flexWrap","tag","size","categories","category","join","gaps","severity","gap","notes","note","AiProductCopyCard","jobs","hostId","product","proposeValues","orgId","verdict","notice","start","follows","job","brief","startedId","setStartedId","busy","setBusy","staged","setStaged","write","target","id","type","text","options","imageUrl","mediaUrls","find","entry","some","running","unnamed","help","excerpt","border","borderColor","borderRadius","p","aria-label","href","rel","title","disabled","onClick","error","status","skipped","alignSelf","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-product-copy-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { seoListingFieldCount, seoListingFieldTooLong } from '@aglyn/aglyn/app-utils/seo-listing-fields'\nimport type {\n ConsoleProductCopyValues,\n ConsoleProductEditorZoneProps,\n} from './ai-product-zones'\nimport { Alert, Box, Button, Chip, CircularProgress, Link, Stack, Typography } from '@mui/material'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport {\n aiProductCopyProposals,\n aiProductsJobInputs,\n type AiProductCopyProposal,\n} from '../model/ai-products'\nimport { isAiJobMoving, useAiProductsJobs } from './use-ai-products-jobs'\n\n/**\n * \"Write with AI\" in the commerce product editor (AGL-2916), through the\n * `productEditor` zone. A `products` job writes the product's description,\n * search listing, tags, categories and clearer option names from what the\n * editor holds: its name, text, tags and options, and its first photo for a\n * model that reads pictures. The proposal is shown in full, with any fact the\n * copy could not know marked for the person to fill in, and \"Put in the\n * fields\" stages it in the editor as unsaved changes. Save product is the\n * write; this card never writes a product.\n */\n\n/** The brief a product copy job carries, which the jobs list shows. */\nexport const aiProductCopyBrief = (name: string) => `Write the storefront copy for ${name.trim()}`\n\n/** The values a proposal stages, in the editor's terms. */\nexport function aiProductCopyValues(proposal: AiProductCopyProposal): ConsoleProductCopyValues {\n const values = proposal.values\n if (!values) return {}\n return {\n description: values.description,\n tags: values.tags,\n categoryIds: values.categoryIds,\n seoTitle: values.seoTitle,\n seoDescription: values.seoDescription,\n ...(values.optionNames.length ? { optionNames: values.optionNames } : {}),\n }\n}\n\nfunction Field(props: { label: string; children: ReactNode; count?: ReactNode }) {\n return (\n <Stack spacing={0.25}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ flexGrow: 1 }}>\n {props.label}\n </Typography>\n {props.count}\n </Stack>\n {props.children}\n </Stack>\n )\n}\n\n/** The proposed copy, field by field. */\nexport function AiProductCopyProposalView(props: { proposal: AiProductCopyProposal }) {\n const { proposal } = props\n const values = proposal.values\n if (!values) return null\n const renamed = values.optionNames\n .map((name, index) => ({ before: proposal.optionNamesBefore[index] ?? '', after: name }))\n .filter((pair) => pair.before !== pair.after)\n return (\n <Stack spacing={1}>\n <Field label=\"Description\">\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>\n {values.description}\n </Typography>\n </Field>\n {values.tags.length ? (\n <Field label=\"Tags\">\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {values.tags.map((tag) => (\n <Chip key={tag} size=\"small\" variant=\"outlined\" label={tag} />\n ))}\n </Stack>\n </Field>\n ) : null}\n {proposal.categories.length ? (\n <Field label=\"Categories\">\n <Typography variant=\"body2\">{proposal.categories.map((category) => category.name).join(', ')}</Typography>\n </Field>\n ) : null}\n {renamed.length ? (\n <Field label=\"Option names\">\n <Typography variant=\"body2\">\n {renamed.map((pair) => `${pair.before || 'Unnamed'} → ${pair.after}`).join(', ')}\n </Typography>\n </Field>\n ) : null}\n <Field\n label=\"Search title\"\n count={\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={seoListingFieldTooLong('title', values.seoTitle) ? 'error' : 'default'}\n label={seoListingFieldCount('title', values.seoTitle)}\n />\n }\n >\n <Typography variant=\"body2\">{values.seoTitle}</Typography>\n </Field>\n <Field\n label=\"Search description\"\n count={\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={seoListingFieldTooLong('description', values.seoDescription) ? 'error' : 'default'}\n label={seoListingFieldCount('description', values.seoDescription)}\n />\n }\n >\n <Typography variant=\"body2\">{values.seoDescription}</Typography>\n </Field>\n {proposal.gaps.length ? (\n <Alert severity=\"warning\">\n {`Fill in what the copy could not know before you save: ${proposal.gaps.map((gap) => `[${gap}]`).join(', ')}.`}\n </Alert>\n ) : null}\n {proposal.notes.map((note) => (\n <Typography key={note} variant=\"caption\" color=\"text.secondary\">\n {note}\n </Typography>\n ))}\n </Stack>\n )\n}\n\nexport function AiProductCopyCard(props: ConsoleProductEditorZoneProps) {\n const { hostId, product, proposeValues } = props\n const { orgId, verdict, jobs, notice, start } = useAiProductsJobs({\n hostId,\n orgId: props.orgId,\n follows: (job) => job.brief === aiProductCopyBrief(product.name),\n })\n const [startedId, setStartedId] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [staged, setStaged] = useState<string | null>(null)\n\n const write = useCallback(async () => {\n setBusy(true)\n setStaged(null)\n try {\n const job = await start(\n aiProductCopyBrief(product.name),\n aiProductsJobInputs({\n target: 'product',\n product: {\n id: product.id,\n name: product.name,\n type: product.type,\n text: product.description,\n tags: product.tags,\n categoryIds: product.categoryIds,\n options: product.options,\n imageUrl: product.mediaUrls[0] ?? null,\n seoTitle: product.seoTitle,\n seoDescription: product.seoDescription,\n },\n }),\n )\n if (job) setStartedId(job.id)\n } finally {\n setBusy(false)\n }\n }, [start, product])\n\n if (verdict !== 'ready') return null\n\n // The job this card started, or the latest one that wrote this saved product's copy.\n const job = startedId\n ? (jobs.find((entry) => entry.id === startedId) ?? null)\n : product.id\n ? (jobs.find((entry) => aiProductCopyProposals(entry).some((proposal) => proposal.product.id === product.id)) ??\n null)\n : null\n const proposal = job\n ? (aiProductCopyProposals(job).find((entry) => entry.product.id === product.id) ?? aiProductCopyProposals(job)[0] ?? null)\n : null\n const running = isAiJobMoving(job)\n const unnamed = !product.name.trim()\n const help = pluginDocsHelp('aiProducts', {\n excerpt:\n 'Write a product’s description, search listing and tags from what it says and shows. It is put in the fields as unsaved changes; nothing is saved until you save.',\n })\n\n return (\n <Box sx={{ border: 1, borderColor: 'divider', borderRadius: 1, p: 1.5 }} aria-label=\"Product copy assistant\">\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'baseline' }}>\n <Typography variant=\"subtitle2\" sx={{ flexGrow: 1 }}>\n {'Write with AI'}\n </Typography>\n <Link href={help.href} target=\"_blank\" rel=\"noopener\" variant=\"caption\" title={help.excerpt}>\n {'How it works'}\n </Link>\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Writes the description, search listing and tags, suggests categories and clearer option names, ' +\n 'from the product’s name, text, tags and options and its first photo. Prices are never written.'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button size=\"small\" variant=\"outlined\" disabled={busy || running || unnamed || !orgId} onClick={() => void write()}>\n {proposal ? 'Write again' : 'Write copy'}\n </Button>\n {busy || running ? <CircularProgress size={16} aria-label=\"Writing the copy\" /> : null}\n {unnamed ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Name the product first.'}\n </Typography>\n ) : null}\n </Stack>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n {job?.error && !proposal?.values ? (\n <Alert severity={job.status === 'failed' ? 'error' : 'warning'}>{job.error}</Alert>\n ) : null}\n {proposal?.skipped ? <Alert severity=\"info\">{proposal.skipped}</Alert> : null}\n {proposal?.values && job ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Proposed copy'}</Typography>\n <AiProductCopyProposalView proposal={proposal} />\n <Button\n size=\"small\"\n variant=\"contained\"\n sx={{ alignSelf: 'flex-start' }}\n disabled={staged === job.id}\n onClick={() => {\n proposeValues(aiProductCopyValues(proposal), job.id)\n setStaged(job.id)\n }}\n >\n {'Put in the fields'}\n </Button>\n </Stack>\n ) : null}\n {staged ? (\n <Alert severity=\"success\">{'In the fields as unsaved changes. Review them, then press Save product.'}</Alert>\n ) : null}\n </Stack>\n </Box>\n )\n}\nAiProductCopyCard.displayName = 'AiProductCopyCard'\n\nexport default AiProductCopyCard\n"],"names":["pluginDocsHelp","seoListingFieldCount","seoListingFieldTooLong","Alert","Box","Button","Chip","CircularProgress","Link","Stack","Typography","useCallback","useState","aiProductCopyProposals","aiProductsJobInputs","isAiJobMoving","useAiProductsJobs","aiProductCopyBrief","name","trim","aiProductCopyValues","proposal","values","description","tags","categoryIds","seoTitle","seoDescription","optionNames","length","Field","props","spacing","direction","sx","alignItems","variant","color","flexGrow","label","count","children","AiProductCopyProposalView","renamed","map","index","before","optionNamesBefore","after","filter","pair","whiteSpace","wordBreak","useFlexGap","flexWrap","tag","size","categories","category","join","gaps","severity","gap","notes","note","AiProductCopyCard","jobs","hostId","product","proposeValues","orgId","verdict","notice","start","follows","job","brief","startedId","setStartedId","busy","setBusy","staged","setStaged","write","target","id","type","text","options","imageUrl","mediaUrls","find","entry","some","running","unnamed","help","excerpt","border","borderColor","borderRadius","p","aria-label","href","rel","title","disabled","onClick","error","status","skipped","alignSelf","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAASA,cAAc,QAAQ,mCAAkC;AACjE,SAASC,oBAAoB,EAAEC,sBAAsB,QAAQ,4CAA2C;AAKxG,SAASC,KAAK,EAAEC,GAAG,EAAEC,MAAM,EAAEC,IAAI,EAAEC,gBAAgB,EAAEC,IAAI,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AACnG,SAASC,WAAW,EAAEC,QAAQ,QAAwB,QAAO;AAC7D,SACEC,sBAAsB,EACtBC,mBAAmB,QAEd,0BAAsB;AAC7B,SAASC,aAAa,EAAEC,iBAAiB,QAAQ,4BAAwB;AAEzE;;;;;;;;;CASC,GAED,qEAAqE,GACrE,OAAO,MAAMC,qBAAqB,CAACC,OAAiB,CAAC,8BAA8B,EAAEA,KAAKC,IAAI,IAAI,CAAA;AAElG,yDAAyD,GACzD,OAAO,SAASC,oBAAoBC,QAA+B;IACjE,MAAMC,SAASD,SAASC,MAAM;IAC9B,IAAI,CAACA,QAAQ,OAAO,CAAC;IACrB,OAAO;QACLC,aAAaD,OAAOC,WAAW;QAC/BC,MAAMF,OAAOE,IAAI;QACjBC,aAAaH,OAAOG,WAAW;QAC/BC,UAAUJ,OAAOI,QAAQ;QACzBC,gBAAgBL,OAAOK,cAAc;OACjCL,OAAOM,WAAW,CAACC,MAAM,GAAG;QAAED,aAAaN,OAAOM,WAAW;IAAC,IAAI,CAAC;AAE3E;AAEA,SAASE,MAAMC,KAAgE;IAC7E,qBACE,MAACtB;QAAMuB,SAAS;;0BACd,MAACvB;gBAAMwB,WAAU;gBAAMD,SAAS;gBAAGE,IAAI;oBAAEC,YAAY;gBAAS;;kCAC5D,KAACzB;wBAAW0B,SAAQ;wBAAUC,OAAM;wBAAiBH,IAAI;4BAAEI,UAAU;wBAAE;kCACpEP,MAAMQ,KAAK;;oBAEbR,MAAMS,KAAK;;;YAEbT,MAAMU,QAAQ;;;AAGrB;AAEA,uCAAuC,GACvC,OAAO,SAASC,0BAA0BX,KAA0C;IAClF,MAAM,EAAEV,QAAQ,EAAE,GAAGU;IACrB,MAAMT,SAASD,SAASC,MAAM;IAC9B,IAAI,CAACA,QAAQ,OAAO;IACpB,MAAMqB,UAAUrB,OAAOM,WAAW,CAC/BgB,GAAG,CAAC,CAAC1B,MAAM2B;YAAqBxB;eAAV;YAAEyB,MAAM,GAAEzB,oCAAAA,SAAS0B,iBAAiB,CAACF,MAAM,YAAjCxB,oCAAqC;YAAI2B,OAAO9B;QAAK;OACrF+B,MAAM,CAAC,CAACC,OAASA,KAAKJ,MAAM,KAAKI,KAAKF,KAAK;IAC9C,qBACE,MAACvC;QAAMuB,SAAS;;0BACd,KAACF;gBAAMS,OAAM;0BACX,cAAA,KAAC7B;oBAAW0B,SAAQ;oBAAQF,IAAI;wBAAEiB,YAAY;wBAAYC,WAAW;oBAAa;8BAC/E9B,OAAOC,WAAW;;;YAGtBD,OAAOE,IAAI,CAACK,MAAM,iBACjB,KAACC;gBAAMS,OAAM;0BACX,cAAA,KAAC9B;oBAAMwB,WAAU;oBAAMD,SAAS;oBAAKqB,UAAU;oBAACnB,IAAI;wBAAEoB,UAAU;oBAAO;8BACpEhC,OAAOE,IAAI,CAACoB,GAAG,CAAC,CAACW,oBAChB,KAACjD;4BAAekD,MAAK;4BAAQpB,SAAQ;4BAAWG,OAAOgB;2BAA5CA;;iBAIf;YACHlC,SAASoC,UAAU,CAAC5B,MAAM,iBACzB,KAACC;gBAAMS,OAAM;0BACX,cAAA,KAAC7B;oBAAW0B,SAAQ;8BAASf,SAASoC,UAAU,CAACb,GAAG,CAAC,CAACc,WAAaA,SAASxC,IAAI,EAAEyC,IAAI,CAAC;;iBAEvF;YACHhB,QAAQd,MAAM,iBACb,KAACC;gBAAMS,OAAM;0BACX,cAAA,KAAC7B;oBAAW0B,SAAQ;8BACjBO,QAAQC,GAAG,CAAC,CAACM,OAAS,GAAGA,KAAKJ,MAAM,IAAI,UAAU,GAAG,EAAEI,KAAKF,KAAK,EAAE,EAAEW,IAAI,CAAC;;iBAG7E;0BACJ,KAAC7B;gBACCS,OAAM;gBACNC,qBACE,KAAClC;oBACCkD,MAAK;oBACLpB,SAAQ;oBACRC,OAAOnC,uBAAuB,SAASoB,OAAOI,QAAQ,IAAI,UAAU;oBACpEa,OAAOtC,qBAAqB,SAASqB,OAAOI,QAAQ;;0BAIxD,cAAA,KAAChB;oBAAW0B,SAAQ;8BAASd,OAAOI,QAAQ;;;0BAE9C,KAACI;gBACCS,OAAM;gBACNC,qBACE,KAAClC;oBACCkD,MAAK;oBACLpB,SAAQ;oBACRC,OAAOnC,uBAAuB,eAAeoB,OAAOK,cAAc,IAAI,UAAU;oBAChFY,OAAOtC,qBAAqB,eAAeqB,OAAOK,cAAc;;0BAIpE,cAAA,KAACjB;oBAAW0B,SAAQ;8BAASd,OAAOK,cAAc;;;YAEnDN,SAASuC,IAAI,CAAC/B,MAAM,iBACnB,KAAC1B;gBAAM0D,UAAS;0BACb,CAAC,sDAAsD,EAAExC,SAASuC,IAAI,CAAChB,GAAG,CAAC,CAACkB,MAAQ,CAAC,CAAC,EAAEA,IAAI,CAAC,CAAC,EAAEH,IAAI,CAAC,MAAM,CAAC,CAAC;iBAE9G;YACHtC,SAAS0C,KAAK,CAACnB,GAAG,CAAC,CAACoB,qBACnB,KAACtD;oBAAsB0B,SAAQ;oBAAUC,OAAM;8BAC5C2B;mBADcA;;;AAMzB;AAEA,OAAO,SAASC,kBAAkBlC,KAAoC;QA2C/DmC,YAEEA,aAIFrD,MAAAA;IAhDL,MAAM,EAAEsD,MAAM,EAAEC,OAAO,EAAEC,aAAa,EAAE,GAAGtC;IAC3C,MAAM,EAAEuC,KAAK,EAAEC,OAAO,EAAEL,IAAI,EAAEM,MAAM,EAAEC,KAAK,EAAE,GAAGzD,kBAAkB;QAChEmD;QACAG,OAAOvC,MAAMuC,KAAK;QAClBI,SAAS,CAACC,MAAQA,IAAIC,KAAK,KAAK3D,mBAAmBmD,QAAQlD,IAAI;IACjE;IACA,MAAM,CAAC2D,WAAWC,aAAa,GAAGlE,SAAwB;IAC1D,MAAM,CAACmE,MAAMC,QAAQ,GAAGpE,SAAS;IACjC,MAAM,CAACqE,QAAQC,UAAU,GAAGtE,SAAwB;IAEpD,MAAMuE,QAAQxE,YAAY;QACxBqE,QAAQ;QACRE,UAAU;QACV,IAAI;gBAacd;YAZhB,MAAMO,MAAM,MAAMF,MAChBxD,mBAAmBmD,QAAQlD,IAAI,GAC/BJ,oBAAoB;gBAClBsE,QAAQ;gBACRhB,SAAS;oBACPiB,IAAIjB,QAAQiB,EAAE;oBACdnE,MAAMkD,QAAQlD,IAAI;oBAClBoE,MAAMlB,QAAQkB,IAAI;oBAClBC,MAAMnB,QAAQ7C,WAAW;oBACzBC,MAAM4C,QAAQ5C,IAAI;oBAClBC,aAAa2C,QAAQ3C,WAAW;oBAChC+D,SAASpB,QAAQoB,OAAO;oBACxBC,QAAQ,GAAErB,sBAAAA,QAAQsB,SAAS,CAAC,EAAE,YAApBtB,sBAAwB;oBAClC1C,UAAU0C,QAAQ1C,QAAQ;oBAC1BC,gBAAgByC,QAAQzC,cAAc;gBACxC;YACF;YAEF,IAAIgD,KAAKG,aAAaH,IAAIU,EAAE;QAC9B,SAAU;YACRL,QAAQ;QACV;IACF,GAAG;QAACP;QAAOL;KAAQ;IAEnB,IAAIG,YAAY,SAAS,OAAO;IAEhC,qFAAqF;IACrF,MAAMI,MAAME,aACPX,aAAAA,KAAKyB,IAAI,CAAC,CAACC,QAAUA,MAAMP,EAAE,KAAKR,sBAAlCX,aAAgD,OACjDE,QAAQiB,EAAE,IACPnB,cAAAA,KAAKyB,IAAI,CAAC,CAACC,QAAU/E,uBAAuB+E,OAAOC,IAAI,CAAC,CAACxE,WAAaA,SAAS+C,OAAO,CAACiB,EAAE,KAAKjB,QAAQiB,EAAE,cAAxGnB,cACD,OACA;IACN,MAAM7C,WAAWsD,OACZ9D,QAAAA,+BAAAA,uBAAuB8D,KAAKgB,IAAI,CAAC,CAACC,QAAUA,MAAMxB,OAAO,CAACiB,EAAE,KAAKjB,QAAQiB,EAAE,aAA3ExE,+BAAgFA,uBAAuB8D,IAAI,CAAC,EAAE,YAA9G9D,OAAkH,OACnH;IACJ,MAAMiF,UAAU/E,cAAc4D;IAC9B,MAAMoB,UAAU,CAAC3B,QAAQlD,IAAI,CAACC,IAAI;IAClC,MAAM6E,OAAOhG,eAAe,cAAc;QACxCiG,SACE;IACJ;IAEA,qBACE,KAAC7F;QAAI8B,IAAI;YAAEgE,QAAQ;YAAGC,aAAa;YAAWC,cAAc;YAAGC,GAAG;QAAI;QAAGC,cAAW;kBAClF,cAAA,MAAC7F;YAAMuB,SAAS;;8BACd,MAACvB;oBAAMwB,WAAU;oBAAMD,SAAS;oBAAGE,IAAI;wBAAEC,YAAY;oBAAW;;sCAC9D,KAACzB;4BAAW0B,SAAQ;4BAAYF,IAAI;gCAAEI,UAAU;4BAAE;sCAC/C;;sCAEH,KAAC9B;4BAAK+F,MAAMP,KAAKO,IAAI;4BAAEnB,QAAO;4BAASoB,KAAI;4BAAWpE,SAAQ;4BAAUqE,OAAOT,KAAKC,OAAO;sCACxF;;;;8BAGL,KAACvF;oBAAW0B,SAAQ;oBAAQC,OAAM;8BAC/B,oGACC;;8BAEJ,MAAC5B;oBAAMwB,WAAU;oBAAMD,SAAS;oBAAGE,IAAI;wBAAEC,YAAY;oBAAS;;sCAC5D,KAAC9B;4BAAOmD,MAAK;4BAAQpB,SAAQ;4BAAWsE,UAAU3B,QAAQe,WAAWC,WAAW,CAACzB;4BAAOqC,SAAS,IAAM,KAAKxB;sCACzG9D,WAAW,gBAAgB;;wBAE7B0D,QAAQe,wBAAU,KAACvF;4BAAiBiD,MAAM;4BAAI8C,cAAW;6BAAwB;wBACjFP,wBACC,KAACrF;4BAAW0B,SAAQ;4BAAUC,OAAM;sCACjC;6BAED;;;gBAELmC,uBAAS,KAACrE;oBAAM0D,UAAS;8BAAWW;qBAAkB;gBACtDG,CAAAA,uBAAAA,IAAKiC,KAAK,KAAI,EAACvF,4BAAAA,SAAUC,MAAM,kBAC9B,KAACnB;oBAAM0D,UAAUc,IAAIkC,MAAM,KAAK,WAAW,UAAU;8BAAYlC,IAAIiC,KAAK;qBACxE;gBACHvF,CAAAA,4BAAAA,SAAUyF,OAAO,kBAAG,KAAC3G;oBAAM0D,UAAS;8BAAQxC,SAASyF,OAAO;qBAAY;gBACxEzF,CAAAA,4BAAAA,SAAUC,MAAM,KAAIqD,oBACnB,MAAClE;oBAAMuB,SAAS;;sCACd,KAACtB;4BAAW0B,SAAQ;sCAAa;;sCACjC,KAACM;4BAA0BrB,UAAUA;;sCACrC,KAAChB;4BACCmD,MAAK;4BACLpB,SAAQ;4BACRF,IAAI;gCAAE6E,WAAW;4BAAa;4BAC9BL,UAAUzB,WAAWN,IAAIU,EAAE;4BAC3BsB,SAAS;gCACPtC,cAAcjD,oBAAoBC,WAAWsD,IAAIU,EAAE;gCACnDH,UAAUP,IAAIU,EAAE;4BAClB;sCAEC;;;qBAGH;gBACHJ,uBACC,KAAC9E;oBAAM0D,UAAS;8BAAW;qBACzB;;;;AAIZ;AACAI,kBAAkB+C,WAAW,GAAG;AAEhC,eAAe/C,kBAAiB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsoleProductImportZoneProps } from '
|
|
1
|
+
import type { ConsoleProductImportZoneProps } from './ai-product-zones';
|
|
2
2
|
/**
|
|
3
3
|
* "Write copy with AI as they land" in the commerce CSV import dialog
|
|
4
4
|
* (AGL-2916), through the `productImport` zone. The box sets an option on the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-product-import-option.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { ConsoleProductImportZoneProps } from '
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-product-import-option.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { ConsoleProductImportZoneProps } from './ai-product-zones'\nimport { Checkbox, FormControlLabel, Stack, Typography } from '@mui/material'\nimport { AI_PRODUCTS_BULK_MAX, AI_PRODUCTS_IMPORT_WRITE_COPY } from '../model/ai-products'\nimport { useAiProductsJobs } from './use-ai-products-jobs'\n\n/**\n * \"Write copy with AI as they land\" in the commerce CSV import dialog\n * (AGL-2916), through the `productImport` zone. The box sets an option on the\n * import; once the import has created its products, the products hub's AI\n * card starts one copy job for them, and its proposals wait there for review\n * like any other. Nothing is written to an imported product until a person\n * applies its copy.\n */\nexport function AiProductImportOption(props: ConsoleProductImportZoneProps) {\n const { hostId, count, options, setOption } = props\n const { verdict } = useAiProductsJobs({ hostId, orgId: props.orgId })\n if (verdict !== 'ready') return null\n const covered = Math.min(count, AI_PRODUCTS_BULK_MAX)\n return (\n <Stack spacing={0.25}>\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={Boolean(options[AI_PRODUCTS_IMPORT_WRITE_COPY])}\n onChange={(event) => setOption(AI_PRODUCTS_IMPORT_WRITE_COPY, event.target.checked)}\n />\n }\n label=\"Write descriptions, search listings and tags with AI as they land\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {(count > AI_PRODUCTS_BULK_MAX\n ? `Copy is written for the first ${covered} of the ${count} products. `\n : '') + 'You review it on the products page before any is saved. Prices are never written.'}\n </Typography>\n </Stack>\n )\n}\nAiProductImportOption.displayName = 'AiProductImportOption'\n\nexport default AiProductImportOption\n"],"names":["Checkbox","FormControlLabel","Stack","Typography","AI_PRODUCTS_BULK_MAX","AI_PRODUCTS_IMPORT_WRITE_COPY","useAiProductsJobs","AiProductImportOption","props","hostId","count","options","setOption","verdict","orgId","covered","Math","min","spacing","control","size","checked","Boolean","onChange","event","target","label","variant","color","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,QAAQ,EAAEC,gBAAgB,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AAC7E,SAASC,oBAAoB,EAAEC,6BAA6B,QAAQ,0BAAsB;AAC1F,SAASC,iBAAiB,QAAQ,4BAAwB;AAE1D;;;;;;;CAOC,GACD,OAAO,SAASC,sBAAsBC,KAAoC;IACxE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAEC,SAAS,EAAE,GAAGJ;IAC9C,MAAM,EAAEK,OAAO,EAAE,GAAGP,kBAAkB;QAAEG;QAAQK,OAAON,MAAMM,KAAK;IAAC;IACnE,IAAID,YAAY,SAAS,OAAO;IAChC,MAAME,UAAUC,KAAKC,GAAG,CAACP,OAAON;IAChC,qBACE,MAACF;QAAMgB,SAAS;;0BACd,KAACjB;gBACCkB,uBACE,KAACnB;oBACCoB,MAAK;oBACLC,SAASC,QAAQX,OAAO,CAACN,8BAA8B;oBACvDkB,UAAU,CAACC,QAAUZ,UAAUP,+BAA+BmB,MAAMC,MAAM,CAACJ,OAAO;;gBAGtFK,OAAM;;0BAER,KAACvB;gBAAWwB,SAAQ;gBAAUC,OAAM;0BACjC,AAAClB,CAAAA,QAAQN,uBACN,CAAC,8BAA8B,EAAEW,QAAQ,QAAQ,EAAEL,MAAM,WAAW,CAAC,GACrE,EAAC,IAAK;;;;AAIlB;AACAH,sBAAsBsB,WAAW,GAAG;AAEpC,eAAetB,sBAAqB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* What the commerce plugin's product zones hand this plugin's widgets.
|
|
19
|
+
*
|
|
20
|
+
* `productEditor`, `productsHub` and `productImport` are zones the commerce
|
|
21
|
+
* plugin hosts and declares (`registerPluginZone`), with their props on its
|
|
22
|
+
* own zone tokens. A plugin never imports another plugin, so the widgets
|
|
23
|
+
* registered there read the props through the shapes restated here — the
|
|
24
|
+
* zone's props as the host documents them, by the same names. A widget is
|
|
25
|
+
* handed whatever the host passes, so this restates what the host SENDS; a
|
|
26
|
+
* field dropped from the host's declaration leaves a widget reading
|
|
27
|
+
* `undefined`, which is why every member below is one the host documents in
|
|
28
|
+
* the injection zones reference.
|
|
29
|
+
*
|
|
30
|
+
* Types only: nothing here reaches a bundle.
|
|
31
|
+
*/
|
|
32
|
+
export type ConsoleProductType = 'physical' | 'digital' | 'service';
|
|
33
|
+
export interface ConsoleProductOption {
|
|
34
|
+
name: string;
|
|
35
|
+
values: string[];
|
|
36
|
+
}
|
|
37
|
+
/** A product as the editor holds it: saved, or staged and unsaved. */
|
|
38
|
+
export interface ConsoleProductDraft {
|
|
39
|
+
/** `null` for a product nobody has saved yet. */
|
|
40
|
+
id: string | null;
|
|
41
|
+
name: string;
|
|
42
|
+
type: ConsoleProductType;
|
|
43
|
+
description: string;
|
|
44
|
+
tags: string[];
|
|
45
|
+
categoryIds: string[];
|
|
46
|
+
options: ConsoleProductOption[];
|
|
47
|
+
/** The product's media, in order; the first is the photo the storefront leads with. */
|
|
48
|
+
mediaUrls: string[];
|
|
49
|
+
seoTitle: string;
|
|
50
|
+
seoDescription: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Copy proposed for one product. A field left out is left alone;
|
|
54
|
+
* `optionNames` renames the product's options in order.
|
|
55
|
+
*/
|
|
56
|
+
export interface ConsoleProductCopyValues {
|
|
57
|
+
description?: string;
|
|
58
|
+
tags?: string[];
|
|
59
|
+
categoryIds?: string[];
|
|
60
|
+
optionNames?: string[];
|
|
61
|
+
seoTitle?: string;
|
|
62
|
+
seoDescription?: string;
|
|
63
|
+
}
|
|
64
|
+
/** The `productEditor` zone: the product, and a door that stages copy as unsaved edits. */
|
|
65
|
+
export interface ConsoleProductEditorZoneProps {
|
|
66
|
+
hostId: string;
|
|
67
|
+
orgId: string | undefined;
|
|
68
|
+
product: ConsoleProductDraft;
|
|
69
|
+
categories: ReadonlyArray<{
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
}>;
|
|
73
|
+
proposeValues: (values: ConsoleProductCopyValues, key: string) => void;
|
|
74
|
+
}
|
|
75
|
+
/** A catalog row as the products hub holds it. */
|
|
76
|
+
export interface ConsoleProductSummary {
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
status: 'draft' | 'active' | 'archived';
|
|
80
|
+
priceMissing: boolean;
|
|
81
|
+
hasPhoto: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** A new product proposed to the hub, which creates it as a draft with no price. */
|
|
84
|
+
export interface ConsoleProposedProduct {
|
|
85
|
+
name: string;
|
|
86
|
+
type: ConsoleProductType;
|
|
87
|
+
description: string;
|
|
88
|
+
tags: string[];
|
|
89
|
+
options: ConsoleProductOption[];
|
|
90
|
+
seoTitle: string;
|
|
91
|
+
seoDescription: string;
|
|
92
|
+
}
|
|
93
|
+
/** A discount proposed to the hub, which creates it switched off. */
|
|
94
|
+
export interface ConsoleProposedDiscount {
|
|
95
|
+
name: string;
|
|
96
|
+
code: string | null;
|
|
97
|
+
kind: 'percent' | 'fixed' | 'free_shipping';
|
|
98
|
+
valuePct: number | null;
|
|
99
|
+
valueCents: number | null;
|
|
100
|
+
minSubtotalCents: number | null;
|
|
101
|
+
}
|
|
102
|
+
/** The `productsHub` zone: the catalog rows, the latest import, and the hub's writes. */
|
|
103
|
+
export interface ConsoleProductsHubZoneProps {
|
|
104
|
+
hostId: string;
|
|
105
|
+
orgId: string | undefined;
|
|
106
|
+
products: readonly ConsoleProductSummary[];
|
|
107
|
+
lastImport: {
|
|
108
|
+
key: string;
|
|
109
|
+
productIds: readonly string[];
|
|
110
|
+
options: Readonly<Record<string, boolean>>;
|
|
111
|
+
} | null;
|
|
112
|
+
applyProductCopy: (productId: string, values: ConsoleProductCopyValues) => Promise<void>;
|
|
113
|
+
createProductDrafts: (products: readonly ConsoleProposedProduct[]) => Promise<string[]>;
|
|
114
|
+
createCategories: (names: readonly string[]) => Promise<number>;
|
|
115
|
+
createDiscountDrafts: (discounts: readonly ConsoleProposedDiscount[]) => Promise<number>;
|
|
116
|
+
}
|
|
117
|
+
/** The `productImport` zone: the import's size and the options set for it. */
|
|
118
|
+
export interface ConsoleProductImportZoneProps {
|
|
119
|
+
hostId: string;
|
|
120
|
+
orgId: string | undefined;
|
|
121
|
+
count: number;
|
|
122
|
+
options: Readonly<Record<string, boolean>>;
|
|
123
|
+
setOption: (key: string, on: boolean) => void;
|
|
124
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* What the commerce plugin's product zones hand this plugin's widgets.
|
|
18
|
+
*
|
|
19
|
+
* `productEditor`, `productsHub` and `productImport` are zones the commerce
|
|
20
|
+
* plugin hosts and declares (`registerPluginZone`), with their props on its
|
|
21
|
+
* own zone tokens. A plugin never imports another plugin, so the widgets
|
|
22
|
+
* registered there read the props through the shapes restated here — the
|
|
23
|
+
* zone's props as the host documents them, by the same names. A widget is
|
|
24
|
+
* handed whatever the host passes, so this restates what the host SENDS; a
|
|
25
|
+
* field dropped from the host's declaration leaves a widget reading
|
|
26
|
+
* `undefined`, which is why every member below is one the host documents in
|
|
27
|
+
* the injection zones reference.
|
|
28
|
+
*
|
|
29
|
+
* Types only: nothing here reaches a bundle.
|
|
30
|
+
*/ /** The `productImport` zone: the import's size and the options set for it. */ export { };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=ai-product-zones.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-product-zones.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * What the commerce plugin's product zones hand this plugin's widgets.\n *\n * `productEditor`, `productsHub` and `productImport` are zones the commerce\n * plugin hosts and declares (`registerPluginZone`), with their props on its\n * own zone tokens. A plugin never imports another plugin, so the widgets\n * registered there read the props through the shapes restated here — the\n * zone's props as the host documents them, by the same names. A widget is\n * handed whatever the host passes, so this restates what the host SENDS; a\n * field dropped from the host's declaration leaves a widget reading\n * `undefined`, which is why every member below is one the host documents in\n * the injection zones reference.\n *\n * Types only: nothing here reaches a bundle.\n */\n\nexport type ConsoleProductType = 'physical' | 'digital' | 'service'\n\nexport interface ConsoleProductOption {\n name: string\n values: string[]\n}\n\n/** A product as the editor holds it: saved, or staged and unsaved. */\nexport interface ConsoleProductDraft {\n /** `null` for a product nobody has saved yet. */\n id: string | null\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n categoryIds: string[]\n options: ConsoleProductOption[]\n /** The product's media, in order; the first is the photo the storefront leads with. */\n mediaUrls: string[]\n seoTitle: string\n seoDescription: string\n}\n\n/**\n * Copy proposed for one product. A field left out is left alone;\n * `optionNames` renames the product's options in order.\n */\nexport interface ConsoleProductCopyValues {\n description?: string\n tags?: string[]\n categoryIds?: string[]\n optionNames?: string[]\n seoTitle?: string\n seoDescription?: string\n}\n\n/** The `productEditor` zone: the product, and a door that stages copy as unsaved edits. */\nexport interface ConsoleProductEditorZoneProps {\n hostId: string\n orgId: string | undefined\n product: ConsoleProductDraft\n categories: ReadonlyArray<{ id: string; name: string }>\n proposeValues: (values: ConsoleProductCopyValues, key: string) => void\n}\n\n/** A catalog row as the products hub holds it. */\nexport interface ConsoleProductSummary {\n id: string\n name: string\n status: 'draft' | 'active' | 'archived'\n priceMissing: boolean\n hasPhoto: boolean\n}\n\n/** A new product proposed to the hub, which creates it as a draft with no price. */\nexport interface ConsoleProposedProduct {\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n options: ConsoleProductOption[]\n seoTitle: string\n seoDescription: string\n}\n\n/** A discount proposed to the hub, which creates it switched off. */\nexport interface ConsoleProposedDiscount {\n name: string\n code: string | null\n kind: 'percent' | 'fixed' | 'free_shipping'\n valuePct: number | null\n valueCents: number | null\n minSubtotalCents: number | null\n}\n\n/** The `productsHub` zone: the catalog rows, the latest import, and the hub's writes. */\nexport interface ConsoleProductsHubZoneProps {\n hostId: string\n orgId: string | undefined\n products: readonly ConsoleProductSummary[]\n lastImport: {\n key: string\n productIds: readonly string[]\n options: Readonly<Record<string, boolean>>\n } | null\n applyProductCopy: (productId: string, values: ConsoleProductCopyValues) => Promise<void>\n createProductDrafts: (products: readonly ConsoleProposedProduct[]) => Promise<string[]>\n createCategories: (names: readonly string[]) => Promise<number>\n createDiscountDrafts: (discounts: readonly ConsoleProposedDiscount[]) => Promise<number>\n}\n\n/** The `productImport` zone: the import's size and the options set for it. */\nexport interface ConsoleProductImportZoneProps {\n hostId: string\n orgId: string | undefined\n count: number\n options: Readonly<Record<string, boolean>>\n setOption: (key: string, on: boolean) => void\n}\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;CAcC,GA6FD,4EAA4E,GAC5E,WAMC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsoleProductsHubZoneProps } from '
|
|
1
|
+
import type { ConsoleProductsHubZoneProps } from './ai-product-zones';
|
|
2
2
|
type BriefKind = 'catalog' | 'categories';
|
|
3
3
|
/** What each brief dialog says. */
|
|
4
4
|
export declare const AI_PRODUCTS_BRIEF_COPY: Readonly<Record<BriefKind, {
|