@aglyn/plugins-ai 1.0.0-beta.261 → 1.0.0-beta.263
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-brief-dialog.component.d.ts +21 -58
- package/src/lib/components/ai-brief-dialog.component.js +8 -132
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +2 -35
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +2 -8
- package/src/lib/components/ai-job-plan.component.js +13 -74
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-template-collection-field.component.d.ts +2 -12
- package/src/lib/components/ai-template-collection-field.component.js +13 -10
- package/src/lib/components/ai-template-collection-field.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +3 -25
- package/src/lib/components/ai-upsell-dialog.component.js +9 -66
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -8
- package/src/lib/components/use-ai-job-run.js +4 -15
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/insights/ai-figure-readers.js +15 -5
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-figure-summaries.d.ts +27 -0
- package/src/lib/insights/ai-figure-summaries.js +96 -0
- package/src/lib/insights/ai-figure-summaries.js.map +1 -0
- package/src/lib/insights/ai-insight-digest.d.ts +2 -0
- package/src/lib/insights/ai-insight-digest.js +7 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +2 -1
- package/src/lib/insights/ai-insight-readers.js +11 -26
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +5 -3
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-page-language.js +23 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +14 -1
- package/src/lib/jobs/ai-job-plan-step.js +44 -9
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +8 -1
- package/src/lib/jobs/ai-jobs-notify.js +44 -2
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +11 -1
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +18 -7
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-promised-copy.d.ts +54 -0
- package/src/lib/layout-language/ai-layout-promised-copy.js +104 -0
- package/src/lib/layout-language/ai-layout-promised-copy.js.map +1 -0
- package/src/lib/model/ai-brief-copy.d.ts +85 -0
- package/src/lib/model/ai-brief-copy.js +152 -0
- package/src/lib/model/ai-brief-copy.js.map +1 -0
- package/src/lib/model/ai-create-view.d.ts +200 -0
- package/src/lib/model/ai-create-view.js +263 -0
- package/src/lib/model/ai-create-view.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +10 -1
- package/src/lib/model/ai-insight.js +16 -39
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +24 -0
- package/src/lib/model/ai-job-activity.js +52 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +7 -1
- package/src/lib/model/ai-job-notice.js +10 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-job-plan-lines.d.ts +46 -0
- package/src/lib/model/ai-job-plan-lines.js +70 -0
- package/src/lib/model/ai-job-plan-lines.js.map +1 -0
- package/src/lib/model/ai-plan-said-sections.d.ts +81 -0
- package/src/lib/model/ai-plan-said-sections.js +168 -0
- package/src/lib/model/ai-plan-said-sections.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +8 -2
- package/src/lib/model/ai-site-job.js +7 -1
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-collections.d.ts +28 -0
- package/src/lib/model/ai-template-collections.js +28 -0
- package/src/lib/model/ai-template-collections.js.map +1 -0
- package/src/lib/model/ai-upsell-copy.d.ts +41 -0
- package/src/lib/model/ai-upsell-copy.js +85 -0
- package/src/lib/model/ai-upsell-copy.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +15 -1
- package/src/lib/runtime/ai-doctrine-validators.js +96 -3
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-eval-memory-firestore.js +2 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +11 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +32 -0
- package/src/lib/server/ai-create-route.js +295 -0
- package/src/lib/server/ai-create-route.js.map +1 -0
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
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 { NodesMap } from '@aglyn/aglyn/types/nodes';
|
|
18
|
+
import type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
|
|
19
|
+
/**
|
|
20
|
+
* No section that promises its own content later (AGL-3887). A guided start
|
|
21
|
+
* publishes what it builds, so a section whose words say it is still being
|
|
22
|
+
* filled — "We are building this space for the words of families we have
|
|
23
|
+
* worked with", the live Whitmore Lane Family Law home of 2026-10-16 — is a
|
|
24
|
+
* stand-in a visitor reads as an unfinished site. A page answer holding one
|
|
25
|
+
* is asked again; the last answer a generation takes has the section taken
|
|
26
|
+
* out instead (`aiLayoutPageCheck`), its first section aside.
|
|
27
|
+
*
|
|
28
|
+
* Scoped to the page speaking of itself: "this space", "this section", the
|
|
29
|
+
* site's own reviews, photos or portfolio promised for later. "Coming soon"
|
|
30
|
+
* alone is never one: an event, a menu item or a new location is genuinely
|
|
31
|
+
* coming soon on a dated list. A coming-soon site, whose whole point is what
|
|
32
|
+
* is coming, is not held to it.
|
|
33
|
+
*/
|
|
34
|
+
/** The violation code a section that promises its content later is named by. */
|
|
35
|
+
export declare const AI_LAYOUT_PROMISED_COPY_CODE = "layout-promised-copy";
|
|
36
|
+
/** Whether a line promises the page's own content later, rather than saying it. */
|
|
37
|
+
export declare function aiLayoutPromisesContent(text: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The plan indices of a built page's sections that promise their content
|
|
40
|
+
* later, each with the line that does, in page order.
|
|
41
|
+
*/
|
|
42
|
+
export declare function aiLayoutPromisedSections(nodes: NodesMap, sectionIds: readonly string[]): Array<{
|
|
43
|
+
index: number;
|
|
44
|
+
line: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** The violation that asks again for the sections that promise their content later. */
|
|
47
|
+
export declare function aiLayoutPromisedCopyViolations(screen: {
|
|
48
|
+
sections: ReadonlyArray<{
|
|
49
|
+
name: string;
|
|
50
|
+
}>;
|
|
51
|
+
}, found: ReadonlyArray<{
|
|
52
|
+
index: number;
|
|
53
|
+
line: string;
|
|
54
|
+
}>): AiDoctrineViolation[];
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
* No section that promises its own content later (AGL-3887). A guided start
|
|
18
|
+
* publishes what it builds, so a section whose words say it is still being
|
|
19
|
+
* filled — "We are building this space for the words of families we have
|
|
20
|
+
* worked with", the live Whitmore Lane Family Law home of 2026-10-16 — is a
|
|
21
|
+
* stand-in a visitor reads as an unfinished site. A page answer holding one
|
|
22
|
+
* is asked again; the last answer a generation takes has the section taken
|
|
23
|
+
* out instead (`aiLayoutPageCheck`), its first section aside.
|
|
24
|
+
*
|
|
25
|
+
* Scoped to the page speaking of itself: "this space", "this section", the
|
|
26
|
+
* site's own reviews, photos or portfolio promised for later. "Coming soon"
|
|
27
|
+
* alone is never one: an event, a menu item or a new location is genuinely
|
|
28
|
+
* coming soon on a dated list. A coming-soon site, whose whole point is what
|
|
29
|
+
* is coming, is not held to it.
|
|
30
|
+
*/ /** The violation code a section that promises its content later is named by. */ export const AI_LAYOUT_PROMISED_COPY_CODE = 'layout-promised-copy';
|
|
31
|
+
/** What a site owes and may promise later: its customers' words, its pictures, its work. */ const OWED = '(?:testimonials?|reviews?|kind words|feedback|client stories|success stories|stories from (?:our )?\\w+|words from (?:our )?\\w+|photos|pictures|gallery|portfolio|case studies)';
|
|
32
|
+
const PROMISED_COPY = [
|
|
33
|
+
// The page building itself: "We are building this space", "we're putting together this section".
|
|
34
|
+
/\b(?:we(?:['’]re| are)|we(?:['’]ll| will) be)\s+(?:still\s+)?(?:building|growing|gathering|collecting|putting together|working on|filling|adding to)\s+(?:this|the)\s+(?:space|section|page|area|corner)\b/i,
|
|
35
|
+
// "This space will soon…", "This section is being built", "This page is under construction".
|
|
36
|
+
/\bthis\s+(?:space|section|area|corner|page)\s+will\s+soon\b/i,
|
|
37
|
+
/\bthis\s+(?:space|section|area|corner|page)\s+is\s+(?:being\s+(?:built|filled|prepared)|(?:currently\s+)?under\s+construction|reserved\s+for|still\s+(?:empty|growing|being))\b/i,
|
|
38
|
+
/\bthis\s+(?:space|section|area|corner)\s+will\s+(?:hold|feature|carry|fill with|share|be filled|be home to)\b/i,
|
|
39
|
+
/\b(?:page|site|section|space|website)\s+(?:is\s+)?(?:currently\s+)?under\s+construction\b/i,
|
|
40
|
+
// What the site owes, promised for later: "Reviews coming soon", "photos are on their way".
|
|
41
|
+
new RegExp(`\\b${OWED}\\b[^.!?]{0,50}\\b(?:coming soon|on (?:the|its|their) way|to come|being (?:gathered|collected|added))\\b`, 'i'),
|
|
42
|
+
new RegExp(`\\b(?:coming soon|check back|stay tuned)\\b[^.!?]{0,60}\\b(?:${OWED}|this (?:space|section|page))\\b`, 'i'),
|
|
43
|
+
new RegExp(`\\b${OWED}\\b[^.!?]{0,60}\\b(?:check back|stay tuned)\\b`, 'i')
|
|
44
|
+
];
|
|
45
|
+
/** Whether a line promises the page's own content later, rather than saying it. */ export function aiLayoutPromisesContent(text) {
|
|
46
|
+
return PROMISED_COPY.some((pattern)=>pattern.test(text));
|
|
47
|
+
}
|
|
48
|
+
/** The props a node's own words are in, as a visitor reads them. */ const WORD_PROPS = [
|
|
49
|
+
'children',
|
|
50
|
+
'primary',
|
|
51
|
+
'secondary',
|
|
52
|
+
'label'
|
|
53
|
+
];
|
|
54
|
+
/** Every line a node and everything under it says. */ function linesUnder(nodes, id) {
|
|
55
|
+
const lines = [];
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
const stack = [
|
|
58
|
+
id
|
|
59
|
+
];
|
|
60
|
+
while(stack.length){
|
|
61
|
+
var _node_nodes;
|
|
62
|
+
const next = stack.pop();
|
|
63
|
+
const node = nodes[next];
|
|
64
|
+
if (!node || seen.has(next)) continue;
|
|
65
|
+
seen.add(next);
|
|
66
|
+
for (const prop of WORD_PROPS){
|
|
67
|
+
var _node_props;
|
|
68
|
+
const value = (_node_props = node.props) == null ? void 0 : _node_props[prop];
|
|
69
|
+
if (typeof value === 'string' && value.trim()) lines.push(value.trim());
|
|
70
|
+
}
|
|
71
|
+
stack.push(...(_node_nodes = node.nodes) != null ? _node_nodes : []);
|
|
72
|
+
}
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The plan indices of a built page's sections that promise their content
|
|
77
|
+
* later, each with the line that does, in page order.
|
|
78
|
+
*/ export function aiLayoutPromisedSections(nodes, sectionIds) {
|
|
79
|
+
const map = nodes;
|
|
80
|
+
return sectionIds.flatMap((id, index)=>{
|
|
81
|
+
if (!id || !map[id]) return [];
|
|
82
|
+
const line = linesUnder(map, id).find(aiLayoutPromisesContent);
|
|
83
|
+
return line ? [
|
|
84
|
+
{
|
|
85
|
+
index,
|
|
86
|
+
line
|
|
87
|
+
}
|
|
88
|
+
] : [];
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** The violation that asks again for the sections that promise their content later. */ export function aiLayoutPromisedCopyViolations(screen, found) {
|
|
92
|
+
return found.map(({ index, line })=>{
|
|
93
|
+
var _ref;
|
|
94
|
+
var _screen_sections_index;
|
|
95
|
+
return {
|
|
96
|
+
rule: 14,
|
|
97
|
+
code: AI_LAYOUT_PROMISED_COPY_CODE,
|
|
98
|
+
message: `Section ${index + 1} ("${(_ref = (_screen_sections_index = screen.sections[index]) == null ? void 0 : _screen_sections_index.name) != null ? _ref : ''}") promises content that is not there: "${line}". This site is published exactly as written.`,
|
|
99
|
+
detail: `Write section ${index + 1} about what is true now, in the site's voice: what working together is like, how the work goes, what a visitor can expect. Never say a space is being built or something is coming soon, and never write a review, testimonial or quote nobody gave.`
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=ai-layout-promised-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-promised-copy.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 { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\n\n/**\n * No section that promises its own content later (AGL-3887). A guided start\n * publishes what it builds, so a section whose words say it is still being\n * filled — \"We are building this space for the words of families we have\n * worked with\", the live Whitmore Lane Family Law home of 2026-10-16 — is a\n * stand-in a visitor reads as an unfinished site. A page answer holding one\n * is asked again; the last answer a generation takes has the section taken\n * out instead (`aiLayoutPageCheck`), its first section aside.\n *\n * Scoped to the page speaking of itself: \"this space\", \"this section\", the\n * site's own reviews, photos or portfolio promised for later. \"Coming soon\"\n * alone is never one: an event, a menu item or a new location is genuinely\n * coming soon on a dated list. A coming-soon site, whose whole point is what\n * is coming, is not held to it.\n */\n\n/** The violation code a section that promises its content later is named by. */\nexport const AI_LAYOUT_PROMISED_COPY_CODE = 'layout-promised-copy'\n\n/** What a site owes and may promise later: its customers' words, its pictures, its work. */\nconst OWED =\n '(?:testimonials?|reviews?|kind words|feedback|client stories|success stories|stories from (?:our )?\\\\w+|words from (?:our )?\\\\w+|photos|pictures|gallery|portfolio|case studies)'\n\nconst PROMISED_COPY: readonly RegExp[] = [\n // The page building itself: \"We are building this space\", \"we're putting together this section\".\n /\\b(?:we(?:['’]re| are)|we(?:['’]ll| will) be)\\s+(?:still\\s+)?(?:building|growing|gathering|collecting|putting together|working on|filling|adding to)\\s+(?:this|the)\\s+(?:space|section|page|area|corner)\\b/i,\n // \"This space will soon…\", \"This section is being built\", \"This page is under construction\".\n /\\bthis\\s+(?:space|section|area|corner|page)\\s+will\\s+soon\\b/i,\n /\\bthis\\s+(?:space|section|area|corner|page)\\s+is\\s+(?:being\\s+(?:built|filled|prepared)|(?:currently\\s+)?under\\s+construction|reserved\\s+for|still\\s+(?:empty|growing|being))\\b/i,\n /\\bthis\\s+(?:space|section|area|corner)\\s+will\\s+(?:hold|feature|carry|fill with|share|be filled|be home to)\\b/i,\n /\\b(?:page|site|section|space|website)\\s+(?:is\\s+)?(?:currently\\s+)?under\\s+construction\\b/i,\n // What the site owes, promised for later: \"Reviews coming soon\", \"photos are on their way\".\n new RegExp(`\\\\b${OWED}\\\\b[^.!?]{0,50}\\\\b(?:coming soon|on (?:the|its|their) way|to come|being (?:gathered|collected|added))\\\\b`, 'i'),\n new RegExp(`\\\\b(?:coming soon|check back|stay tuned)\\\\b[^.!?]{0,60}\\\\b(?:${OWED}|this (?:space|section|page))\\\\b`, 'i'),\n new RegExp(`\\\\b${OWED}\\\\b[^.!?]{0,60}\\\\b(?:check back|stay tuned)\\\\b`, 'i'),\n]\n\n/** Whether a line promises the page's own content later, rather than saying it. */\nexport function aiLayoutPromisesContent(text: string): boolean {\n return PROMISED_COPY.some((pattern) => pattern.test(text))\n}\n\ninterface WordNode {\n componentId?: string\n props?: Record<string, unknown>\n nodes?: string[]\n}\n\n/** The props a node's own words are in, as a visitor reads them. */\nconst WORD_PROPS = ['children', 'primary', 'secondary', 'label'] as const\n\n/** Every line a node and everything under it says. */\nfunction linesUnder(nodes: Readonly<Record<string, WordNode>>, id: string): string[] {\n const lines: string[] = []\n const seen = new Set<string>()\n const stack = [id]\n while (stack.length) {\n const next = stack.pop() as string\n const node = nodes[next]\n if (!node || seen.has(next)) continue\n seen.add(next)\n for (const prop of WORD_PROPS) {\n const value = node.props?.[prop]\n if (typeof value === 'string' && value.trim()) lines.push(value.trim())\n }\n stack.push(...(node.nodes ?? []))\n }\n return lines\n}\n\n/**\n * The plan indices of a built page's sections that promise their content\n * later, each with the line that does, in page order.\n */\nexport function aiLayoutPromisedSections(\n nodes: NodesMap,\n sectionIds: readonly string[],\n): Array<{ index: number; line: string }> {\n const map = nodes as unknown as Readonly<Record<string, WordNode>>\n return sectionIds.flatMap((id, index) => {\n if (!id || !map[id]) return []\n const line = linesUnder(map, id).find(aiLayoutPromisesContent)\n return line ? [{ index, line }] : []\n })\n}\n\n/** The violation that asks again for the sections that promise their content later. */\nexport function aiLayoutPromisedCopyViolations(\n screen: { sections: ReadonlyArray<{ name: string }> },\n found: ReadonlyArray<{ index: number; line: string }>,\n): AiDoctrineViolation[] {\n return found.map(({ index, line }) => ({\n rule: 14,\n code: AI_LAYOUT_PROMISED_COPY_CODE,\n message: `Section ${index + 1} (\"${screen.sections[index]?.name ?? ''}\") promises content that is not there: \"${line}\". This site is published exactly as written.`,\n detail: `Write section ${index + 1} about what is true now, in the site's voice: what working together is like, how the work goes, what a visitor can expect. Never say a space is being built or something is coming soon, and never write a review, testimonial or quote nobody gave.`,\n }))\n}\n"],"names":["AI_LAYOUT_PROMISED_COPY_CODE","OWED","PROMISED_COPY","RegExp","aiLayoutPromisesContent","text","some","pattern","test","WORD_PROPS","linesUnder","nodes","id","lines","seen","Set","stack","length","node","next","pop","has","add","prop","value","props","trim","push","aiLayoutPromisedSections","sectionIds","map","flatMap","index","line","find","aiLayoutPromisedCopyViolations","screen","found","rule","code","message","sections","name","detail"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAKD;;;;;;;;;;;;;;CAcC,GAED,8EAA8E,GAC9E,OAAO,MAAMA,+BAA+B,uBAAsB;AAElE,0FAA0F,GAC1F,MAAMC,OACJ;AAEF,MAAMC,gBAAmC;IACvC,iGAAiG;IACjG;IACA,6FAA6F;IAC7F;IACA;IACA;IACA;IACA,4FAA4F;IAC5F,IAAIC,OAAO,CAAC,GAAG,EAAEF,KAAK,wGAAwG,CAAC,EAAE;IACjI,IAAIE,OAAO,CAAC,6DAA6D,EAAEF,KAAK,gCAAgC,CAAC,EAAE;IACnH,IAAIE,OAAO,CAAC,GAAG,EAAEF,KAAK,8CAA8C,CAAC,EAAE;CACxE;AAED,iFAAiF,GACjF,OAAO,SAASG,wBAAwBC,IAAY;IAClD,OAAOH,cAAcI,IAAI,CAAC,CAACC,UAAYA,QAAQC,IAAI,CAACH;AACtD;AAQA,kEAAkE,GAClE,MAAMI,aAAa;IAAC;IAAY;IAAW;IAAa;CAAQ;AAEhE,oDAAoD,GACpD,SAASC,WAAWC,KAAyC,EAAEC,EAAU;IACvE,MAAMC,QAAkB,EAAE;IAC1B,MAAMC,OAAO,IAAIC;IACjB,MAAMC,QAAQ;QAACJ;KAAG;IAClB,MAAOI,MAAMC,MAAM,CAAE;YASJC;QARf,MAAMC,OAAOH,MAAMI,GAAG;QACtB,MAAMF,OAAOP,KAAK,CAACQ,KAAK;QACxB,IAAI,CAACD,QAAQJ,KAAKO,GAAG,CAACF,OAAO;QAC7BL,KAAKQ,GAAG,CAACH;QACT,KAAK,MAAMI,QAAQd,WAAY;gBACfS;YAAd,MAAMM,SAAQN,cAAAA,KAAKO,KAAK,qBAAVP,WAAY,CAACK,KAAK;YAChC,IAAI,OAAOC,UAAU,YAAYA,MAAME,IAAI,IAAIb,MAAMc,IAAI,CAACH,MAAME,IAAI;QACtE;QACAV,MAAMW,IAAI,KAAKT,cAAAA,KAAKP,KAAK,YAAVO,cAAc,EAAE;IACjC;IACA,OAAOL;AACT;AAEA;;;CAGC,GACD,OAAO,SAASe,yBACdjB,KAAe,EACfkB,UAA6B;IAE7B,MAAMC,MAAMnB;IACZ,OAAOkB,WAAWE,OAAO,CAAC,CAACnB,IAAIoB;QAC7B,IAAI,CAACpB,MAAM,CAACkB,GAAG,CAAClB,GAAG,EAAE,OAAO,EAAE;QAC9B,MAAMqB,OAAOvB,WAAWoB,KAAKlB,IAAIsB,IAAI,CAAC9B;QACtC,OAAO6B,OAAO;YAAC;gBAAED;gBAAOC;YAAK;SAAE,GAAG,EAAE;IACtC;AACF;AAEA,qFAAqF,GACrF,OAAO,SAASE,+BACdC,MAAqD,EACrDC,KAAqD;IAErD,OAAOA,MAAMP,GAAG,CAAC,CAAC,EAAEE,KAAK,EAAEC,IAAI,EAAE;;YAGIG;eAHE;YACrCE,MAAM;YACNC,MAAMvC;YACNwC,SAAS,CAAC,QAAQ,EAAER,QAAQ,EAAE,GAAG,WAAEI,yBAAAA,OAAOK,QAAQ,CAACT,MAAM,qBAAtBI,uBAAwBM,IAAI,mBAAI,GAAG,wCAAwC,EAAET,KAAK,6CAA6C,CAAC;YACnKU,QAAQ,CAAC,cAAc,EAAEX,QAAQ,EAAE,oPAAoP,CAAC;QAC1R;;AACF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { AiEmailType } from '../jobs/ai-job-email-step';
|
|
18
|
+
import type { AiJobKind } from './ai-jobs.types';
|
|
19
|
+
import type { AiPageType } from './ai-page-job';
|
|
20
|
+
import { type AiTemplateSubject } from './ai-template-subjects';
|
|
21
|
+
/**
|
|
22
|
+
* WHAT EVERY "CREATE WITH AI" BRIEF SAYS AND SENDS (AGL-2907, AGL-3043,
|
|
23
|
+
* AGL-3051, AGL-3596): each kind's words, the choices beside the brief, and
|
|
24
|
+
* the job and inputs they start. Pure, so the console's brief dialog and the
|
|
25
|
+
* door the native apps read (AGL-3854) say the same thing from one place.
|
|
26
|
+
*/
|
|
27
|
+
/** The longest brief a job admits. */
|
|
28
|
+
export declare const AI_BRIEF_MAX_CHARS = 4000;
|
|
29
|
+
/** The job kinds a member describes from a console page. */
|
|
30
|
+
export type AiBriefKind = Extract<AiJobKind, 'page' | 'template' | 'layout' | 'form' | 'component' | 'email' | 'campaign'>;
|
|
31
|
+
/** What the dialog says for one kind. */
|
|
32
|
+
export interface AiBriefCopy {
|
|
33
|
+
title: string;
|
|
34
|
+
/** The brief box's label. */
|
|
35
|
+
briefLabel: string;
|
|
36
|
+
placeholder: string;
|
|
37
|
+
/** What happens after the member starts the job, under the fields. */
|
|
38
|
+
next: string;
|
|
39
|
+
/** The action that starts the job. */
|
|
40
|
+
submit: string;
|
|
41
|
+
/** What the dialog says once the job exists, above its live status. */
|
|
42
|
+
started: string;
|
|
43
|
+
/** What it says when the door gave no reason, or could not be reached. */
|
|
44
|
+
failed: string;
|
|
45
|
+
}
|
|
46
|
+
export declare const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>>;
|
|
47
|
+
/**
|
|
48
|
+
* The kinds of email a member picks from, as their chips name them. The jobs'
|
|
49
|
+
* own catalog (`AI_EMAIL_TYPES`) also holds `reply`, which only a job the
|
|
50
|
+
* platform composes sets, so it is not offered here.
|
|
51
|
+
*/
|
|
52
|
+
export declare const AI_EMAIL_TYPE_LABELS: Readonly<Record<Exclude<AiEmailType, 'reply'>, string>>;
|
|
53
|
+
/** Each template subject as its chip names it: what one page is drawn for. */
|
|
54
|
+
export declare const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>>;
|
|
55
|
+
/** What a member picked beside the brief; each kind reads only its own. */
|
|
56
|
+
export interface AiBriefChoice {
|
|
57
|
+
/** A page's type, or `null` for none. */
|
|
58
|
+
pageType: AiPageType | null;
|
|
59
|
+
/** What a template's pages are drawn for, or `null` until one is picked. */
|
|
60
|
+
subject: AiTemplateSubject | null;
|
|
61
|
+
/** A collection entry template's collection, or `null` until one is picked. */
|
|
62
|
+
collectionId: string | null;
|
|
63
|
+
/** An email's or a campaign's kind of email; `null` or absent for none. */
|
|
64
|
+
emailType?: Exclude<AiEmailType, 'reply'> | null;
|
|
65
|
+
/** An email's door only: also draft the campaign that would send it. */
|
|
66
|
+
withCampaign?: boolean;
|
|
67
|
+
}
|
|
68
|
+
export declare const AI_BRIEF_NO_CHOICE: AiBriefChoice;
|
|
69
|
+
/**
|
|
70
|
+
* The job the door starts: its own kind, except an email that also asked for
|
|
71
|
+
* its campaign, which is a `campaign` job — the campaign step writes the same
|
|
72
|
+
* design and drafts the campaign around it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function aiBriefJobKind(kind: AiBriefKind, choice: AiBriefChoice): AiBriefKind;
|
|
75
|
+
/**
|
|
76
|
+
* The job's `inputs` for what the member picked, or `null` while the kind's
|
|
77
|
+
* door would refuse them.
|
|
78
|
+
*
|
|
79
|
+
* A template's are read by the parser its door reads, so the dialog cannot
|
|
80
|
+
* send a subject the door does not know or an entry page with no collection.
|
|
81
|
+
* A page's type is optional, and so is an email's or a campaign's kind of
|
|
82
|
+
* email (`inputs.emailType`, which both steps read). A layout, a form and a component read no inputs
|
|
83
|
+
* at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.
|
|
84
|
+
*/
|
|
85
|
+
export declare function aiBriefJobInputs(kind: AiBriefKind, choice: AiBriefChoice): Record<string, string> | null;
|
|
@@ -0,0 +1,152 @@
|
|
|
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 { parseAiTemplateJobInputs } from "./ai-template-subjects.js";
|
|
17
|
+
/**
|
|
18
|
+
* WHAT EVERY "CREATE WITH AI" BRIEF SAYS AND SENDS (AGL-2907, AGL-3043,
|
|
19
|
+
* AGL-3051, AGL-3596): each kind's words, the choices beside the brief, and
|
|
20
|
+
* the job and inputs they start. Pure, so the console's brief dialog and the
|
|
21
|
+
* door the native apps read (AGL-3854) say the same thing from one place.
|
|
22
|
+
*/ /** The longest brief a job admits. */ export const AI_BRIEF_MAX_CHARS = 4000;
|
|
23
|
+
export const AI_BRIEF_COPY = {
|
|
24
|
+
page: {
|
|
25
|
+
title: 'Describe a page',
|
|
26
|
+
briefLabel: 'What is the page for?',
|
|
27
|
+
placeholder: 'A landing page for our spring roof inspection offer, with the quote request form',
|
|
28
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The page uses your ' + 'theme and what the site already has, and the plan lists anything the job builds first.',
|
|
29
|
+
submit: 'Plan the page',
|
|
30
|
+
started: 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' + 'build. The page is built as an unpublished draft.',
|
|
31
|
+
failed: 'The page could not be started. Try again.'
|
|
32
|
+
},
|
|
33
|
+
template: {
|
|
34
|
+
title: 'Describe a page template',
|
|
35
|
+
briefLabel: 'What should each page show?',
|
|
36
|
+
placeholder: 'A page for each of our team members: their name and photo, their role, a short ' + 'biography and a way to get in touch',
|
|
37
|
+
next: 'A plan comes first, and nothing is built until you confirm it. What changes from one ' + 'page to the next, such as a name or a photo, is filled in from each record rather ' + 'than typed.',
|
|
38
|
+
submit: 'Plan the template',
|
|
39
|
+
started: 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' + 'to build. The template is built as a draft in your library, and nothing on your ' + 'site uses it until you do.',
|
|
40
|
+
failed: 'The template could not be started. Try again.'
|
|
41
|
+
},
|
|
42
|
+
layout: {
|
|
43
|
+
title: 'Describe a layout',
|
|
44
|
+
briefLabel: 'What should the layout hold?',
|
|
45
|
+
placeholder: 'A header with our name and links to Services, About and Contact, and a footer with ' + 'our address and opening hours',
|
|
46
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The layout links your ' + 'pages, and places your navigation or menu component when the site has one.',
|
|
47
|
+
submit: 'Plan the layout',
|
|
48
|
+
started: 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' + 'build. The layout is built as a draft, and no page uses it until you assign it.',
|
|
49
|
+
failed: 'The layout could not be started. Try again.'
|
|
50
|
+
},
|
|
51
|
+
form: {
|
|
52
|
+
title: 'Describe a form',
|
|
53
|
+
briefLabel: 'What is the form for?',
|
|
54
|
+
placeholder: 'A quote request form for a roofing company, asking what needs fixing and the best ' + 'time to call',
|
|
55
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' + 'marketing consent and where each submission goes are planned together.',
|
|
56
|
+
submit: 'Plan the form',
|
|
57
|
+
started: 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' + 'build. The form is built as a draft, and it collects nothing until you place it on ' + 'a page.',
|
|
58
|
+
failed: 'The form could not be started. Try again.'
|
|
59
|
+
},
|
|
60
|
+
component: {
|
|
61
|
+
title: 'Describe a reusable component',
|
|
62
|
+
briefLabel: 'What should the component show?',
|
|
63
|
+
placeholder: 'A testimonial card with the customer’s name, their role, their quote and a photo',
|
|
64
|
+
next: 'A plan comes first, and nothing is built until you confirm it. What each page can ' + 'change, such as a name or a photo, becomes a property of the component.',
|
|
65
|
+
submit: 'Plan the component',
|
|
66
|
+
started: 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' + 'to build. The component is built as a draft, and no page shows it until you insert ' + 'it.',
|
|
67
|
+
failed: 'The component could not be started. Try again.'
|
|
68
|
+
},
|
|
69
|
+
email: {
|
|
70
|
+
title: 'Describe an email',
|
|
71
|
+
briefLabel: 'What is the email for?',
|
|
72
|
+
placeholder: 'Announce the autumn menu to our regulars, and point them at the class page to book ' + 'the Tuesday bread class',
|
|
73
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The email is built ' + 'from email blocks, with three subject lines and three preheaders to choose from. ' + 'Nothing is sent or scheduled.',
|
|
74
|
+
submit: 'Plan the email',
|
|
75
|
+
started: 'The email is being planned. Review the plan here or in AI jobs, and confirm it to ' + 'build. The email is saved as a draft template, and nothing sends it until a ' + 'campaign does.',
|
|
76
|
+
failed: 'The email could not be started. Try again.'
|
|
77
|
+
},
|
|
78
|
+
campaign: {
|
|
79
|
+
title: 'Describe a campaign',
|
|
80
|
+
briefLabel: 'What is the campaign’s email for?',
|
|
81
|
+
placeholder: 'Announce the autumn menu to our regulars, and point them at the class page to book ' + 'the Tuesday bread class',
|
|
82
|
+
next: 'A plan comes first, and nothing is built until you confirm it. The job writes the ' + 'email and a draft campaign that would send it. The campaign is aimed at nobody and ' + 'sends nothing until you pick who receives it and schedule it.',
|
|
83
|
+
submit: 'Plan the campaign',
|
|
84
|
+
started: 'The campaign is being planned. Review the plan here or in AI jobs, and confirm it ' + 'to build. The email and the campaign are saved as drafts, and nothing is sent.',
|
|
85
|
+
failed: 'The campaign could not be started. Try again.'
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* The kinds of email a member picks from, as their chips name them. The jobs'
|
|
90
|
+
* own catalog (`AI_EMAIL_TYPES`) also holds `reply`, which only a job the
|
|
91
|
+
* platform composes sets, so it is not offered here.
|
|
92
|
+
*/ export const AI_EMAIL_TYPE_LABELS = {
|
|
93
|
+
welcome: 'Welcome',
|
|
94
|
+
newsletter: 'Newsletter',
|
|
95
|
+
launch: 'Launch',
|
|
96
|
+
abandonedCart: 'Cart reminder',
|
|
97
|
+
eventReminder: 'Event reminder'
|
|
98
|
+
};
|
|
99
|
+
/** Each template subject as its chip names it: what one page is drawn for. */ export const AI_TEMPLATE_SUBJECT_LABELS = {
|
|
100
|
+
entry: 'Collection entry',
|
|
101
|
+
product: 'Product',
|
|
102
|
+
author: 'Author'
|
|
103
|
+
};
|
|
104
|
+
export const AI_BRIEF_NO_CHOICE = {
|
|
105
|
+
pageType: null,
|
|
106
|
+
subject: null,
|
|
107
|
+
collectionId: null,
|
|
108
|
+
emailType: null,
|
|
109
|
+
withCampaign: false
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The job the door starts: its own kind, except an email that also asked for
|
|
113
|
+
* its campaign, which is a `campaign` job — the campaign step writes the same
|
|
114
|
+
* design and drafts the campaign around it.
|
|
115
|
+
*/ export function aiBriefJobKind(kind, choice) {
|
|
116
|
+
return kind === 'email' && choice.withCampaign ? 'campaign' : kind;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The job's `inputs` for what the member picked, or `null` while the kind's
|
|
120
|
+
* door would refuse them.
|
|
121
|
+
*
|
|
122
|
+
* A template's are read by the parser its door reads, so the dialog cannot
|
|
123
|
+
* send a subject the door does not know or an entry page with no collection.
|
|
124
|
+
* A page's type is optional, and so is an email's or a campaign's kind of
|
|
125
|
+
* email (`inputs.emailType`, which both steps read). A layout, a form and a component read no inputs
|
|
126
|
+
* at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.
|
|
127
|
+
*/ export function aiBriefJobInputs(kind, choice) {
|
|
128
|
+
if (kind === 'page') return choice.pageType ? {
|
|
129
|
+
pageType: choice.pageType
|
|
130
|
+
} : {};
|
|
131
|
+
if (kind === 'email' || kind === 'campaign') {
|
|
132
|
+
return choice.emailType ? {
|
|
133
|
+
emailType: choice.emailType
|
|
134
|
+
} : {};
|
|
135
|
+
}
|
|
136
|
+
if (kind === 'template') {
|
|
137
|
+
const inputs = parseAiTemplateJobInputs({
|
|
138
|
+
subject: choice.subject,
|
|
139
|
+
collectionId: choice.collectionId
|
|
140
|
+
});
|
|
141
|
+
if (typeof inputs === 'string') return null;
|
|
142
|
+
return inputs.collectionId ? {
|
|
143
|
+
subject: inputs.subject,
|
|
144
|
+
collectionId: inputs.collectionId
|
|
145
|
+
} : {
|
|
146
|
+
subject: inputs.subject
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=ai-brief-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-brief-copy.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 { AiEmailType } from '../jobs/ai-job-email-step'\nimport type { AiJobKind } from './ai-jobs.types'\nimport type { AiPageType } from './ai-page-job'\nimport { parseAiTemplateJobInputs, type AiTemplateSubject } from './ai-template-subjects'\n\n/**\n * WHAT EVERY \"CREATE WITH AI\" BRIEF SAYS AND SENDS (AGL-2907, AGL-3043,\n * AGL-3051, AGL-3596): each kind's words, the choices beside the brief, and\n * the job and inputs they start. Pure, so the console's brief dialog and the\n * door the native apps read (AGL-3854) say the same thing from one place.\n */\n\n/** The longest brief a job admits. */\nexport const AI_BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component' | 'email' | 'campaign'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists, above its live status. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The template is built as a draft in your library, and nothing on your ' +\n 'site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'pages, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The layout is built as a draft, and no page uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The form is built as a draft, and it collects nothing until you place it on ' +\n 'a page.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The component is built as a draft, and no page shows it until you insert ' +\n 'it.',\n failed: 'The component could not be started. Try again.',\n },\n email: {\n title: 'Describe an email',\n briefLabel: 'What is the email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The email is built ' +\n 'from email blocks, with three subject lines and three preheaders to choose from. ' +\n 'Nothing is sent or scheduled.',\n submit: 'Plan the email',\n started:\n 'The email is being planned. Review the plan here or in AI jobs, and confirm it to ' +\n 'build. The email is saved as a draft template, and nothing sends it until a ' +\n 'campaign does.',\n failed: 'The email could not be started. Try again.',\n },\n campaign: {\n title: 'Describe a campaign',\n briefLabel: 'What is the campaign’s email for?',\n placeholder:\n 'Announce the autumn menu to our regulars, and point them at the class page to book ' +\n 'the Tuesday bread class',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The job writes the ' +\n 'email and a draft campaign that would send it. The campaign is aimed at nobody and ' +\n 'sends nothing until you pick who receives it and schedule it.',\n submit: 'Plan the campaign',\n started:\n 'The campaign is being planned. Review the plan here or in AI jobs, and confirm it ' +\n 'to build. The email and the campaign are saved as drafts, and nothing is sent.',\n failed: 'The campaign could not be started. Try again.',\n },\n}\n\n/**\n * The kinds of email a member picks from, as their chips name them. The jobs'\n * own catalog (`AI_EMAIL_TYPES`) also holds `reply`, which only a job the\n * platform composes sets, so it is not offered here.\n */\nexport const AI_EMAIL_TYPE_LABELS: Readonly<Record<Exclude<AiEmailType, 'reply'>, string>> = {\n welcome: 'Welcome',\n newsletter: 'Newsletter',\n launch: 'Launch',\n abandonedCart: 'Cart reminder',\n eventReminder: 'Event reminder',\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n /** An email's or a campaign's kind of email; `null` or absent for none. */\n emailType?: Exclude<AiEmailType, 'reply'> | null\n /** An email's door only: also draft the campaign that would send it. */\n withCampaign?: boolean\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n emailType: null,\n withCampaign: false,\n}\n\n/**\n * The job the door starts: its own kind, except an email that also asked for\n * its campaign, which is a `campaign` job — the campaign step writes the same\n * design and drafts the campaign around it.\n */\nexport function aiBriefJobKind(kind: AiBriefKind, choice: AiBriefChoice): AiBriefKind {\n return kind === 'email' && choice.withCampaign ? 'campaign' : kind\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional, and so is an email's or a campaign's kind of\n * email (`inputs.emailType`, which both steps read). A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'email' || kind === 'campaign') {\n return choice.emailType ? { emailType: choice.emailType } : {}\n }\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n"],"names":["parseAiTemplateJobInputs","AI_BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","email","campaign","AI_EMAIL_TYPE_LABELS","welcome","newsletter","launch","abandonedCart","eventReminder","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","emailType","withCampaign","aiBriefJobKind","kind","choice","aiBriefJobInputs","inputs"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAKD,SAASA,wBAAwB,QAAgC,4BAAwB;AAEzF;;;;;CAKC,GAED,oCAAoC,GACpC,OAAO,MAAMC,qBAAqB,KAAK;AAwBvC,OAAO,MAAMC,gBAA4D;IACvEC,MAAM;QACJC,OAAO;QACPC,YAAY;QACZC,aACE;QACFC,MACE,uFACA;QACFC,QAAQ;QACRC,SACE,sFACA;QACFC,QAAQ;IACV;IACAC,UAAU;QACRP,OAAO;QACPC,YAAY;QACZC,aACE,oFACA;QACFC,MACE,0FACA,uFACA;QACFC,QAAQ;QACRC,SACE,uFACA,qFACA;QACFC,QAAQ;IACV;IACAE,QAAQ;QACNR,OAAO;QACPC,YAAY;QACZC,aACE,wFACA;QACFC,MACE,0FACA;QACFC,QAAQ;QACRC,SACE,wFACA;QACFC,QAAQ;IACV;IACAG,MAAM;QACJT,OAAO;QACPC,YAAY;QACZC,aACE,uFACA;QACFC,MACE,2FACA;QACFC,QAAQ;QACRC,SACE,sFACA,wFACA;QACFC,QAAQ;IACV;IACAI,WAAW;QACTV,OAAO;QACPC,YAAY;QACZC,aACE;QACFC,MACE,uFACA;QACFC,QAAQ;QACRC,SACE,wFACA,wFACA;QACFC,QAAQ;IACV;IACAK,OAAO;QACLX,OAAO;QACPC,YAAY;QACZC,aACE,wFACA;QACFC,MACE,uFACA,sFACA;QACFC,QAAQ;QACRC,SACE,uFACA,iFACA;QACFC,QAAQ;IACV;IACAM,UAAU;QACRZ,OAAO;QACPC,YAAY;QACZC,aACE,wFACA;QACFC,MACE,uFACA,wFACA;QACFC,QAAQ;QACRC,SACE,uFACA;QACFC,QAAQ;IACV;AACF,EAAC;AAED;;;;CAIC,GACD,OAAO,MAAMO,uBAAgF;IAC3FC,SAAS;IACTC,YAAY;IACZC,QAAQ;IACRC,eAAe;IACfC,eAAe;AACjB,EAAC;AAED,4EAA4E,GAC5E,OAAO,MAAMC,6BAA0E;IACrFC,OAAO;IACPC,SAAS;IACTC,QAAQ;AACV,EAAC;AAgBD,OAAO,MAAMC,qBAAoC;IAC/CC,UAAU;IACVC,SAAS;IACTC,cAAc;IACdC,WAAW;IACXC,cAAc;AAChB,EAAC;AAED;;;;CAIC,GACD,OAAO,SAASC,eAAeC,IAAiB,EAAEC,MAAqB;IACrE,OAAOD,SAAS,WAAWC,OAAOH,YAAY,GAAG,aAAaE;AAChE;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASE,iBACdF,IAAiB,EACjBC,MAAqB;IAErB,IAAID,SAAS,QAAQ,OAAOC,OAAOP,QAAQ,GAAG;QAAEA,UAAUO,OAAOP,QAAQ;IAAC,IAAI,CAAC;IAC/E,IAAIM,SAAS,WAAWA,SAAS,YAAY;QAC3C,OAAOC,OAAOJ,SAAS,GAAG;YAAEA,WAAWI,OAAOJ,SAAS;QAAC,IAAI,CAAC;IAC/D;IACA,IAAIG,SAAS,YAAY;QACvB,MAAMG,SAASrC,yBAAyB;YACtC6B,SAASM,OAAON,OAAO;YACvBC,cAAcK,OAAOL,YAAY;QACnC;QACA,IAAI,OAAOO,WAAW,UAAU,OAAO;QACvC,OAAOA,OAAOP,YAAY,GACtB;YAAED,SAASQ,OAAOR,OAAO;YAAEC,cAAcO,OAAOP,YAAY;QAAC,IAC7D;YAAED,SAASQ,OAAOR,OAAO;QAAC;IAChC;IACA,OAAO,CAAC;AACV"}
|
|
@@ -0,0 +1,200 @@
|
|
|
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 AiBriefCopy } from './ai-brief-copy';
|
|
18
|
+
import { AI_JOB_FOLLOW_PHASE_LINES, type AiFollowTone, type AiJobPhase } from './ai-job-activity';
|
|
19
|
+
import { type AiJobOutput, type AiJobSummary } from './ai-jobs.types';
|
|
20
|
+
import { type AiFreeCreditsLeft } from './ai-site-job';
|
|
21
|
+
import type { AiTemplateCollectionOption } from './ai-template-collections';
|
|
22
|
+
/**
|
|
23
|
+
* "CREATE WITH AI" AS THE NATIVE APPS DRAW IT (AGL-3854): the door a list's
|
|
24
|
+
* Create with AI button reads, and the follow view of the job it starts.
|
|
25
|
+
*
|
|
26
|
+
* The console decides each entry from gates its shell already resolved — the
|
|
27
|
+
* plan and the add-on it could buy, `ai.generate`, the release flag and the
|
|
28
|
+
* site's AI switch — and draws the brief and the job from the plugin's own
|
|
29
|
+
* components. An app has neither the shell nor the components, so the AI
|
|
30
|
+
* plugin's `/api/ai/create` door answers both as data: whether the entry is
|
|
31
|
+
* drawn and as what, every word the brief dialog says, and the job as the
|
|
32
|
+
* dialog's follow card reads it, in sentences built here from the same models
|
|
33
|
+
* the console's components read. Nothing here decides what a request may do:
|
|
34
|
+
* the jobs route's own ladder still decides each start, confirm and cancel.
|
|
35
|
+
*/
|
|
36
|
+
/** The lists the apps offer Create with AI on, beside their own create action. */
|
|
37
|
+
export declare const AI_CREATE_KINDS: readonly ["page", "template", "layout", "form", "component"];
|
|
38
|
+
export type AiCreateKind = (typeof AI_CREATE_KINDS)[number];
|
|
39
|
+
export declare function isAiCreateKind(value: unknown): value is AiCreateKind;
|
|
40
|
+
/** One row of chips beside the brief, and the input the pick is sent as. */
|
|
41
|
+
export interface AiCreateChoiceView {
|
|
42
|
+
/** The `inputs` key the pick is sent under. */
|
|
43
|
+
key: 'pageType' | 'subject';
|
|
44
|
+
label: string;
|
|
45
|
+
/** A required pick keeps the start disabled until one is made; an optional one clears when pressed again. */
|
|
46
|
+
required: boolean;
|
|
47
|
+
options: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
/** A template's collection field, shown once "Collection entry" is picked. */
|
|
53
|
+
export interface AiCreateCollectionsView {
|
|
54
|
+
/** The subject the field belongs to. */
|
|
55
|
+
when: 'entry';
|
|
56
|
+
label: string;
|
|
57
|
+
options: AiTemplateCollectionOption[];
|
|
58
|
+
helper: string;
|
|
59
|
+
}
|
|
60
|
+
/** What is left of the month's AI credits, said before a brief is sent. */
|
|
61
|
+
export interface AiCreateCreditsView {
|
|
62
|
+
text: string;
|
|
63
|
+
/** Nothing is left and the workspace stops at its band, so a start is refused until credits return. */
|
|
64
|
+
exhausted: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** The add-on's offer, on a plan that could buy it and has not (AGL-3601). */
|
|
67
|
+
export interface AiCreateUpsellView {
|
|
68
|
+
title: string;
|
|
69
|
+
does: string;
|
|
70
|
+
offer: string;
|
|
71
|
+
/** The reader may add the add-on (`billing.manage`); the action is drawn only then. */
|
|
72
|
+
canManageBilling: boolean;
|
|
73
|
+
/** "Add Aglyn AI", or `null` for a reader who cannot. */
|
|
74
|
+
action: string | null;
|
|
75
|
+
dismiss: string;
|
|
76
|
+
}
|
|
77
|
+
export interface AiCreateDoorView {
|
|
78
|
+
kind: AiCreateKind;
|
|
79
|
+
/** `create`: the brief; `upsell`: the add-on's offer; `hidden`: the entry is not drawn. */
|
|
80
|
+
door: 'create' | 'upsell' | 'hidden';
|
|
81
|
+
/** The entry's label, "Create with AI". */
|
|
82
|
+
label: string;
|
|
83
|
+
copy: AiBriefCopy | null;
|
|
84
|
+
briefMaxChars: number;
|
|
85
|
+
choices: AiCreateChoiceView[];
|
|
86
|
+
collections: AiCreateCollectionsView | null;
|
|
87
|
+
credits: AiCreateCreditsView | null;
|
|
88
|
+
/** Another page already being built on this site (AGL-3765): a warning, not a stop. */
|
|
89
|
+
building: string | null;
|
|
90
|
+
upsell: AiCreateUpsellView | null;
|
|
91
|
+
}
|
|
92
|
+
/** What the door knows about the month's credits. */
|
|
93
|
+
export type AiCreateCreditsFacts = {
|
|
94
|
+
kind: 'free';
|
|
95
|
+
left: AiFreeCreditsLeft;
|
|
96
|
+
} | {
|
|
97
|
+
kind: 'band';
|
|
98
|
+
used: number;
|
|
99
|
+
limit: number;
|
|
100
|
+
remaining: number;
|
|
101
|
+
stopsAtBand: boolean;
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'unlimited';
|
|
104
|
+
} | {
|
|
105
|
+
kind: 'none';
|
|
106
|
+
};
|
|
107
|
+
/** The credits line above the brief, or `null` where the workspace has no band to read. */
|
|
108
|
+
export declare function aiCreateCreditsView(facts: AiCreateCreditsFacts): AiCreateCreditsView | null;
|
|
109
|
+
/** The chips each kind offers beside its brief, as the console's dialog draws them. */
|
|
110
|
+
export declare function aiCreateChoices(kind: AiCreateKind): AiCreateChoiceView[];
|
|
111
|
+
export interface AiCreateDoorInput {
|
|
112
|
+
kind: AiCreateKind;
|
|
113
|
+
/** The gates that draw no entry at all: `ai.generate`, the release flag, the site's AI switch. */
|
|
114
|
+
shown: boolean;
|
|
115
|
+
/** The plan carries `aiGenerative`. */
|
|
116
|
+
entitled: boolean;
|
|
117
|
+
/** The workspace could buy the add-on that grants it, and the add-on store is open to it. */
|
|
118
|
+
purchasable: boolean;
|
|
119
|
+
canManageBilling: boolean;
|
|
120
|
+
credits: AiCreateCreditsFacts;
|
|
121
|
+
/** The site's content collections, read only for a template's door. */
|
|
122
|
+
collections?: {
|
|
123
|
+
status: 'success' | 'error';
|
|
124
|
+
options: AiTemplateCollectionOption[];
|
|
125
|
+
} | null;
|
|
126
|
+
/** Another page build in flight on the site, for a page's door. */
|
|
127
|
+
buildInFlight?: boolean;
|
|
128
|
+
addonName: string;
|
|
129
|
+
}
|
|
130
|
+
/** The door one list's Create with AI reads. */
|
|
131
|
+
export declare function aiCreateDoorView(input: AiCreateDoorInput): AiCreateDoorView;
|
|
132
|
+
export { AI_JOB_FOLLOW_PHASE_LINES, type AiFollowTone };
|
|
133
|
+
/**
|
|
134
|
+
* Where one output opens in an app, as the console addresses the apps' link
|
|
135
|
+
* resolver answers natively: a page opens its page (the console's preview
|
|
136
|
+
* address, which the apps' Pages claims), a component, layout or template its
|
|
137
|
+
* details, and anything else the page its owner publishes through the
|
|
138
|
+
* record-route seam (a form's own page). `besigner` is the version the job
|
|
139
|
+
* wrote, opened in the Besigner. `null` where the output has no page of its own,
|
|
140
|
+
* names no site, or its owner is not loaded.
|
|
141
|
+
*/
|
|
142
|
+
export declare function aiJobOutputAppLinks(output: Pick<AiJobOutput, 'resource' | 'id' | 'versionId' | 'hostSubdomain'>, orgSlug: string): {
|
|
143
|
+
detail: string;
|
|
144
|
+
besigner: string | null;
|
|
145
|
+
} | null;
|
|
146
|
+
/** The one thing a finished job leads with, as the console's `aiJobPrimaryLink` picks it. */
|
|
147
|
+
export interface AiJobFollowOpen {
|
|
148
|
+
label: string;
|
|
149
|
+
detail: string;
|
|
150
|
+
besigner: string | null;
|
|
151
|
+
}
|
|
152
|
+
export declare function aiJobFollowOpen(job: Pick<AiJobSummary, 'status' | 'outputs'>, orgSlug: string): AiJobFollowOpen | null;
|
|
153
|
+
export interface AiJobFollowView {
|
|
154
|
+
id: string;
|
|
155
|
+
kind: string;
|
|
156
|
+
status: string;
|
|
157
|
+
hostId: string | null;
|
|
158
|
+
phase: AiJobPhase;
|
|
159
|
+
phaseLabel: string;
|
|
160
|
+
line: {
|
|
161
|
+
tone: AiFollowTone;
|
|
162
|
+
text: string;
|
|
163
|
+
};
|
|
164
|
+
/** Still moving, so the apps read it again shortly. */
|
|
165
|
+
moving: boolean;
|
|
166
|
+
/** Finished, failed or canceled: nothing more happens to it. */
|
|
167
|
+
settled: boolean;
|
|
168
|
+
/** "2 of 3 steps", for a job of more than one step. */
|
|
169
|
+
steps: string | null;
|
|
170
|
+
plan: {
|
|
171
|
+
heading: string;
|
|
172
|
+
lines: string[];
|
|
173
|
+
} | null;
|
|
174
|
+
/** The building rules a refused answer broke, as a member reads them. */
|
|
175
|
+
findings: string[];
|
|
176
|
+
/** The checks' own sentence on a refused answer, for staff. */
|
|
177
|
+
detail: string | null;
|
|
178
|
+
/** The plan's estimated cost, read before it is confirmed. */
|
|
179
|
+
estimate: string | null;
|
|
180
|
+
retryRefusal: string | null;
|
|
181
|
+
/** "Confirm plan" or "Try again" while the job waits for the person. */
|
|
182
|
+
confirm: {
|
|
183
|
+
label: string;
|
|
184
|
+
enabled: boolean;
|
|
185
|
+
} | null;
|
|
186
|
+
/** Another page build in flight on the site, said beside Confirm (AGL-3765). */
|
|
187
|
+
building: string | null;
|
|
188
|
+
/** Why a stopped job has nothing to show. */
|
|
189
|
+
problem: string | null;
|
|
190
|
+
/** What the job spent so far. */
|
|
191
|
+
spent: string | null;
|
|
192
|
+
open: AiJobFollowOpen | null;
|
|
193
|
+
}
|
|
194
|
+
/** The follow card's view of one job, for the apps (the console's `AiJobFollow` and `AiJobPlan`). */
|
|
195
|
+
export declare function aiJobFollowView(job: AiJobSummary, options?: {
|
|
196
|
+
orgSlug?: string | null;
|
|
197
|
+
staff?: boolean;
|
|
198
|
+
buildInFlight?: boolean;
|
|
199
|
+
now?: number;
|
|
200
|
+
}): AiJobFollowView;
|