@aglyn/plugins-ai 1.0.0-beta.231 → 1.0.0-beta.232
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.js +3 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-step.js +21 -4
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +8 -3
- package/src/lib/jobs/ai-job-layout-language.js +8 -2
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +7 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +52 -7
- package/src/lib/jobs/ai-job-page-language.js +72 -9
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +19 -5
- package/src/lib/jobs/ai-job-plan-step.js +54 -20
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +26 -5
- package/src/lib/jobs/ai-job-site-step.js +163 -24
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.d.ts +9 -0
- package/src/lib/layout-language/ai-layout-compiler.js +120 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +5 -4
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +12 -5
- package/src/lib/layout-language/ai-layout-language.js +92 -9
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-store.js +7 -1
- package/src/lib/layout-language/ai-layout-store.js.map +1 -1
- package/src/lib/model/ai-build-progress.js +4 -1
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-job-activity.js +1 -0
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +4 -0
- package/src/lib/model/ai-site-build-progress.js +28 -11
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +10 -2
- package/src/lib/model/ai-site-job.js +12 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start.d.ts +10 -1
- package/src/lib/model/ai-site-start.js +16 -2
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +20 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +37 -7
- package/src/lib/providers/catalog.js +103 -11
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine.js +5 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-logic-job.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n evaluateHostFunction,\n expressionSyntaxError,\n type FunctionComparator,\n type FunctionValueType,\n type HostFunction,\n functionReferencedNames,\n} from '@aglyn/aglyn/app-utils/functions'\nimport {\n functionGlobals,\n type HostVariable,\n type HostVariableType,\n VARIABLE_NAME_PATTERN,\n} from '@aglyn/aglyn/app-utils/variables'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\n\n/**\n * A `logic` job (AGL-3603): a site's Functions & Variables, by AI.\n *\n * - `function` proposes a function — parameters, locals, the numbered\n * if/then/otherwise operations and the value it returns — from a\n * description, or, naming a saved function, a changed or fixed copy of\n * it. Every expression is held to the evaluator's own grammar\n * (`expressionSyntaxError`), every name it reads to the function's own\n * names and the site's variables, and the whole definition is run once\n * with each parameter at its starting value before anyone sees it.\n * - `variable` proposes one site variable: a name, a type and a value in\n * that type's stored form.\n * - `explain` reads a saved function and answers in plain words what it\n * computes, and anything in it worth checking.\n *\n * Nothing is written: a proposal rides on the job's output, and the logic\n * editor is where a person saves it, or does not.\n */\n\nexport const AI_LOGIC_JOB_MODES = ['function', 'variable', 'explain'] as const\nexport type AiLogicJobMode = (typeof AI_LOGIC_JOB_MODES)[number]\n\nexport type AiLogicJobInputs =\n | { mode: 'function'; functionId: string | null }\n | { mode: 'variable' }\n | { mode: 'explain'; functionId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\nexport const AI_LOGIC_PICK_FUNCTION_COPY = 'Pick the function to explain'\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiLogicJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiLogicJobInputs | string {\n const mode = inputs?.['mode'] ?? 'function'\n if (!(AI_LOGIC_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be function, variable or explain'\n }\n if (mode === 'variable') return { mode }\n const functionId = inputs?.['functionId']\n const named = typeof functionId === 'string' && DOCUMENT_ID.test(functionId) ? functionId : null\n if (functionId != null && !named) return AI_LOGIC_PICK_FUNCTION_COPY\n if (mode === 'explain') return named ? { mode, functionId: named } : AI_LOGIC_PICK_FUNCTION_COPY\n return { mode: 'function', functionId: named }\n}\n\n// ── The bounds a proposal is held to ──────────────────────────────────────\n\nexport const AI_LOGIC_MAX_PARAMETERS = 12\nexport const AI_LOGIC_MAX_LOCALS = 20\nexport const AI_LOGIC_MAX_OPERATIONS = 24\nexport const AI_LOGIC_MAX_SETS = 12\nexport const AI_LOGIC_EXPRESSION_MAX_CHARS = 400\nexport const AI_LOGIC_LABEL_MAX_CHARS = 80\nexport const AI_LOGIC_VARIABLE_VALUE_MAX_CHARS = 2_000\n\nexport const AI_LOGIC_VALUE_TYPES: readonly FunctionValueType[] = ['number', 'text', 'boolean']\nexport const AI_LOGIC_COMPARATORS: readonly FunctionComparator[] = ['==', '!=', '<', '<=', '>', '>=']\n\n/** The variable types a proposal may use: every type the Variables editor offers. */\nexport const AI_LOGIC_VARIABLE_TYPES: readonly HostVariableType[] = [\n 'text',\n 'number',\n 'boolean',\n 'date',\n 'time',\n 'dictionary',\n 'collection',\n]\n\n/** A site variable as a proposal is checked against: its name, type and value. */\nexport type AiLogicSiteVariable = Pick<HostVariable, 'name' | 'type' | 'value'>\n\n/** What a proposal's names are checked against: the site's variables and functions. */\nexport interface AiLogicContext {\n variables: readonly AiLogicSiteVariable[]\n /** The site's function names, which a new function's name must not repeat. */\n functions: readonly string[]\n /** The saved function a change starts from; its own name is free to keep. */\n editing?: string | null\n}\n\nexport interface AiLogicCheckResult<T> {\n value: T | null\n violations: AiDoctrineViolation[]\n}\n\nconst violation = (code: string, message: string): AiDoctrineViolation => ({ rule: null, code, message })\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst MARKUP = /<\\s*\\/?\\s*(script|iframe|style|object|embed)\\b|javascript:/i\n\n/** A starting value a parameter's type can hold, for the run every proposal is given. */\nfunction sampleFor(type: FunctionValueType, defaultValue: string | undefined): string {\n if (defaultValue) return defaultValue\n return type === 'number' ? '1' : type === 'boolean' ? 'true' : 'sample'\n}\n\n/**\n * A `submit_function` answer read into a definition the function editor\n * opens, or the violations a re-ask names. Only what the evaluator can run\n * is kept: an expression it cannot parse, a name it cannot find, a SET on a\n * site variable, or a run that fails on starting values is refused.\n */\nexport function checkAiLogicFunction(\n answer: Record<string, unknown>,\n context: AiLogicContext,\n): AiLogicCheckResult<HostFunction> {\n const violations: AiDoctrineViolation[] = []\n const name = str(answer['name'])\n if (!VARIABLE_NAME_PATTERN.test(name)) {\n violations.push(\n violation('logic-name', 'The function name must start with a letter or _, then letters, digits or _; at most 40 characters.'),\n )\n } else if (name !== context.editing && context.functions.includes(name)) {\n violations.push(violation('logic-name-taken', `The site already has a function named ${name}. Choose another name.`))\n }\n\n const own = new Set<string>()\n const ownName = (raw: unknown, what: string): string => {\n const value = str(raw)\n if (!VARIABLE_NAME_PATTERN.test(value)) {\n violations.push(violation('logic-name', `A ${what} name must start with a letter or _, then letters, digits or _.`))\n } else if (own.has(value)) {\n violations.push(violation('logic-name-repeated', `The name ${value} is used twice. Each parameter and local needs its own name.`))\n }\n own.add(value)\n return value\n }\n const typeOf = (raw: unknown): FunctionValueType => {\n if ((AI_LOGIC_VALUE_TYPES as readonly unknown[]).includes(raw)) return raw as FunctionValueType\n violations.push(violation('logic-type', 'A type is number, text or boolean.'))\n return 'number'\n }\n\n const rawParameters = Array.isArray(answer['parameters']) ? answer['parameters'].filter(isRecord) : []\n const rawLocals = Array.isArray(answer['locals']) ? answer['locals'].filter(isRecord) : []\n const rawOperations = Array.isArray(answer['operations']) ? answer['operations'].filter(isRecord) : []\n if (rawParameters.length > AI_LOGIC_MAX_PARAMETERS) {\n violations.push(violation('logic-size', `At most ${AI_LOGIC_MAX_PARAMETERS} parameters.`))\n }\n if (rawLocals.length > AI_LOGIC_MAX_LOCALS) {\n violations.push(violation('logic-size', `At most ${AI_LOGIC_MAX_LOCALS} locals.`))\n }\n if (!rawOperations.length || rawOperations.length > AI_LOGIC_MAX_OPERATIONS) {\n violations.push(violation('logic-size', `Between 1 and ${AI_LOGIC_MAX_OPERATIONS} operations.`))\n }\n\n const parameters = rawParameters.slice(0, AI_LOGIC_MAX_PARAMETERS).map((raw) => {\n const parameter: HostFunction['parameters'][number] = {\n name: ownName(raw['name'], 'parameter'),\n type: typeOf(raw['type']),\n required: raw['required'] === true,\n }\n const label = str(raw['label']).slice(0, AI_LOGIC_LABEL_MAX_CHARS)\n if (label) parameter.label = label\n const start = str(raw['defaultValue']).slice(0, AI_LOGIC_LABEL_MAX_CHARS)\n if (start) parameter.defaultValue = start\n return parameter\n })\n const variables = rawLocals.slice(0, AI_LOGIC_MAX_LOCALS).map((raw) => ({\n name: ownName(raw['name'], 'local'),\n type: typeOf(raw['type']),\n }))\n\n const site = new Set(context.variables.map((variable) => variable.name))\n const expression = (raw: unknown, where: string): string => {\n const text = typeof raw === 'string' ? raw.trim() : ''\n if (!text) {\n violations.push(violation('logic-expression', `${where} is empty. Write a value, a name or an expression.`))\n return text\n }\n if (text.length > AI_LOGIC_EXPRESSION_MAX_CHARS || MARKUP.test(text)) {\n violations.push(violation('logic-expression', `${where} is not an expression the function can run.`))\n return text\n }\n const problem = expressionSyntaxError(text)\n if (problem) violations.push(violation('logic-syntax', `${where} cannot be read: ${problem}.`))\n return text\n }\n const sets = (raw: unknown, where: string) => {\n const list = Array.isArray(raw) ? raw.filter(isRecord) : []\n if (list.length > AI_LOGIC_MAX_SETS) {\n violations.push(violation('logic-size', `${where} has more than ${AI_LOGIC_MAX_SETS} assignments.`))\n }\n return list.slice(0, AI_LOGIC_MAX_SETS).map((set, index) => {\n const target = str(set['set'])\n if (!own.has(target)) {\n violations.push(\n violation(\n 'logic-set-target',\n site.has(target)\n ? `${where}, assignment ${index + 1}, sets the site variable ${target}. A function reads site variables and sets only its own parameters and locals.`\n : `${where}, assignment ${index + 1}, sets ${target || 'nothing'}, which is not one of the function's parameters or locals.`,\n ),\n )\n }\n return { set: target, expression: expression(set['expression'], `${where}, assignment ${index + 1}`) }\n })\n }\n const operations = rawOperations.slice(0, AI_LOGIC_MAX_OPERATIONS).map((raw, index) => {\n const where = `Operation ${index + 1}`\n const condition = isRecord(raw['if']) ? raw['if'] : {}\n const comparator = (AI_LOGIC_COMPARATORS as readonly unknown[]).includes(condition['comparator'])\n ? (condition['comparator'] as FunctionComparator)\n : null\n if (!comparator) violations.push(violation('logic-comparator', `${where}'s condition compares with one of == != < <= > >=.`))\n return {\n if: {\n left: expression(condition['left'], `${where}'s condition (left)`),\n comparator: comparator ?? '==',\n right: expression(condition['right'], `${where}'s condition (right)`),\n },\n then: sets(raw['then'], `${where} (then)`),\n otherwise: sets(raw['otherwise'], `${where} (otherwise)`),\n }\n })\n\n const returnValue = str(answer['returnValue'])\n if (!own.has(returnValue)) {\n violations.push(violation('logic-return', 'returnValue names one of the function’s own parameters or locals.'))\n }\n const definition: HostFunction = { name, parameters, variables, operations, returnValue }\n\n // Every name an expression reads that is not the function's own must be a site variable.\n const unknown = functionReferencedNames(definition).filter((read) => !site.has(read))\n if (unknown.length) {\n violations.push(\n violation(\n 'logic-unknown-name',\n `The function reads ${unknown.join(', ')}, which is neither one of its own names nor a site variable. Use only the names listed.`,\n ),\n )\n }\n\n if (!violations.length) {\n // One run with every parameter at a starting value: what the editor's\n // own test run would show first, so a definition that cannot run on its\n // first try is refused here rather than handed to a person.\n const variablesByName = Object.fromEntries(\n context.variables.map((variable) => [variable.name, variable as HostVariable]),\n )\n const args = Object.fromEntries(\n parameters.map((parameter) => [parameter.name, sampleFor(parameter.type, parameter.defaultValue)]),\n )\n const run = evaluateHostFunction(definition, args, { globals: functionGlobals(definition, variablesByName) })\n if (run.ok === false) violations.push(violation('logic-run', `The function fails when it runs: ${run.error}.`))\n }\n return { value: violations.length ? null : definition, violations }\n}\n\n/** A `submit_variable` answer read into the variable the Variables editor opens. */\nexport function checkAiLogicVariable(\n answer: Record<string, unknown>,\n context: Pick<AiLogicContext, 'variables'>,\n): AiLogicCheckResult<AiLogicSiteVariable> {\n const violations: AiDoctrineViolation[] = []\n const name = str(answer['name'])\n if (!VARIABLE_NAME_PATTERN.test(name)) {\n violations.push(violation('logic-name', 'The variable name must start with a letter or _, then letters, digits or _; at most 40 characters.'))\n } else if (context.variables.some((variable) => variable.name === name)) {\n violations.push(violation('logic-name-taken', `The site already has a variable named ${name}. Choose another name.`))\n }\n const type = answer['type']\n if (!(AI_LOGIC_VARIABLE_TYPES as readonly unknown[]).includes(type)) {\n violations.push(violation('logic-type', `A variable's type is one of ${AI_LOGIC_VARIABLE_TYPES.join(', ')}.`))\n }\n const value = typeof answer['value'] === 'string' ? answer['value'] : ''\n if (value.length > AI_LOGIC_VARIABLE_VALUE_MAX_CHARS || MARKUP.test(value)) {\n violations.push(violation('logic-value', 'The value is too long, or holds markup.'))\n } else if (type === 'number' && !Number.isFinite(Number(value))) {\n violations.push(violation('logic-value', 'A number variable’s value is a number, such as 49 or 0.5.'))\n } else if (type === 'boolean' && value !== 'true' && value !== 'false') {\n violations.push(violation('logic-value', 'A boolean variable’s value is true or false.'))\n } else if (type === 'date' && Number.isNaN(new Date(value).getTime())) {\n violations.push(violation('logic-value', 'A date variable’s value is a date such as 2026-10-01.'))\n } else if (type === 'dictionary' || type === 'collection') {\n let parsed: unknown\n try {\n parsed = JSON.parse(value)\n } catch {\n parsed = null\n }\n const fits = type === 'collection' ? Array.isArray(parsed) : isRecord(parsed)\n if (!fits) {\n violations.push(\n violation(\n 'logic-value',\n type === 'collection'\n ? 'A collection’s value is a JSON list, such as [\"red\",\"green\"].'\n : 'A dictionary’s value is a JSON object of names to values, such as {\"starter\":19,\"pro\":49}.',\n ),\n )\n }\n }\n return {\n value: violations.length ? null : { name, type: type as HostVariableType, value },\n violations,\n }\n}\n\n// ── What a job hands the logic editor ─────────────────────────────────────\n\n/** The output resource a logic proposal rides on. */\nexport const AI_LOGIC_RESOURCE = 'logic'\n\n/** A logic output's proposal: a function to open in the editor, or a variable. */\nexport type AiLogicProposal =\n | { kind: 'function'; functionId: string | null; definition: HostFunction }\n | { kind: 'variable'; variable: AiLogicSiteVariable }\n\n/**\n * A proposal read back from a job's output, in the browser, before the\n * editor is handed it; `null` for anything that is not one. The server held\n * the definition to the grammar; this only refuses a shape that is not one.\n */\nexport function readAiLogicProposal(value: unknown): AiLogicProposal | null {\n if (!isRecord(value)) return null\n if (value['kind'] === 'variable' && isRecord(value['variable'])) {\n const variable = value['variable']\n if (typeof variable['name'] !== 'string' || typeof variable['type'] !== 'string') return null\n return {\n kind: 'variable',\n variable: {\n name: variable['name'],\n type: variable['type'] as HostVariableType,\n value: typeof variable['value'] === 'string' ? variable['value'] : '',\n },\n }\n }\n if (value['kind'] !== 'function' || !isRecord(value['definition'])) return null\n const definition = value['definition']\n if (\n typeof definition['name'] !== 'string' ||\n !Array.isArray(definition['parameters']) ||\n !Array.isArray(definition['variables']) ||\n !Array.isArray(definition['operations'])\n ) {\n return null\n }\n return {\n kind: 'function',\n functionId: typeof value['functionId'] === 'string' ? value['functionId'] : null,\n definition: definition as unknown as HostFunction,\n }\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\nexport const AI_LOGIC_NO_SITE_COPY = 'This logic job names a site this workspace does not have.'\nexport const AI_LOGIC_UNAVAILABLE_COPY = 'Turn on Logic for this site before starting the job.'\nexport const AI_LOGIC_GONE_COPY = 'That function no longer exists.'\nexport const AI_LOGIC_NO_FUNCTION_COPY =\n 'The AI could not write a function the site can run from this description. Try describing what goes in, and what should come out.'\nexport const AI_LOGIC_NO_VARIABLE_COPY =\n 'The AI could not write a variable from this description. Try naming what it holds, and its value.'\nexport const AI_LOGIC_NO_EXPLANATION_COPY = 'The AI could not explain this function. Try again.'\n"],"names":["evaluateHostFunction","expressionSyntaxError","functionReferencedNames","functionGlobals","VARIABLE_NAME_PATTERN","AI_LOGIC_JOB_MODES","DOCUMENT_ID","AI_LOGIC_PICK_FUNCTION_COPY","parseAiLogicJobInputs","inputs","mode","includes","functionId","named","test","AI_LOGIC_MAX_PARAMETERS","AI_LOGIC_MAX_LOCALS","AI_LOGIC_MAX_OPERATIONS","AI_LOGIC_MAX_SETS","AI_LOGIC_EXPRESSION_MAX_CHARS","AI_LOGIC_LABEL_MAX_CHARS","AI_LOGIC_VARIABLE_VALUE_MAX_CHARS","AI_LOGIC_VALUE_TYPES","AI_LOGIC_COMPARATORS","AI_LOGIC_VARIABLE_TYPES","violation","code","message","rule","isRecord","value","Array","isArray","str","trim","MARKUP","sampleFor","type","defaultValue","checkAiLogicFunction","answer","context","violations","name","push","editing","functions","own","Set","ownName","raw","what","has","add","typeOf","rawParameters","filter","rawLocals","rawOperations","length","parameters","slice","map","parameter","required","label","start","variables","site","variable","expression","where","text","problem","sets","list","set","index","target","operations","condition","comparator","if","left","right","then","otherwise","returnValue","definition","unknown","read","join","variablesByName","Object","fromEntries","args","run","globals","ok","error","checkAiLogicVariable","some","Number","isFinite","isNaN","Date","getTime","parsed","JSON","parse","fits","AI_LOGIC_RESOURCE","readAiLogicProposal","kind","AI_LOGIC_NO_SITE_COPY","AI_LOGIC_UNAVAILABLE_COPY","AI_LOGIC_GONE_COPY","AI_LOGIC_NO_FUNCTION_COPY","AI_LOGIC_NO_VARIABLE_COPY","AI_LOGIC_NO_EXPLANATION_COPY"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-logic-job.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n evaluateHostFunction,\n expressionSyntaxError,\n type FunctionComparator,\n type FunctionValueType,\n type HostFunction,\n functionReferencedNames,\n} from '@aglyn/aglyn/app-utils/functions'\nimport {\n functionGlobals,\n type HostVariable,\n type HostVariableType,\n VARIABLE_NAME_PATTERN,\n} from '@aglyn/aglyn/app-utils/variables'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\n\n/**\n * A `logic` job (AGL-3603): a site's Functions & Variables, by AI.\n *\n * - `function` proposes a function — parameters, locals, the numbered\n * if/then/otherwise operations and the value it returns — from a\n * description, or, naming a saved function, a changed or fixed copy of\n * it. Every expression is held to the evaluator's own grammar\n * (`expressionSyntaxError`), every name it reads to the function's own\n * names and the site's variables, and the whole definition is run once\n * with each parameter at its starting value before anyone sees it.\n * - `variable` proposes one site variable: a name, a type and a value in\n * that type's stored form.\n * - `explain` reads a saved function and answers in plain words what it\n * computes, and anything in it worth checking.\n *\n * Nothing is written: a proposal rides on the job's output, and the logic\n * editor is where a person saves it, or does not. The one exception is a\n * NEW function asked for by a build (AGL-3616), which the person confirmed\n * as a plan: that unit's job writes the checked function through the logic\n * plugin's `function` draft writer, a record nothing on the site uses until\n * a page binds it.\n */\n\nexport const AI_LOGIC_JOB_MODES = ['function', 'variable', 'explain'] as const\nexport type AiLogicJobMode = (typeof AI_LOGIC_JOB_MODES)[number]\n\nexport type AiLogicJobInputs =\n | { mode: 'function'; functionId: string | null }\n | { mode: 'variable' }\n | { mode: 'explain'; functionId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\nexport const AI_LOGIC_PICK_FUNCTION_COPY = 'Pick the function to explain'\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiLogicJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiLogicJobInputs | string {\n const mode = inputs?.['mode'] ?? 'function'\n if (!(AI_LOGIC_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be function, variable or explain'\n }\n if (mode === 'variable') return { mode }\n const functionId = inputs?.['functionId']\n const named = typeof functionId === 'string' && DOCUMENT_ID.test(functionId) ? functionId : null\n if (functionId != null && !named) return AI_LOGIC_PICK_FUNCTION_COPY\n if (mode === 'explain') return named ? { mode, functionId: named } : AI_LOGIC_PICK_FUNCTION_COPY\n return { mode: 'function', functionId: named }\n}\n\n// ── The bounds a proposal is held to ──────────────────────────────────────\n\nexport const AI_LOGIC_MAX_PARAMETERS = 12\nexport const AI_LOGIC_MAX_LOCALS = 20\nexport const AI_LOGIC_MAX_OPERATIONS = 24\nexport const AI_LOGIC_MAX_SETS = 12\nexport const AI_LOGIC_EXPRESSION_MAX_CHARS = 400\nexport const AI_LOGIC_LABEL_MAX_CHARS = 80\nexport const AI_LOGIC_VARIABLE_VALUE_MAX_CHARS = 2_000\n\nexport const AI_LOGIC_VALUE_TYPES: readonly FunctionValueType[] = ['number', 'text', 'boolean']\nexport const AI_LOGIC_COMPARATORS: readonly FunctionComparator[] = ['==', '!=', '<', '<=', '>', '>=']\n\n/** The variable types a proposal may use: every type the Variables editor offers. */\nexport const AI_LOGIC_VARIABLE_TYPES: readonly HostVariableType[] = [\n 'text',\n 'number',\n 'boolean',\n 'date',\n 'time',\n 'dictionary',\n 'collection',\n]\n\n/** A site variable as a proposal is checked against: its name, type and value. */\nexport type AiLogicSiteVariable = Pick<HostVariable, 'name' | 'type' | 'value'>\n\n/** What a proposal's names are checked against: the site's variables and functions. */\nexport interface AiLogicContext {\n variables: readonly AiLogicSiteVariable[]\n /** The site's function names, which a new function's name must not repeat. */\n functions: readonly string[]\n /** The saved function a change starts from; its own name is free to keep. */\n editing?: string | null\n}\n\nexport interface AiLogicCheckResult<T> {\n value: T | null\n violations: AiDoctrineViolation[]\n}\n\nconst violation = (code: string, message: string): AiDoctrineViolation => ({ rule: null, code, message })\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst MARKUP = /<\\s*\\/?\\s*(script|iframe|style|object|embed)\\b|javascript:/i\n\n/** A starting value a parameter's type can hold, for the run every proposal is given. */\nfunction sampleFor(type: FunctionValueType, defaultValue: string | undefined): string {\n if (defaultValue) return defaultValue\n return type === 'number' ? '1' : type === 'boolean' ? 'true' : 'sample'\n}\n\n/**\n * A `submit_function` answer read into a definition the function editor\n * opens, or the violations a re-ask names. Only what the evaluator can run\n * is kept: an expression it cannot parse, a name it cannot find, a SET on a\n * site variable, or a run that fails on starting values is refused.\n */\nexport function checkAiLogicFunction(\n answer: Record<string, unknown>,\n context: AiLogicContext,\n): AiLogicCheckResult<HostFunction> {\n const violations: AiDoctrineViolation[] = []\n const name = str(answer['name'])\n if (!VARIABLE_NAME_PATTERN.test(name)) {\n violations.push(\n violation('logic-name', 'The function name must start with a letter or _, then letters, digits or _; at most 40 characters.'),\n )\n } else if (\n name.toLowerCase() !== context.editing?.toLowerCase() &&\n context.functions.some((taken) => taken.toLowerCase() === name.toLowerCase())\n ) {\n // Case-insensitive, as the Functions card and the logic plugin's writer compare names.\n violations.push(violation('logic-name-taken', `The site already has a function named ${name}. Choose another name.`))\n }\n\n const own = new Set<string>()\n const ownName = (raw: unknown, what: string): string => {\n const value = str(raw)\n if (!VARIABLE_NAME_PATTERN.test(value)) {\n violations.push(violation('logic-name', `A ${what} name must start with a letter or _, then letters, digits or _.`))\n } else if (own.has(value)) {\n violations.push(violation('logic-name-repeated', `The name ${value} is used twice. Each parameter and local needs its own name.`))\n }\n own.add(value)\n return value\n }\n const typeOf = (raw: unknown): FunctionValueType => {\n if ((AI_LOGIC_VALUE_TYPES as readonly unknown[]).includes(raw)) return raw as FunctionValueType\n violations.push(violation('logic-type', 'A type is number, text or boolean.'))\n return 'number'\n }\n\n const rawParameters = Array.isArray(answer['parameters']) ? answer['parameters'].filter(isRecord) : []\n const rawLocals = Array.isArray(answer['locals']) ? answer['locals'].filter(isRecord) : []\n const rawOperations = Array.isArray(answer['operations']) ? answer['operations'].filter(isRecord) : []\n if (rawParameters.length > AI_LOGIC_MAX_PARAMETERS) {\n violations.push(violation('logic-size', `At most ${AI_LOGIC_MAX_PARAMETERS} parameters.`))\n }\n if (rawLocals.length > AI_LOGIC_MAX_LOCALS) {\n violations.push(violation('logic-size', `At most ${AI_LOGIC_MAX_LOCALS} locals.`))\n }\n if (!rawOperations.length || rawOperations.length > AI_LOGIC_MAX_OPERATIONS) {\n violations.push(violation('logic-size', `Between 1 and ${AI_LOGIC_MAX_OPERATIONS} operations.`))\n }\n\n const parameters = rawParameters.slice(0, AI_LOGIC_MAX_PARAMETERS).map((raw) => {\n const parameter: HostFunction['parameters'][number] = {\n name: ownName(raw['name'], 'parameter'),\n type: typeOf(raw['type']),\n required: raw['required'] === true,\n }\n const label = str(raw['label']).slice(0, AI_LOGIC_LABEL_MAX_CHARS)\n if (label) parameter.label = label\n const start = str(raw['defaultValue']).slice(0, AI_LOGIC_LABEL_MAX_CHARS)\n if (start) parameter.defaultValue = start\n return parameter\n })\n const variables = rawLocals.slice(0, AI_LOGIC_MAX_LOCALS).map((raw) => ({\n name: ownName(raw['name'], 'local'),\n type: typeOf(raw['type']),\n }))\n\n const site = new Set(context.variables.map((variable) => variable.name))\n const expression = (raw: unknown, where: string): string => {\n const text = typeof raw === 'string' ? raw.trim() : ''\n if (!text) {\n violations.push(violation('logic-expression', `${where} is empty. Write a value, a name or an expression.`))\n return text\n }\n if (text.length > AI_LOGIC_EXPRESSION_MAX_CHARS || MARKUP.test(text)) {\n violations.push(violation('logic-expression', `${where} is not an expression the function can run.`))\n return text\n }\n const problem = expressionSyntaxError(text)\n if (problem) violations.push(violation('logic-syntax', `${where} cannot be read: ${problem}.`))\n return text\n }\n const sets = (raw: unknown, where: string) => {\n const list = Array.isArray(raw) ? raw.filter(isRecord) : []\n if (list.length > AI_LOGIC_MAX_SETS) {\n violations.push(violation('logic-size', `${where} has more than ${AI_LOGIC_MAX_SETS} assignments.`))\n }\n return list.slice(0, AI_LOGIC_MAX_SETS).map((set, index) => {\n const target = str(set['set'])\n if (!own.has(target)) {\n violations.push(\n violation(\n 'logic-set-target',\n site.has(target)\n ? `${where}, assignment ${index + 1}, sets the site variable ${target}. A function reads site variables and sets only its own parameters and locals.`\n : `${where}, assignment ${index + 1}, sets ${target || 'nothing'}, which is not one of the function's parameters or locals.`,\n ),\n )\n }\n return { set: target, expression: expression(set['expression'], `${where}, assignment ${index + 1}`) }\n })\n }\n const operations = rawOperations.slice(0, AI_LOGIC_MAX_OPERATIONS).map((raw, index) => {\n const where = `Operation ${index + 1}`\n const condition = isRecord(raw['if']) ? raw['if'] : {}\n const comparator = (AI_LOGIC_COMPARATORS as readonly unknown[]).includes(condition['comparator'])\n ? (condition['comparator'] as FunctionComparator)\n : null\n if (!comparator) violations.push(violation('logic-comparator', `${where}'s condition compares with one of == != < <= > >=.`))\n return {\n if: {\n left: expression(condition['left'], `${where}'s condition (left)`),\n comparator: comparator ?? '==',\n right: expression(condition['right'], `${where}'s condition (right)`),\n },\n then: sets(raw['then'], `${where} (then)`),\n otherwise: sets(raw['otherwise'], `${where} (otherwise)`),\n }\n })\n\n const returnValue = str(answer['returnValue'])\n if (!own.has(returnValue)) {\n violations.push(violation('logic-return', 'returnValue names one of the function’s own parameters or locals.'))\n }\n const definition: HostFunction = { name, parameters, variables, operations, returnValue }\n\n // Every name an expression reads that is not the function's own must be a site variable.\n const unknown = functionReferencedNames(definition).filter((read) => !site.has(read))\n if (unknown.length) {\n violations.push(\n violation(\n 'logic-unknown-name',\n `The function reads ${unknown.join(', ')}, which is neither one of its own names nor a site variable. Use only the names listed.`,\n ),\n )\n }\n\n if (!violations.length) {\n // One run with every parameter at a starting value: what the editor's\n // own test run would show first, so a definition that cannot run on its\n // first try is refused here rather than handed to a person.\n const variablesByName = Object.fromEntries(\n context.variables.map((variable) => [variable.name, variable as HostVariable]),\n )\n const args = Object.fromEntries(\n parameters.map((parameter) => [parameter.name, sampleFor(parameter.type, parameter.defaultValue)]),\n )\n const run = evaluateHostFunction(definition, args, { globals: functionGlobals(definition, variablesByName) })\n if (run.ok === false) violations.push(violation('logic-run', `The function fails when it runs: ${run.error}.`))\n }\n return { value: violations.length ? null : definition, violations }\n}\n\n/** A `submit_variable` answer read into the variable the Variables editor opens. */\nexport function checkAiLogicVariable(\n answer: Record<string, unknown>,\n context: Pick<AiLogicContext, 'variables'>,\n): AiLogicCheckResult<AiLogicSiteVariable> {\n const violations: AiDoctrineViolation[] = []\n const name = str(answer['name'])\n if (!VARIABLE_NAME_PATTERN.test(name)) {\n violations.push(violation('logic-name', 'The variable name must start with a letter or _, then letters, digits or _; at most 40 characters.'))\n } else if (context.variables.some((variable) => variable.name.toLowerCase() === name.toLowerCase())) {\n // Case-insensitive, as the Variables card and the logic plugin's writer compare names.\n violations.push(violation('logic-name-taken', `The site already has a variable named ${name}. Choose another name.`))\n }\n const type = answer['type']\n if (!(AI_LOGIC_VARIABLE_TYPES as readonly unknown[]).includes(type)) {\n violations.push(violation('logic-type', `A variable's type is one of ${AI_LOGIC_VARIABLE_TYPES.join(', ')}.`))\n }\n const value = typeof answer['value'] === 'string' ? answer['value'] : ''\n if (value.length > AI_LOGIC_VARIABLE_VALUE_MAX_CHARS || MARKUP.test(value)) {\n violations.push(violation('logic-value', 'The value is too long, or holds markup.'))\n } else if (type === 'number' && !Number.isFinite(Number(value))) {\n violations.push(violation('logic-value', 'A number variable’s value is a number, such as 49 or 0.5.'))\n } else if (type === 'boolean' && value !== 'true' && value !== 'false') {\n violations.push(violation('logic-value', 'A boolean variable’s value is true or false.'))\n } else if (type === 'date' && Number.isNaN(new Date(value).getTime())) {\n violations.push(violation('logic-value', 'A date variable’s value is a date such as 2026-10-01.'))\n } else if (type === 'dictionary' || type === 'collection') {\n let parsed: unknown\n try {\n parsed = JSON.parse(value)\n } catch {\n parsed = null\n }\n const fits = type === 'collection' ? Array.isArray(parsed) : isRecord(parsed)\n if (!fits) {\n violations.push(\n violation(\n 'logic-value',\n type === 'collection'\n ? 'A collection’s value is a JSON list, such as [\"red\",\"green\"].'\n : 'A dictionary’s value is a JSON object of names to values, such as {\"starter\":19,\"pro\":49}.',\n ),\n )\n }\n }\n return {\n value: violations.length ? null : { name, type: type as HostVariableType, value },\n violations,\n }\n}\n\n// ── What a job hands the logic editor ─────────────────────────────────────\n\n/** The output resource a logic proposal rides on. */\nexport const AI_LOGIC_RESOURCE = 'logic'\n\n/** A logic output's proposal: a function to open in the editor, or a variable. */\nexport type AiLogicProposal =\n | { kind: 'function'; functionId: string | null; definition: HostFunction }\n | { kind: 'variable'; variable: AiLogicSiteVariable }\n\n/**\n * A proposal read back from a job's output, in the browser, before the\n * editor is handed it; `null` for anything that is not one. The server held\n * the definition to the grammar; this only refuses a shape that is not one.\n */\nexport function readAiLogicProposal(value: unknown): AiLogicProposal | null {\n if (!isRecord(value)) return null\n if (value['kind'] === 'variable' && isRecord(value['variable'])) {\n const variable = value['variable']\n if (typeof variable['name'] !== 'string' || typeof variable['type'] !== 'string') return null\n return {\n kind: 'variable',\n variable: {\n name: variable['name'],\n type: variable['type'] as HostVariableType,\n value: typeof variable['value'] === 'string' ? variable['value'] : '',\n },\n }\n }\n if (value['kind'] !== 'function' || !isRecord(value['definition'])) return null\n const definition = value['definition']\n if (\n typeof definition['name'] !== 'string' ||\n !Array.isArray(definition['parameters']) ||\n !Array.isArray(definition['variables']) ||\n !Array.isArray(definition['operations'])\n ) {\n return null\n }\n return {\n kind: 'function',\n functionId: typeof value['functionId'] === 'string' ? value['functionId'] : null,\n definition: definition as unknown as HostFunction,\n }\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\nexport const AI_LOGIC_NO_SITE_COPY = 'This logic job names a site this workspace does not have.'\nexport const AI_LOGIC_UNAVAILABLE_COPY = 'Turn on Logic for this site before starting the job.'\nexport const AI_LOGIC_GONE_COPY = 'That function no longer exists.'\nexport const AI_LOGIC_NO_FUNCTION_COPY =\n 'The AI could not write a function the site can run from this description. Try describing what goes in, and what should come out.'\nexport const AI_LOGIC_NO_VARIABLE_COPY =\n 'The AI could not write a variable from this description. Try naming what it holds, and its value.'\nexport const AI_LOGIC_NO_EXPLANATION_COPY = 'The AI could not explain this function. Try again.'\n\n// ── A function a build writes (AGL-3616) ──────────────────────────────────\n\n/**\n * The resource the logic plugin's `function` draft writer is registered\n * under. A `logic` job run as a unit of a build writes its function through\n * it; one a person started proposes it and writes nothing.\n */\nexport const AI_LOGIC_FUNCTION_RESOURCE = 'function'\n\n/** Whether a job is a build's unit rather than one a person started: a derived job carries its origin. */\nexport function aiLogicJobIsBuildUnit(job: { inputs?: Readonly<Record<string, unknown>> | null }): boolean {\n const origin = job.inputs?.['originJobId']\n return typeof origin === 'string' && origin.length > 0\n}\n\nexport const AI_LOGIC_FUNCTION_UNAVAILABLE_COPY = 'Functions are not available on this site.'\nexport const AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY = 'The function was written but could not be saved. Try the job again.'\nexport const AI_LOGIC_FUNCTION_WRITTEN_NOTE =\n 'A new site function, checked against the expression grammar and run once. Nothing on the site uses it until a page binds it.'\n"],"names":["evaluateHostFunction","expressionSyntaxError","functionReferencedNames","functionGlobals","VARIABLE_NAME_PATTERN","AI_LOGIC_JOB_MODES","DOCUMENT_ID","AI_LOGIC_PICK_FUNCTION_COPY","parseAiLogicJobInputs","inputs","mode","includes","functionId","named","test","AI_LOGIC_MAX_PARAMETERS","AI_LOGIC_MAX_LOCALS","AI_LOGIC_MAX_OPERATIONS","AI_LOGIC_MAX_SETS","AI_LOGIC_EXPRESSION_MAX_CHARS","AI_LOGIC_LABEL_MAX_CHARS","AI_LOGIC_VARIABLE_VALUE_MAX_CHARS","AI_LOGIC_VALUE_TYPES","AI_LOGIC_COMPARATORS","AI_LOGIC_VARIABLE_TYPES","violation","code","message","rule","isRecord","value","Array","isArray","str","trim","MARKUP","sampleFor","type","defaultValue","checkAiLogicFunction","answer","context","violations","name","push","toLowerCase","editing","functions","some","taken","own","Set","ownName","raw","what","has","add","typeOf","rawParameters","filter","rawLocals","rawOperations","length","parameters","slice","map","parameter","required","label","start","variables","site","variable","expression","where","text","problem","sets","list","set","index","target","operations","condition","comparator","if","left","right","then","otherwise","returnValue","definition","unknown","read","join","variablesByName","Object","fromEntries","args","run","globals","ok","error","checkAiLogicVariable","Number","isFinite","isNaN","Date","getTime","parsed","JSON","parse","fits","AI_LOGIC_RESOURCE","readAiLogicProposal","kind","AI_LOGIC_NO_SITE_COPY","AI_LOGIC_UNAVAILABLE_COPY","AI_LOGIC_GONE_COPY","AI_LOGIC_NO_FUNCTION_COPY","AI_LOGIC_NO_VARIABLE_COPY","AI_LOGIC_NO_EXPLANATION_COPY","AI_LOGIC_FUNCTION_RESOURCE","aiLogicJobIsBuildUnit","job","origin","AI_LOGIC_FUNCTION_UNAVAILABLE_COPY","AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY","AI_LOGIC_FUNCTION_WRITTEN_NOTE"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { AiUsage } from '../providers/contract';
|
|
18
|
-
import type { AiImageMode } from '../providers/image-contract';
|
|
18
|
+
import type { AiImageMode, AiImageSize } from '../providers/image-contract';
|
|
19
19
|
/**
|
|
20
20
|
* What one picture in Media is expected to draw (AGL-3602), in credits: the
|
|
21
21
|
* estimate the dialog shows before anything is spent, and the figure the
|
|
@@ -23,7 +23,8 @@ import type { AiImageMode } from '../providers/image-contract';
|
|
|
23
23
|
*
|
|
24
24
|
* Each is an ESTIMATE at nominal usage, priced at the billed rates:
|
|
25
25
|
*
|
|
26
|
-
* - a photo is one picture at its per-picture rate
|
|
26
|
+
* - a photo is one picture at its per-picture rate for the size the plan
|
|
27
|
+
* makes (512 px on Free, 1K on a paid plan), plus a prompt and the
|
|
27
28
|
* thinking Google's model does before it draws;
|
|
28
29
|
* - an illustration is the text provider's answer — the instructions, the
|
|
29
30
|
* description, and a few thousand tokens of SVG.
|
|
@@ -48,5 +49,8 @@ export declare const AI_MEDIA_SVG_NOMINAL_USAGE: AiUsage;
|
|
|
48
49
|
* text provider serves illustrations on.
|
|
49
50
|
*/
|
|
50
51
|
export declare const AI_MEDIA_SVG_ESTIMATE_MODEL: string;
|
|
51
|
-
/**
|
|
52
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The credits one picture is expected to draw in `mode`, on `model`, at
|
|
54
|
+
* `size` for a photo (1K when absent; an illustration has no size).
|
|
55
|
+
*/
|
|
56
|
+
export declare function aiMediaCreditsPerPicture(mode: AiImageMode, model?: string, size?: AiImageSize): number;
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
|
-
*/
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
var _ref;
|
|
17
18
|
var _AI_MODEL_CATALOG_find;
|
|
18
19
|
import { AI_IMAGE_DEFAULT_MODEL, AI_MODEL_CATALOG, estimateAiBilledUsd } from "../providers/catalog.js";
|
|
19
20
|
import { assistCreditsFromUsd } from "../usage/assist-credits.js";
|
|
@@ -24,7 +25,8 @@ import { assistCreditsFromUsd } from "../usage/assist-credits.js";
|
|
|
24
25
|
*
|
|
25
26
|
* Each is an ESTIMATE at nominal usage, priced at the billed rates:
|
|
26
27
|
*
|
|
27
|
-
* - a photo is one picture at its per-picture rate
|
|
28
|
+
* - a photo is one picture at its per-picture rate for the size the plan
|
|
29
|
+
* makes (512 px on Free, 1K on a paid plan), plus a prompt and the
|
|
28
30
|
* thinking Google's model does before it draws;
|
|
29
31
|
* - an illustration is the text provider's answer — the instructions, the
|
|
30
32
|
* description, and a few thousand tokens of SVG.
|
|
@@ -54,8 +56,13 @@ import { assistCreditsFromUsd } from "../usage/assist-credits.js";
|
|
|
54
56
|
* not known: the catalog's first fast-tier row, which is what the default
|
|
55
57
|
* text provider serves illustrations on.
|
|
56
58
|
*/ export const AI_MEDIA_SVG_ESTIMATE_MODEL = (_ref = (_AI_MODEL_CATALOG_find = AI_MODEL_CATALOG.find((entry)=>entry.tier === 'fast')) == null ? void 0 : _AI_MODEL_CATALOG_find.id) != null ? _ref : AI_MODEL_CATALOG[0].id;
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The credits one picture is expected to draw in `mode`, on `model`, at
|
|
61
|
+
* `size` for a photo (1K when absent; an illustration has no size).
|
|
62
|
+
*/ export function aiMediaCreditsPerPicture(mode, model, size) {
|
|
63
|
+
return mode === 'photo' ? assistCreditsFromUsd(estimateAiBilledUsd(_extends({}, AI_MEDIA_PHOTO_NOMINAL_USAGE, size ? {
|
|
64
|
+
imageSize: size
|
|
65
|
+
} : {}), model != null ? model : AI_IMAGE_DEFAULT_MODEL)) : assistCreditsFromUsd(estimateAiBilledUsd(AI_MEDIA_SVG_NOMINAL_USAGE, model != null ? model : AI_MEDIA_SVG_ESTIMATE_MODEL));
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
//# sourceMappingURL=ai-media-credits.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-media-credits.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiUsage } from '../providers/contract'\nimport {\n AI_IMAGE_DEFAULT_MODEL,\n AI_MODEL_CATALOG,\n estimateAiBilledUsd,\n} from '../providers/catalog'\nimport type { AiImageMode } from '../providers/image-contract'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\n\n/**\n * What one picture in Media is expected to draw (AGL-3602), in credits: the\n * estimate the dialog shows before anything is spent, and the figure the\n * door holds a walled band to. Browser-safe, so both read the same number.\n *\n * Each is an ESTIMATE at nominal usage, priced at the billed rates:\n *\n * - a photo is one picture at its per-picture rate, plus a prompt and the\n * thinking Google's model does before it draws;\n * - an illustration is the text provider's answer — the instructions, the\n * description, and a few thousand tokens of SVG.\n *\n * What is actually charged is what was spent, measured: a picture held back\n * or not stored is never charged, and an illustration that needed its second\n * attempt costs about twice its estimate.\n */\n\n/** A photo's nominal tokens beside its picture. */\nexport const AI_MEDIA_PHOTO_NOMINAL_USAGE: AiUsage & { images: number } = {\n inputTokens: 600,\n outputTokens: 1_500,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n images: 1,\n}\n\n/**\n * An illustration's nominal tokens: about 2,100 of instructions — under the\n * fast tier's caching minimum, so read at full rate every time — the tool and\n * the description, and a few thousand of SVG.\n */\nexport const AI_MEDIA_SVG_NOMINAL_USAGE: AiUsage = {\n inputTokens: 2_500,\n outputTokens: 3_000,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/**\n * The model an illustration is estimated at where the server's choice is\n * not known: the catalog's first fast-tier row, which is what the default\n * text provider serves illustrations on.\n */\nexport const AI_MEDIA_SVG_ESTIMATE_MODEL =\n AI_MODEL_CATALOG.find((entry) => entry.tier === 'fast')?.id ?? AI_MODEL_CATALOG[0].id\n\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-media-credits.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiUsage } from '../providers/contract'\nimport {\n AI_IMAGE_DEFAULT_MODEL,\n AI_MODEL_CATALOG,\n estimateAiBilledUsd,\n} from '../providers/catalog'\nimport type { AiImageMode, AiImageSize } from '../providers/image-contract'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\n\n/**\n * What one picture in Media is expected to draw (AGL-3602), in credits: the\n * estimate the dialog shows before anything is spent, and the figure the\n * door holds a walled band to. Browser-safe, so both read the same number.\n *\n * Each is an ESTIMATE at nominal usage, priced at the billed rates:\n *\n * - a photo is one picture at its per-picture rate for the size the plan\n * makes (512 px on Free, 1K on a paid plan), plus a prompt and the\n * thinking Google's model does before it draws;\n * - an illustration is the text provider's answer — the instructions, the\n * description, and a few thousand tokens of SVG.\n *\n * What is actually charged is what was spent, measured: a picture held back\n * or not stored is never charged, and an illustration that needed its second\n * attempt costs about twice its estimate.\n */\n\n/** A photo's nominal tokens beside its picture. */\nexport const AI_MEDIA_PHOTO_NOMINAL_USAGE: AiUsage & { images: number } = {\n inputTokens: 600,\n outputTokens: 1_500,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n images: 1,\n}\n\n/**\n * An illustration's nominal tokens: about 2,100 of instructions — under the\n * fast tier's caching minimum, so read at full rate every time — the tool and\n * the description, and a few thousand of SVG.\n */\nexport const AI_MEDIA_SVG_NOMINAL_USAGE: AiUsage = {\n inputTokens: 2_500,\n outputTokens: 3_000,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/**\n * The model an illustration is estimated at where the server's choice is\n * not known: the catalog's first fast-tier row, which is what the default\n * text provider serves illustrations on.\n */\nexport const AI_MEDIA_SVG_ESTIMATE_MODEL =\n AI_MODEL_CATALOG.find((entry) => entry.tier === 'fast')?.id ?? AI_MODEL_CATALOG[0].id\n\n/**\n * The credits one picture is expected to draw in `mode`, on `model`, at\n * `size` for a photo (1K when absent; an illustration has no size).\n */\nexport function aiMediaCreditsPerPicture(\n mode: AiImageMode,\n model?: string,\n size?: AiImageSize,\n): number {\n return mode === 'photo'\n ? assistCreditsFromUsd(\n estimateAiBilledUsd(\n { ...AI_MEDIA_PHOTO_NOMINAL_USAGE, ...(size ? { imageSize: size } : {}) },\n model ?? AI_IMAGE_DEFAULT_MODEL,\n ),\n )\n : assistCreditsFromUsd(estimateAiBilledUsd(AI_MEDIA_SVG_NOMINAL_USAGE, model ?? AI_MEDIA_SVG_ESTIMATE_MODEL))\n}\n"],"names":["AI_MODEL_CATALOG","AI_IMAGE_DEFAULT_MODEL","estimateAiBilledUsd","assistCreditsFromUsd","AI_MEDIA_PHOTO_NOMINAL_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","images","AI_MEDIA_SVG_NOMINAL_USAGE","AI_MEDIA_SVG_ESTIMATE_MODEL","find","entry","tier","id","aiMediaCreditsPerPicture","mode","model","size","imageSize"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;;IAwDCA;AArDF,SACEC,sBAAsB,EACtBD,gBAAgB,EAChBE,mBAAmB,QACd,0BAAsB;AAE7B,SAASC,oBAAoB,QAAQ,6BAAyB;AAE9D;;;;;;;;;;;;;;;;CAgBC,GAED,iDAAiD,GACjD,OAAO,MAAMC,+BAA6D;IACxEC,aAAa;IACbC,cAAc;IACdC,iBAAiB;IACjBC,kBAAkB;IAClBC,QAAQ;AACV,EAAC;AAED;;;;CAIC,GACD,OAAO,MAAMC,6BAAsC;IACjDL,aAAa;IACbC,cAAc;IACdC,iBAAiB;IACjBC,kBAAkB;AACpB,EAAC;AAED;;;;CAIC,GACD,OAAO,MAAMG,uCACXX,yBAAAA,iBAAiBY,IAAI,CAAC,CAACC,QAAUA,MAAMC,IAAI,KAAK,4BAAhDd,uBAAyDe,EAAE,mBAAIf,gBAAgB,CAAC,EAAE,CAACe,EAAE,CAAA;AAEvF;;;CAGC,GACD,OAAO,SAASC,yBACdC,IAAiB,EACjBC,KAAc,EACdC,IAAkB;IAElB,OAAOF,SAAS,UACZd,qBACED,oBACE,aAAKE,8BAAkCe,OAAO;QAAEC,WAAWD;IAAK,IAAI,CAAC,IACrED,gBAAAA,QAASjB,2BAGbE,qBAAqBD,oBAAoBQ,4BAA4BQ,gBAAAA,QAASP;AACpF"}
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
import { type AiImageAspectRatio, type AiImageMode, type AiRasterStyle, type AiSvgStyle } from '../providers/image-contract';
|
|
18
|
+
/**
|
|
19
|
+
* The kinds of picture Media's Create with AI offers (AGL-3602), as the Kind
|
|
20
|
+
* menu lists them: four drawn as SVG by the text provider, and seventeen made
|
|
21
|
+
* by the image provider — a photo whose description decides its style, six
|
|
22
|
+
* photographic genres, six art styles and four design assets.
|
|
23
|
+
*
|
|
24
|
+
* Browser-safe: the dialog reads this to draw the menu, pick a shape and
|
|
25
|
+
* decide which controls to show, with no request until Create. The style
|
|
26
|
+
* wording a raster kind adds to the description is the server's
|
|
27
|
+
* (`server/ai-media-raster-prompt.ts`), so the browser bundle does not carry
|
|
28
|
+
* it.
|
|
29
|
+
*/
|
|
30
|
+
/** The Kind menu's sections, in order. */
|
|
31
|
+
export declare const AI_MEDIA_KIND_GROUPS: readonly ["vector", "photo", "art", "design"];
|
|
32
|
+
export type AiMediaKindGroup = (typeof AI_MEDIA_KIND_GROUPS)[number];
|
|
33
|
+
/** Each section's heading in the Kind menu. */
|
|
34
|
+
export declare const AI_MEDIA_KIND_GROUP_LABELS: Readonly<Record<AiMediaKindGroup, string>>;
|
|
35
|
+
/** A kind's id: an SVG style, or an image-provider style. */
|
|
36
|
+
export type AiMediaKindId = AiSvgStyle | AiRasterStyle;
|
|
37
|
+
/**
|
|
38
|
+
* Which colors a kind takes. `palette` offers the site theme's colors or the
|
|
39
|
+
* person's own, sent with the request; `describe` sends none and suggests
|
|
40
|
+
* naming colors in the description; `none` is a photograph, whose colors
|
|
41
|
+
* are the scene's.
|
|
42
|
+
*/
|
|
43
|
+
export type AiMediaKindColors = 'palette' | 'describe' | 'none';
|
|
44
|
+
export interface AiMediaKind {
|
|
45
|
+
id: AiMediaKindId;
|
|
46
|
+
group: AiMediaKindGroup;
|
|
47
|
+
/** The provider mode the door runs it in. */
|
|
48
|
+
mode: AiImageMode;
|
|
49
|
+
label: string;
|
|
50
|
+
/** The line under the Kind menu once it is chosen. */
|
|
51
|
+
hint: string;
|
|
52
|
+
/** The shape the dialog switches to when the kind is chosen. */
|
|
53
|
+
aspectRatio: AiImageAspectRatio;
|
|
54
|
+
colors: AiMediaKindColors;
|
|
55
|
+
/** The description field's example. */
|
|
56
|
+
placeholder: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const AI_MEDIA_KINDS: readonly AiMediaKind[];
|
|
59
|
+
/** A kind by id; `undefined` for an id no kind has. */
|
|
60
|
+
export declare function aiMediaKind(id: string): AiMediaKind | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* The kinds this deployment offers: every kind where the image provider is
|
|
63
|
+
* on, and only the SVG kinds where it is not, so nothing the door would
|
|
64
|
+
* refuse is ever listed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function aiMediaKindsOffered(photos: boolean): readonly AiMediaKind[];
|
|
67
|
+
/** Every SVG and raster style has exactly one kind; the spec holds the list to it. */
|
|
68
|
+
export declare const AI_MEDIA_KIND_IDS: readonly AiMediaKindId[];
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
*/ import { AI_RASTER_STYLES, AI_SVG_STYLES } from "../providers/image-contract.js";
|
|
17
|
+
/**
|
|
18
|
+
* The kinds of picture Media's Create with AI offers (AGL-3602), as the Kind
|
|
19
|
+
* menu lists them: four drawn as SVG by the text provider, and seventeen made
|
|
20
|
+
* by the image provider — a photo whose description decides its style, six
|
|
21
|
+
* photographic genres, six art styles and four design assets.
|
|
22
|
+
*
|
|
23
|
+
* Browser-safe: the dialog reads this to draw the menu, pick a shape and
|
|
24
|
+
* decide which controls to show, with no request until Create. The style
|
|
25
|
+
* wording a raster kind adds to the description is the server's
|
|
26
|
+
* (`server/ai-media-raster-prompt.ts`), so the browser bundle does not carry
|
|
27
|
+
* it.
|
|
28
|
+
*/ /** The Kind menu's sections, in order. */ export const AI_MEDIA_KIND_GROUPS = [
|
|
29
|
+
'vector',
|
|
30
|
+
'photo',
|
|
31
|
+
'art',
|
|
32
|
+
'design'
|
|
33
|
+
];
|
|
34
|
+
/** Each section's heading in the Kind menu. */ export const AI_MEDIA_KIND_GROUP_LABELS = {
|
|
35
|
+
vector: 'Vector',
|
|
36
|
+
photo: 'Photo',
|
|
37
|
+
art: 'Art',
|
|
38
|
+
design: 'Design'
|
|
39
|
+
};
|
|
40
|
+
const vector = (id, label, hint, placeholder)=>({
|
|
41
|
+
id,
|
|
42
|
+
group: 'vector',
|
|
43
|
+
mode: 'illustration',
|
|
44
|
+
label,
|
|
45
|
+
hint,
|
|
46
|
+
aspectRatio: '1:1',
|
|
47
|
+
colors: 'palette',
|
|
48
|
+
placeholder
|
|
49
|
+
});
|
|
50
|
+
const raster = (id, group, label, hint, aspectRatio, placeholder)=>({
|
|
51
|
+
id,
|
|
52
|
+
group,
|
|
53
|
+
mode: 'photo',
|
|
54
|
+
label,
|
|
55
|
+
hint,
|
|
56
|
+
aspectRatio,
|
|
57
|
+
colors: group === 'photo' ? 'none' : 'describe',
|
|
58
|
+
placeholder
|
|
59
|
+
});
|
|
60
|
+
export const AI_MEDIA_KINDS = [
|
|
61
|
+
vector('illustration', 'Illustration', 'A spot illustration or small scene, drawn as an SVG and sharp at any size.', 'A friendly delivery van with a parcel, simple and flat'),
|
|
62
|
+
vector('icon', 'Icon', 'A single icon in one color, or two tones, readable at small sizes.', 'A shopping bag with a heart'),
|
|
63
|
+
vector('pattern', 'Pattern', 'A seamless background that tiles.', 'Scattered coffee beans and leaves'),
|
|
64
|
+
vector('logo', 'Logo mark', 'A simple symbol, without lettering. Not a real brand’s logo.', 'A mountain peak inside a circle'),
|
|
65
|
+
raster('photo', 'photo', 'Photo', 'A realistic photo; your description decides the style.', '1:1', 'A sunlit bakery counter with fresh sourdough loaves, warm morning light'),
|
|
66
|
+
raster('natural', 'photo', 'Natural photo', 'Candid and true to life, in soft available light.', '4:3', 'A florist arranging tulips in a small shop'),
|
|
67
|
+
raster('product', 'photo', 'Studio product shot', 'The product alone on a clean backdrop, in soft studio light.', '1:1', 'A matte ceramic coffee mug in sage green'),
|
|
68
|
+
raster('lifestyle', 'photo', 'Lifestyle with people', 'People using or enjoying the subject in a real setting. The people are made up, not anyone real.', '4:3', 'Friends sharing pastries at an outdoor café table'),
|
|
69
|
+
raster('architecture', 'photo', 'Architecture & interiors', 'Buildings and rooms with straight lines and natural daylight.', '4:3', 'A bright Scandinavian living room with oak floors'),
|
|
70
|
+
raster('food', 'photo', 'Food', 'Styled dishes in soft window light.', '3:4', 'A bowl of ramen with a soft-boiled egg and scallions'),
|
|
71
|
+
raster('aerial', 'photo', 'Aerial & landscape', 'A sweeping view from above or across the land.', '16:9', 'A winding coastal road at sunset'),
|
|
72
|
+
raster('render-3d', 'art', '3D render', 'Smooth, stylized 3D shapes in soft studio light.', '1:1', 'A stack of colorful gift boxes'),
|
|
73
|
+
raster('watercolor', 'art', 'Watercolor', 'Soft washes of color on textured paper.', '4:3', 'A harbor town with fishing boats'),
|
|
74
|
+
raster('oil-painting', 'art', 'Oil painting', 'Rich color and visible brushstrokes on canvas.', '4:3', 'A vase of sunflowers on a wooden table'),
|
|
75
|
+
raster('flat-art', 'art', 'Flat vector art', 'Clean flat shapes and solid colors, made as an image rather than an SVG.', '4:3', 'A team working together around a laptop'),
|
|
76
|
+
raster('line-drawing', 'art', 'Line drawing', 'Clean ink lines on a plain background.', '1:1', 'A bicycle leaning against a lamppost'),
|
|
77
|
+
raster('cartoon', 'art', 'Cartoon or anime', 'Bold outlines and bright colors, with original characters only.', '1:1', 'A cheerful robot watering plants'),
|
|
78
|
+
raster('background', 'design', 'Background or texture', 'Low contrast with no focal point, so text can sit on top.', '16:9', 'Soft gradient waves in calm blues'),
|
|
79
|
+
raster('banner', 'design', 'Banner or hero image', 'Wide, with calm space on one side for a headline. No lettering.', '16:9', 'A yoga mat and plants by a sunny window'),
|
|
80
|
+
raster('social', 'design', 'Social post graphic', 'A bold, centered picture for a social post.', '1:1', 'A summer sale feel with citrus fruit and bright colors'),
|
|
81
|
+
raster('mockup', 'design', 'Mockup', 'Your design shown on a device screen or on packaging.', '4:3', 'A phone on a desk showing a bakery’s online menu')
|
|
82
|
+
];
|
|
83
|
+
/** A kind by id; `undefined` for an id no kind has. */ export function aiMediaKind(id) {
|
|
84
|
+
return AI_MEDIA_KINDS.find((kind)=>kind.id === id);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The kinds this deployment offers: every kind where the image provider is
|
|
88
|
+
* on, and only the SVG kinds where it is not, so nothing the door would
|
|
89
|
+
* refuse is ever listed.
|
|
90
|
+
*/ export function aiMediaKindsOffered(photos) {
|
|
91
|
+
return photos ? AI_MEDIA_KINDS : AI_MEDIA_KINDS.filter((kind)=>kind.mode === 'illustration');
|
|
92
|
+
}
|
|
93
|
+
/** Every SVG and raster style has exactly one kind; the spec holds the list to it. */ export const AI_MEDIA_KIND_IDS = [
|
|
94
|
+
...AI_SVG_STYLES,
|
|
95
|
+
...AI_RASTER_STYLES
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=ai-media-kinds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-media-kinds.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_RASTER_STYLES,\n AI_SVG_STYLES,\n type AiImageAspectRatio,\n type AiImageMode,\n type AiRasterStyle,\n type AiSvgStyle,\n} from '../providers/image-contract'\n\n/**\n * The kinds of picture Media's Create with AI offers (AGL-3602), as the Kind\n * menu lists them: four drawn as SVG by the text provider, and seventeen made\n * by the image provider — a photo whose description decides its style, six\n * photographic genres, six art styles and four design assets.\n *\n * Browser-safe: the dialog reads this to draw the menu, pick a shape and\n * decide which controls to show, with no request until Create. The style\n * wording a raster kind adds to the description is the server's\n * (`server/ai-media-raster-prompt.ts`), so the browser bundle does not carry\n * it.\n */\n\n/** The Kind menu's sections, in order. */\nexport const AI_MEDIA_KIND_GROUPS = ['vector', 'photo', 'art', 'design'] as const\nexport type AiMediaKindGroup = (typeof AI_MEDIA_KIND_GROUPS)[number]\n\n/** Each section's heading in the Kind menu. */\nexport const AI_MEDIA_KIND_GROUP_LABELS: Readonly<Record<AiMediaKindGroup, string>> = {\n vector: 'Vector',\n photo: 'Photo',\n art: 'Art',\n design: 'Design',\n}\n\n/** A kind's id: an SVG style, or an image-provider style. */\nexport type AiMediaKindId = AiSvgStyle | AiRasterStyle\n\n/**\n * Which colors a kind takes. `palette` offers the site theme's colors or the\n * person's own, sent with the request; `describe` sends none and suggests\n * naming colors in the description; `none` is a photograph, whose colors\n * are the scene's.\n */\nexport type AiMediaKindColors = 'palette' | 'describe' | 'none'\n\nexport interface AiMediaKind {\n id: AiMediaKindId\n group: AiMediaKindGroup\n /** The provider mode the door runs it in. */\n mode: AiImageMode\n label: string\n /** The line under the Kind menu once it is chosen. */\n hint: string\n /** The shape the dialog switches to when the kind is chosen. */\n aspectRatio: AiImageAspectRatio\n colors: AiMediaKindColors\n /** The description field's example. */\n placeholder: string\n}\n\nconst vector = (\n id: AiSvgStyle,\n label: string,\n hint: string,\n placeholder: string,\n): AiMediaKind => ({\n id,\n group: 'vector',\n mode: 'illustration',\n label,\n hint,\n aspectRatio: '1:1',\n colors: 'palette',\n placeholder,\n})\n\nconst raster = (\n id: AiRasterStyle,\n group: Exclude<AiMediaKindGroup, 'vector'>,\n label: string,\n hint: string,\n aspectRatio: AiImageAspectRatio,\n placeholder: string,\n): AiMediaKind => ({\n id,\n group,\n mode: 'photo',\n label,\n hint,\n aspectRatio,\n colors: group === 'photo' ? 'none' : 'describe',\n placeholder,\n})\n\nexport const AI_MEDIA_KINDS: readonly AiMediaKind[] = [\n vector(\n 'illustration',\n 'Illustration',\n 'A spot illustration or small scene, drawn as an SVG and sharp at any size.',\n 'A friendly delivery van with a parcel, simple and flat',\n ),\n vector(\n 'icon',\n 'Icon',\n 'A single icon in one color, or two tones, readable at small sizes.',\n 'A shopping bag with a heart',\n ),\n vector(\n 'pattern',\n 'Pattern',\n 'A seamless background that tiles.',\n 'Scattered coffee beans and leaves',\n ),\n vector(\n 'logo',\n 'Logo mark',\n 'A simple symbol, without lettering. Not a real brand’s logo.',\n 'A mountain peak inside a circle',\n ),\n raster(\n 'photo',\n 'photo',\n 'Photo',\n 'A realistic photo; your description decides the style.',\n '1:1',\n 'A sunlit bakery counter with fresh sourdough loaves, warm morning light',\n ),\n raster(\n 'natural',\n 'photo',\n 'Natural photo',\n 'Candid and true to life, in soft available light.',\n '4:3',\n 'A florist arranging tulips in a small shop',\n ),\n raster(\n 'product',\n 'photo',\n 'Studio product shot',\n 'The product alone on a clean backdrop, in soft studio light.',\n '1:1',\n 'A matte ceramic coffee mug in sage green',\n ),\n raster(\n 'lifestyle',\n 'photo',\n 'Lifestyle with people',\n 'People using or enjoying the subject in a real setting. The people are made up, not anyone real.',\n '4:3',\n 'Friends sharing pastries at an outdoor café table',\n ),\n raster(\n 'architecture',\n 'photo',\n 'Architecture & interiors',\n 'Buildings and rooms with straight lines and natural daylight.',\n '4:3',\n 'A bright Scandinavian living room with oak floors',\n ),\n raster(\n 'food',\n 'photo',\n 'Food',\n 'Styled dishes in soft window light.',\n '3:4',\n 'A bowl of ramen with a soft-boiled egg and scallions',\n ),\n raster(\n 'aerial',\n 'photo',\n 'Aerial & landscape',\n 'A sweeping view from above or across the land.',\n '16:9',\n 'A winding coastal road at sunset',\n ),\n raster(\n 'render-3d',\n 'art',\n '3D render',\n 'Smooth, stylized 3D shapes in soft studio light.',\n '1:1',\n 'A stack of colorful gift boxes',\n ),\n raster(\n 'watercolor',\n 'art',\n 'Watercolor',\n 'Soft washes of color on textured paper.',\n '4:3',\n 'A harbor town with fishing boats',\n ),\n raster(\n 'oil-painting',\n 'art',\n 'Oil painting',\n 'Rich color and visible brushstrokes on canvas.',\n '4:3',\n 'A vase of sunflowers on a wooden table',\n ),\n raster(\n 'flat-art',\n 'art',\n 'Flat vector art',\n 'Clean flat shapes and solid colors, made as an image rather than an SVG.',\n '4:3',\n 'A team working together around a laptop',\n ),\n raster(\n 'line-drawing',\n 'art',\n 'Line drawing',\n 'Clean ink lines on a plain background.',\n '1:1',\n 'A bicycle leaning against a lamppost',\n ),\n raster(\n 'cartoon',\n 'art',\n 'Cartoon or anime',\n 'Bold outlines and bright colors, with original characters only.',\n '1:1',\n 'A cheerful robot watering plants',\n ),\n raster(\n 'background',\n 'design',\n 'Background or texture',\n 'Low contrast with no focal point, so text can sit on top.',\n '16:9',\n 'Soft gradient waves in calm blues',\n ),\n raster(\n 'banner',\n 'design',\n 'Banner or hero image',\n 'Wide, with calm space on one side for a headline. No lettering.',\n '16:9',\n 'A yoga mat and plants by a sunny window',\n ),\n raster(\n 'social',\n 'design',\n 'Social post graphic',\n 'A bold, centered picture for a social post.',\n '1:1',\n 'A summer sale feel with citrus fruit and bright colors',\n ),\n raster(\n 'mockup',\n 'design',\n 'Mockup',\n 'Your design shown on a device screen or on packaging.',\n '4:3',\n 'A phone on a desk showing a bakery’s online menu',\n ),\n]\n\n/** A kind by id; `undefined` for an id no kind has. */\nexport function aiMediaKind(id: string): AiMediaKind | undefined {\n return AI_MEDIA_KINDS.find((kind) => kind.id === id)\n}\n\n/**\n * The kinds this deployment offers: every kind where the image provider is\n * on, and only the SVG kinds where it is not, so nothing the door would\n * refuse is ever listed.\n */\nexport function aiMediaKindsOffered(photos: boolean): readonly AiMediaKind[] {\n return photos ? AI_MEDIA_KINDS : AI_MEDIA_KINDS.filter((kind) => kind.mode === 'illustration')\n}\n\n/** Every SVG and raster style has exactly one kind; the spec holds the list to it. */\nexport const AI_MEDIA_KIND_IDS: readonly AiMediaKindId[] = [...AI_SVG_STYLES, ...AI_RASTER_STYLES]\n"],"names":["AI_RASTER_STYLES","AI_SVG_STYLES","AI_MEDIA_KIND_GROUPS","AI_MEDIA_KIND_GROUP_LABELS","vector","photo","art","design","id","label","hint","placeholder","group","mode","aspectRatio","colors","raster","AI_MEDIA_KINDS","aiMediaKind","find","kind","aiMediaKindsOffered","photos","filter","AI_MEDIA_KIND_IDS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,gBAAgB,EAChBC,aAAa,QAKR,iCAA6B;AAEpC;;;;;;;;;;;CAWC,GAED,wCAAwC,GACxC,OAAO,MAAMC,uBAAuB;IAAC;IAAU;IAAS;IAAO;CAAS,CAAS;AAGjF,6CAA6C,GAC7C,OAAO,MAAMC,6BAAyE;IACpFC,QAAQ;IACRC,OAAO;IACPC,KAAK;IACLC,QAAQ;AACV,EAAC;AA4BD,MAAMH,SAAS,CACbI,IACAC,OACAC,MACAC,cACiB,CAAA;QACjBH;QACAI,OAAO;QACPC,MAAM;QACNJ;QACAC;QACAI,aAAa;QACbC,QAAQ;QACRJ;IACF,CAAA;AAEA,MAAMK,SAAS,CACbR,IACAI,OACAH,OACAC,MACAI,aACAH,cACiB,CAAA;QACjBH;QACAI;QACAC,MAAM;QACNJ;QACAC;QACAI;QACAC,QAAQH,UAAU,UAAU,SAAS;QACrCD;IACF,CAAA;AAEA,OAAO,MAAMM,iBAAyC;IACpDb,OACE,gBACA,gBACA,8EACA;IAEFA,OACE,QACA,QACA,sEACA;IAEFA,OACE,WACA,WACA,qCACA;IAEFA,OACE,QACA,aACA,gEACA;IAEFY,OACE,SACA,SACA,SACA,0DACA,OACA;IAEFA,OACE,WACA,SACA,iBACA,qDACA,OACA;IAEFA,OACE,WACA,SACA,uBACA,gEACA,OACA;IAEFA,OACE,aACA,SACA,yBACA,oGACA,OACA;IAEFA,OACE,gBACA,SACA,4BACA,iEACA,OACA;IAEFA,OACE,QACA,SACA,QACA,uCACA,OACA;IAEFA,OACE,UACA,SACA,sBACA,kDACA,QACA;IAEFA,OACE,aACA,OACA,aACA,oDACA,OACA;IAEFA,OACE,cACA,OACA,cACA,2CACA,OACA;IAEFA,OACE,gBACA,OACA,gBACA,kDACA,OACA;IAEFA,OACE,YACA,OACA,mBACA,4EACA,OACA;IAEFA,OACE,gBACA,OACA,gBACA,0CACA,OACA;IAEFA,OACE,WACA,OACA,oBACA,mEACA,OACA;IAEFA,OACE,cACA,UACA,yBACA,6DACA,QACA;IAEFA,OACE,UACA,UACA,wBACA,mEACA,QACA;IAEFA,OACE,UACA,UACA,uBACA,+CACA,OACA;IAEFA,OACE,UACA,UACA,UACA,yDACA,OACA;CAEH,CAAA;AAED,qDAAqD,GACrD,OAAO,SAASE,YAAYV,EAAU;IACpC,OAAOS,eAAeE,IAAI,CAAC,CAACC,OAASA,KAAKZ,EAAE,KAAKA;AACnD;AAEA;;;;CAIC,GACD,OAAO,SAASa,oBAAoBC,MAAe;IACjD,OAAOA,SAASL,iBAAiBA,eAAeM,MAAM,CAAC,CAACH,OAASA,KAAKP,IAAI,KAAK;AACjF;AAEA,oFAAoF,GACpF,OAAO,MAAMW,oBAA8C;OAAIvB;OAAkBD;CAAiB,CAAA"}
|
|
@@ -55,6 +55,10 @@ export declare function aiSiteBuildPhase(job: Pick<AiJobSummary, 'status'> & Par
|
|
|
55
55
|
* the first page's.
|
|
56
56
|
*/
|
|
57
57
|
export declare function aiSiteBuildRows(job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> & Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>): AiSiteBuildRow[];
|
|
58
|
+
/** The row the site's look is designed on (AGL-3660). */
|
|
59
|
+
export declare const AI_SITE_LOOK_ROW_LABEL = "Designing your look";
|
|
60
|
+
/** What the look's row says while it runs. */
|
|
61
|
+
export declare const AI_SITE_LOOK_HINT = "Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.";
|
|
58
62
|
/** What the planning row says while it runs. */
|
|
59
63
|
export declare const AI_SITE_PLAN_HINT = "Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.";
|
|
60
64
|
/** What the page being written says while it is the active row (AGL-3596). */
|
|
@@ -39,7 +39,7 @@ export function aiSiteBuildPhase(job) {
|
|
|
39
39
|
{
|
|
40
40
|
kind: 'theme-change',
|
|
41
41
|
resource: 'theme',
|
|
42
|
-
label: ()=>
|
|
42
|
+
label: ()=>AI_SITE_LOOK_ROW_LABEL
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
kind: 'layout',
|
|
@@ -66,20 +66,24 @@ export function aiSiteBuildPhase(job) {
|
|
|
66
66
|
// A build is read off its item ledger (AGL-3616): one row an item, each with
|
|
67
67
|
// its own state and what became of its credits.
|
|
68
68
|
if ((_job_items = job.items) == null ? void 0 : _job_items.length) {
|
|
69
|
-
var _job_items1;
|
|
69
|
+
var _job_items1, _planStep_creditsSpent1;
|
|
70
70
|
const site = job.kind !== 'build';
|
|
71
71
|
const pages = ((_job_items1 = job.items) != null ? _job_items1 : []).filter((row)=>row.op === 'page');
|
|
72
|
+
// The plan keeps the credits it used once the items take over the page.
|
|
73
|
+
const planStep = job.steps.find((step)=>step.name === 'plan');
|
|
72
74
|
return [
|
|
73
|
-
{
|
|
75
|
+
_extends({
|
|
74
76
|
id: 'plan',
|
|
75
77
|
label: site ? 'Planning your pages' : 'Planning what to build',
|
|
76
78
|
state: 'done'
|
|
77
|
-
},
|
|
79
|
+
}, planStep ? {
|
|
80
|
+
credits: Math.max(0, Math.floor((_planStep_creditsSpent1 = planStep.creditsSpent) != null ? _planStep_creditsSpent1 : 0))
|
|
81
|
+
} : {}),
|
|
78
82
|
...aiBuildItemRows(job).map((row, index)=>{
|
|
79
83
|
var _job_items, _ledger_creditsSpent, _ledger_creditsRefunded, _row_startedAt;
|
|
80
84
|
const ledger = ((_job_items = job.items) != null ? _job_items : [])[index];
|
|
81
85
|
// A site keeps the scaffold's own words for each stage (AGL-3596).
|
|
82
|
-
const label = !site || !ledger ? row.label : ledger.op === 'theme' ?
|
|
86
|
+
const label = !site || !ledger ? row.label : ledger.op === 'theme' ? AI_SITE_LOOK_ROW_LABEL : ledger.op === 'layout' ? `Building the header and footer: ${ledger.label}` : ledger.op === 'form' ? `Building the form: ${ledger.label}` : ledger.op === 'page' ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}` : ledger.op === 'email' ? 'Writing your welcome email' : row.label;
|
|
83
87
|
const screen = (ledger == null ? void 0 : ledger.op) === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null;
|
|
84
88
|
const finished = row.state === 'done' || row.state === 'failed';
|
|
85
89
|
const net = ledger ? Math.max(0, Math.floor(((_ledger_creditsSpent = ledger.creditsSpent) != null ? _ledger_creditsSpent : 0) - ((_ledger_creditsRefunded = ledger.creditsRefunded) != null ? _ledger_creditsRefunded : 0))) : 0;
|
|
@@ -95,7 +99,7 @@ export function aiSiteBuildPhase(job) {
|
|
|
95
99
|
].filter(Boolean).join(' ') : row.detail
|
|
96
100
|
}, row.state === 'active' ? {
|
|
97
101
|
startedAt: (_row_startedAt = row.startedAt) != null ? _row_startedAt : null,
|
|
98
|
-
hint: (ledger == null ? void 0 : ledger.op) === 'page' ? AI_SITE_PAGE_HINT : AI_SITE_ITEM_HINT
|
|
102
|
+
hint: (ledger == null ? void 0 : ledger.op) === 'page' ? AI_SITE_PAGE_HINT : site && (ledger == null ? void 0 : ledger.op) === 'theme' ? AI_SITE_LOOK_HINT : AI_SITE_ITEM_HINT
|
|
99
103
|
} : {}, (screen == null ? void 0 : screen.sections.length) ? {
|
|
100
104
|
sections: screen.sections.map((section)=>section.name)
|
|
101
105
|
} : {}, finished && ledger ? {
|
|
@@ -127,6 +131,12 @@ export function aiSiteBuildPhase(job) {
|
|
|
127
131
|
var _job_siteInputs;
|
|
128
132
|
const pagesAsked = typeof ((_job_siteInputs = job.siteInputs) == null ? void 0 : _job_siteInputs['pages']) === 'number' ? Math.round(job.siteInputs['pages']) : 0;
|
|
129
133
|
if (job.siteInputs) {
|
|
134
|
+
// The look is designed first, before the header and footer (AGL-3660).
|
|
135
|
+
rows.push({
|
|
136
|
+
id: 'look',
|
|
137
|
+
label: AI_SITE_LOOK_ROW_LABEL,
|
|
138
|
+
state: 'waiting'
|
|
139
|
+
});
|
|
130
140
|
rows.push({
|
|
131
141
|
id: 'layout',
|
|
132
142
|
label: 'Building the header and footer',
|
|
@@ -164,11 +174,16 @@ export function aiSiteBuildPhase(job) {
|
|
|
164
174
|
for (const row of CREATION_ROWS){
|
|
165
175
|
var _job_plan1;
|
|
166
176
|
const creation = (_job_plan1 = job.plan) == null ? void 0 : _job_plan1.create.find((entry)=>entry.kind === row.kind);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
// A job read without its item ledger is one from before the look had a
|
|
178
|
+
// unit of its own (AGL-3660): its look is a stage only where it built one.
|
|
179
|
+
if (creation || row.resource === 'theme' && job.outputs.some((output)=>output.resource === 'theme')) {
|
|
180
|
+
var _ref2;
|
|
181
|
+
stages.push({
|
|
182
|
+
id: row.resource,
|
|
183
|
+
label: row.label((_ref2 = creation == null ? void 0 : creation.name) != null ? _ref2 : ''),
|
|
184
|
+
resource: row.resource
|
|
185
|
+
});
|
|
186
|
+
}
|
|
172
187
|
}
|
|
173
188
|
const pages = (_ref1 = (_job_plan = job.plan) == null ? void 0 : _job_plan.screens) != null ? _ref1 : [];
|
|
174
189
|
pages.forEach((page, index)=>{
|
|
@@ -196,6 +211,8 @@ export function aiSiteBuildPhase(job) {
|
|
|
196
211
|
rows.push(...aiSitePublishRow(job, phase));
|
|
197
212
|
return rows;
|
|
198
213
|
}
|
|
214
|
+
/** The row the site's look is designed on (AGL-3660). */ export const AI_SITE_LOOK_ROW_LABEL = 'Designing your look';
|
|
215
|
+
/** What the look's row says while it runs. */ export const AI_SITE_LOOK_HINT = 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.';
|
|
199
216
|
/** What the planning row says while it runs. */ export const AI_SITE_PLAN_HINT = 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.';
|
|
200
217
|
/** What the page being written says while it is the active row (AGL-3596). */ export const AI_SITE_PAGE_HINT = 'Writing this page’s sections from your plan. It is done once every section is in.';
|
|
201
218
|
/** What any other part being built says while it is the active row. */ export const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.';
|