@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
- package/src/lib/components/ai-credits-prompt.component.js +84 -34
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
- package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
- package/src/lib/components/ai-job-cancel.component.js +156 -0
- package/src/lib/components/ai-job-cancel.component.js.map +1 -0
- package/src/lib/components/ai-job-links.js +8 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +12 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +141 -72
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +12 -0
- package/src/lib/components/ai-jobs-store.js +15 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +22 -2
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
- package/src/lib/components/ai-usage-meter-refresh.js +63 -0
- package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
- package/src/lib/components/ai-usage-strip.component.js +40 -9
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +12 -16
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +91 -51
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
- package/src/lib/components/use-ai-usage-meter.js +8 -3
- package/src/lib/components/use-ai-usage-meter.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +33 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
- package/src/lib/jobs/ai-job-cancel.js +102 -0
- package/src/lib/jobs/ai-job-cancel.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.js +4 -1
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +7 -2
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +2 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
- package/src/lib/jobs/ai-job-form-dataset.js +161 -0
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
- package/src/lib/jobs/ai-job-form-step.js +41 -10
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-layout-language.js +13 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +6 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +45 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
- package/src/lib/jobs/ai-job-plan-step.js +90 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
- package/src/lib/jobs/ai-job-site-content.js +66 -7
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
- package/src/lib/jobs/ai-job-site-datasets.js +499 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
- package/src/lib/jobs/ai-job-site-step.js +152 -30
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +4 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +37 -12
- package/src/lib/jobs/ai-jobs.js +184 -28
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
- package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
- package/src/lib/layout-language/ai-layout-compiler.js +397 -9
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +5 -3
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-frame.js +58 -3
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +17 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
- package/src/lib/layout-language/ai-layout-listings.js +118 -50
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
- package/src/lib/layout-language/ai-layout-storefront.js +223 -0
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +11 -1
- package/src/lib/model/ai-build-job.js +35 -6
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +23 -3
- package/src/lib/model/ai-build-plan.js +47 -6
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +2 -2
- package/src/lib/model/ai-build-progress.js +6 -4
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +69 -2
- package/src/lib/model/ai-credit-estimate.js +91 -3
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
- package/src/lib/model/ai-job-cancel-copy.js +53 -0
- package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
- package/src/lib/model/ai-job-failure-copy.js +8 -3
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +20 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.js +7 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +38 -4
- package/src/lib/model/ai-site-job.js +50 -14
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +4 -1
- package/src/lib/model/ai-site-kinds.js +34 -6
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
- package/src/lib/runtime/ai-dataset-generation.js +127 -0
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
- package/src/lib/runtime/ai-doctrine-validators.js +280 -6
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +10 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +2 -0
- package/src/lib/runtime/ai-node-tree.js +16 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +53 -7
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
- package/src/lib/server/ai-jobs-cancel.js +27 -9
- package/src/lib/server/ai-jobs-cancel.js.map +1 -1
- package/src/lib/server/ai-jobs-gate.d.ts +12 -2
- package/src/lib/server/ai-jobs-gate.js +20 -9
- package/src/lib/server/ai-jobs-gate.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
- package/src/lib/tools/ai-dataset-tool.js +231 -0
- package/src/lib/tools/ai-dataset-tool.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +2 -1
- package/src/lib/usage/ai-usage-meter.js +17 -1
- package/src/lib/usage/ai-usage-meter.js.map +1 -1
- package/src/lib/usage/ai-usage-wire.d.ts +6 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +7 -0
- package/src/lib/usage/assist-usage.js +14 -4
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
- package/src/lib/usage/free-ai-credits-left.js +30 -4
- package/src/lib/usage/free-ai-credits-left.js.map +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A STORE'S STOREFRONT (AGL-3676, second round).
|
|
19
|
+
*
|
|
20
|
+
* Zach, 2026-10-09, on the beta.237 Willow Wick Candles start (job
|
|
21
|
+
* MtlXWjvt9P): "again this … site does not look like a store front". Its home
|
|
22
|
+
* was a lifestyle hero, ONE row of featured products and four text-only bands
|
|
23
|
+
* ("Why soy, why small batch", "How our candles are made", "Made to be
|
|
24
|
+
* loved", "Gifting and shipping"); its Shop page opened with a tall hero and
|
|
25
|
+
* the grid below the fold.
|
|
26
|
+
*
|
|
27
|
+
* A designer storefront (Shopify's Dawn, Craft, Sense; Squarespace's commerce
|
|
28
|
+
* templates) is composed of a handful of PARTS, in a familiar order: a hero
|
|
29
|
+
* that sells, the store's bestsellers, its collections as picture tiles, the
|
|
30
|
+
* brand's story beside a photo, why to buy here with icons, what customers
|
|
31
|
+
* say, and a newsletter sign-up. This module names those parts by the words a
|
|
32
|
+
* plan gives its sections, so the plan step can make sure a store's home has
|
|
33
|
+
* them and the compiler and the listings can draw each one its way.
|
|
34
|
+
*
|
|
35
|
+
* It is NOT a section library (Zach 10/7): the model still names, orders and
|
|
36
|
+
* writes every section, and designs each in the layout language; what code
|
|
37
|
+
* adds is a part the plan left out — named in plain words for the model to
|
|
38
|
+
* design — and the platform elements that show the store's own records.
|
|
39
|
+
*
|
|
40
|
+
* Pure: names and plans only.
|
|
41
|
+
*/
|
|
42
|
+
import type { AiBuildPlanScreen } from '../model/ai-build-plan';
|
|
43
|
+
/** A part a storefront home is composed of. */
|
|
44
|
+
export type AiStorefrontPart = 'featured' | 'collections' | 'story' | 'values' | 'reviews' | 'signup';
|
|
45
|
+
/** The section names that say each part, as a plan or a page heading words it. */
|
|
46
|
+
export declare const AI_STOREFRONT_PART_WORDS: Readonly<Record<AiStorefrontPart, RegExp>>;
|
|
47
|
+
/** Whether a planned section's name says a part. */
|
|
48
|
+
export declare function aiStorefrontSays(part: AiStorefrontPart, name: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A store plan's screens with the storefront a shopper expects (AGL-3676):
|
|
51
|
+
*
|
|
52
|
+
* - THE HOME (paid starts only — the Free wall prices each section, and a
|
|
53
|
+
* Free store sells nothing yet): its hero first, then any part the plan
|
|
54
|
+
* left out, added under plain names while the page has room (eight
|
|
55
|
+
* sections), in order of need: the bestsellers the platform lists,
|
|
56
|
+
* right after the hero; the collections as picture tiles after them; the
|
|
57
|
+
* newsletter sign-up as the page's last band; and customer reviews (the
|
|
58
|
+
* store's real ones, which show only once shoppers leave them) before
|
|
59
|
+
* the close.
|
|
60
|
+
* - THE SHOP PAGE: grid first. Its product grid section moves to second,
|
|
61
|
+
* straight under the page's short title, so the products open above the
|
|
62
|
+
* fold; anything the plan put between them (an intro, a story) follows
|
|
63
|
+
* the grid.
|
|
64
|
+
*
|
|
65
|
+
* Every section the plan named stays, in its order otherwise; nothing is
|
|
66
|
+
* removed, and a page at eight sections is given nothing more.
|
|
67
|
+
*/
|
|
68
|
+
export declare function aiStorefrontPlanScreens<S extends Pick<AiBuildPlanScreen, 'slug' | 'title' | 'sections'>>(screens: readonly S[], options: {
|
|
69
|
+
paid: boolean;
|
|
70
|
+
isShopPage: (screen: S) => boolean;
|
|
71
|
+
gridSection: (sections: S['sections']) => number;
|
|
72
|
+
}): S[];
|
|
73
|
+
/**
|
|
74
|
+
* The icon each of a store's reasons to buy shows (AGL-3676): its own where
|
|
75
|
+
* the design named one, else one its words suggest, else the next of a few
|
|
76
|
+
* neutral ones — never the same twice in one row.
|
|
77
|
+
*/
|
|
78
|
+
export declare function aiStorefrontItemIcons(items: ReadonlyArray<{
|
|
79
|
+
title: string;
|
|
80
|
+
text: string;
|
|
81
|
+
icon?: string;
|
|
82
|
+
}>, known: (word: string | undefined) => boolean): string[];
|
|
@@ -0,0 +1,223 @@
|
|
|
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
|
+
* A STORE'S STOREFRONT (AGL-3676, second round).
|
|
18
|
+
*
|
|
19
|
+
* Zach, 2026-10-09, on the beta.237 Willow Wick Candles start (job
|
|
20
|
+
* MtlXWjvt9P): "again this … site does not look like a store front". Its home
|
|
21
|
+
* was a lifestyle hero, ONE row of featured products and four text-only bands
|
|
22
|
+
* ("Why soy, why small batch", "How our candles are made", "Made to be
|
|
23
|
+
* loved", "Gifting and shipping"); its Shop page opened with a tall hero and
|
|
24
|
+
* the grid below the fold.
|
|
25
|
+
*
|
|
26
|
+
* A designer storefront (Shopify's Dawn, Craft, Sense; Squarespace's commerce
|
|
27
|
+
* templates) is composed of a handful of PARTS, in a familiar order: a hero
|
|
28
|
+
* that sells, the store's bestsellers, its collections as picture tiles, the
|
|
29
|
+
* brand's story beside a photo, why to buy here with icons, what customers
|
|
30
|
+
* say, and a newsletter sign-up. This module names those parts by the words a
|
|
31
|
+
* plan gives its sections, so the plan step can make sure a store's home has
|
|
32
|
+
* them and the compiler and the listings can draw each one its way.
|
|
33
|
+
*
|
|
34
|
+
* It is NOT a section library (Zach 10/7): the model still names, orders and
|
|
35
|
+
* writes every section, and designs each in the layout language; what code
|
|
36
|
+
* adds is a part the plan left out — named in plain words for the model to
|
|
37
|
+
* design — and the platform elements that show the store's own records.
|
|
38
|
+
*
|
|
39
|
+
* Pure: names and plans only.
|
|
40
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
41
|
+
/** The section names that say each part, as a plan or a page heading words it. */ export const AI_STOREFRONT_PART_WORDS = {
|
|
42
|
+
featured: /\b(bestsellers?|best[- ]sellers?|best[- ]selling|featured|new arrivals?|arrivals|just in|favou?rites?|signature|most loved|top picks?|our (?:candles|products|picks)|shop (?:all|now|the))\b/i,
|
|
43
|
+
collections: /\b(collections?|categor(?:y|ies)|shop by|browse by|by scent|scents?|ranges?|families|occasions?|edits?)\b/i,
|
|
44
|
+
story: /\b(story|about|our craft|craft|making|made|maker|process|how (?:we|it)|behind|studio|workshop)\b/i,
|
|
45
|
+
values: /\b(why|promise|promises|values?|difference|shipping|returns?|guarantee|perks?|benefits?|care|small[- ]batch|ingredients?|sustainab\w*)\b/i,
|
|
46
|
+
reviews: /\b(reviews?|testimonials?|customers? (?:say|love)|what (?:people|customers|our customers) say|kind words|praise|loved by|social proof)\b/i,
|
|
47
|
+
signup: /\b(newsletter|subscribe|sign[- ]?up|mailing list|join (?:us|the list|our list)|stay in touch|in the loop|first to know|inbox|letters? from)\b/i
|
|
48
|
+
};
|
|
49
|
+
/** Whether a planned section's name says a part. */ export function aiStorefrontSays(part, name) {
|
|
50
|
+
return AI_STOREFRONT_PART_WORDS[part].test(name);
|
|
51
|
+
}
|
|
52
|
+
/** The plan entry a part the plan left out is added as: plain words the page step designs. */ const ADDED = {
|
|
53
|
+
featured: {
|
|
54
|
+
name: 'Bestsellers',
|
|
55
|
+
uses: [],
|
|
56
|
+
items: 4
|
|
57
|
+
},
|
|
58
|
+
collections: {
|
|
59
|
+
name: 'Shop by collection',
|
|
60
|
+
uses: [],
|
|
61
|
+
items: 3
|
|
62
|
+
},
|
|
63
|
+
reviews: {
|
|
64
|
+
name: 'Customer reviews',
|
|
65
|
+
uses: [],
|
|
66
|
+
items: 0
|
|
67
|
+
},
|
|
68
|
+
signup: {
|
|
69
|
+
name: 'Newsletter sign-up',
|
|
70
|
+
uses: [],
|
|
71
|
+
items: 0
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** The most sections one page of a site start holds (`AI_SITE_MAX_SECTIONS`). */ const MOST_SECTIONS = 8;
|
|
75
|
+
const firstSegment = (slug)=>slug.trim().replace(/^\/+/, '').split('/')[0].toLowerCase();
|
|
76
|
+
/** A section that closes a page: its call to action, a contact or a sign-up band. */ const CLOSING = /\b(cta|call to action|closing|close|contact|get in touch|visit)\b/i;
|
|
77
|
+
/**
|
|
78
|
+
* A store plan's screens with the storefront a shopper expects (AGL-3676):
|
|
79
|
+
*
|
|
80
|
+
* - THE HOME (paid starts only — the Free wall prices each section, and a
|
|
81
|
+
* Free store sells nothing yet): its hero first, then any part the plan
|
|
82
|
+
* left out, added under plain names while the page has room (eight
|
|
83
|
+
* sections), in order of need: the bestsellers the platform lists,
|
|
84
|
+
* right after the hero; the collections as picture tiles after them; the
|
|
85
|
+
* newsletter sign-up as the page's last band; and customer reviews (the
|
|
86
|
+
* store's real ones, which show only once shoppers leave them) before
|
|
87
|
+
* the close.
|
|
88
|
+
* - THE SHOP PAGE: grid first. Its product grid section moves to second,
|
|
89
|
+
* straight under the page's short title, so the products open above the
|
|
90
|
+
* fold; anything the plan put between them (an intro, a story) follows
|
|
91
|
+
* the grid.
|
|
92
|
+
*
|
|
93
|
+
* Every section the plan named stays, in its order otherwise; nothing is
|
|
94
|
+
* removed, and a page at eight sections is given nothing more.
|
|
95
|
+
*/ export function aiStorefrontPlanScreens(screens, options) {
|
|
96
|
+
return screens.map((screen)=>{
|
|
97
|
+
if (firstSegment(screen.slug) === '') return options.paid ? storefrontHome(screen) : screen;
|
|
98
|
+
if (!options.isShopPage(screen) || screen.sections.length < 3) return screen;
|
|
99
|
+
const grid = options.gridSection(screen.sections);
|
|
100
|
+
if (grid <= 1) return screen;
|
|
101
|
+
const sections = [
|
|
102
|
+
...screen.sections
|
|
103
|
+
];
|
|
104
|
+
const [moved] = sections.splice(grid, 1);
|
|
105
|
+
sections.splice(1, 0, moved);
|
|
106
|
+
return _extends({}, screen, {
|
|
107
|
+
sections
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function storefrontHome(screen) {
|
|
112
|
+
const sections = [
|
|
113
|
+
...screen.sections
|
|
114
|
+
];
|
|
115
|
+
if (!sections.length) return screen;
|
|
116
|
+
// A section about the collections that also names the bestsellers is the bestsellers'.
|
|
117
|
+
const says = (part, name)=>aiStorefrontSays(part, name) && (part !== 'collections' || !aiStorefrontSays('featured', name));
|
|
118
|
+
const has = (part)=>sections.some((section, index)=>index > 0 && says(part, section.name));
|
|
119
|
+
const room = ()=>sections.length < MOST_SECTIONS;
|
|
120
|
+
// What a shopper meets first matters most, so the parts are added in that
|
|
121
|
+
// order while the page has room: the bestsellers, the collections, the
|
|
122
|
+
// sign-up, then the reviews — which show nothing until a shopper writes one.
|
|
123
|
+
if (!has('featured') && room()) sections.splice(1, 0, _extends({}, ADDED.featured));
|
|
124
|
+
if (!has('collections') && room()) {
|
|
125
|
+
// The collections follow the bestsellers.
|
|
126
|
+
const featured = sections.findIndex((section, index)=>index > 0 && says('featured', section.name));
|
|
127
|
+
sections.splice(featured > 0 ? featured + 1 : 1, 0, _extends({}, ADDED.collections));
|
|
128
|
+
}
|
|
129
|
+
// The newsletter is the page's last band, under any closing call to action.
|
|
130
|
+
if (!has('signup') && room()) sections.push(_extends({}, ADDED.signup));
|
|
131
|
+
if (!has('reviews') && room()) {
|
|
132
|
+
// The reviews come just before the page closes: above a closing sign-up
|
|
133
|
+
// and above a closing call to action.
|
|
134
|
+
let at = sections.length;
|
|
135
|
+
if (at - 1 > 0 && says('signup', sections[at - 1].name)) at -= 1;
|
|
136
|
+
if (at - 1 > 0 && CLOSING.test(sections[at - 1].name)) at -= 1;
|
|
137
|
+
sections.splice(at, 0, _extends({}, ADDED.reviews));
|
|
138
|
+
}
|
|
139
|
+
return sections.length === screen.sections.length ? screen : _extends({}, screen, {
|
|
140
|
+
sections
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// ── Icons for a store's reasons to buy ────────────────────────────────────
|
|
144
|
+
/** Words that suggest an icon from the platform's icon library, first match wins. */ const ICON_WORDS = [
|
|
145
|
+
[
|
|
146
|
+
/\b(ship|shipping|shipped|deliver\w*|dispatch\w*|post(?:age|ed)?|courier)\b/i,
|
|
147
|
+
'truck'
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
/\b(returns?|refunds?|exchanges?)\b/i,
|
|
151
|
+
'recycle'
|
|
152
|
+
],
|
|
153
|
+
[
|
|
154
|
+
/\b(gifts?|gifting|wrapped|wrapping|boxed)\b/i,
|
|
155
|
+
'gift'
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
/\b(natural|soy|organic|plant|vegan|botanical|clean|non-toxic|eco|sustainab\w*|cotton|wood)\b/i,
|
|
159
|
+
'leaf'
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
/\b(hand|handmade|hand-poured|poured|small[- ]batch|batch|crafted|made|maker|artisan)\b/i,
|
|
163
|
+
'care'
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
/\b(guarantee\w*|secure|safe|safety|warranty|protected)\b/i,
|
|
167
|
+
'shield'
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
/\b(fast|quick|same[- ]day|next[- ]day)\b/i,
|
|
171
|
+
'fast'
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
/\b(quality|award\w*|premium|finest)\b/i,
|
|
175
|
+
'award'
|
|
176
|
+
],
|
|
177
|
+
[
|
|
178
|
+
/\b(scents?|fragrances?|aroma\w*|candles?|glow|light|burn\w*)\b/i,
|
|
179
|
+
'sparkle'
|
|
180
|
+
],
|
|
181
|
+
[
|
|
182
|
+
/\b(local|town|city|community)\b/i,
|
|
183
|
+
'location'
|
|
184
|
+
],
|
|
185
|
+
[
|
|
186
|
+
/\b(support|help|questions?|chat)\b/i,
|
|
187
|
+
'chat'
|
|
188
|
+
]
|
|
189
|
+
];
|
|
190
|
+
/** The fallbacks an item takes in turn, so a row of icons never repeats one. */ const ICON_FALLBACKS = [
|
|
191
|
+
'star',
|
|
192
|
+
'heart',
|
|
193
|
+
'sparkle',
|
|
194
|
+
'check',
|
|
195
|
+
'smile',
|
|
196
|
+
'award'
|
|
197
|
+
];
|
|
198
|
+
/**
|
|
199
|
+
* The icon each of a store's reasons to buy shows (AGL-3676): its own where
|
|
200
|
+
* the design named one, else one its words suggest, else the next of a few
|
|
201
|
+
* neutral ones — never the same twice in one row.
|
|
202
|
+
*/ export function aiStorefrontItemIcons(items, known) {
|
|
203
|
+
const used = new Set();
|
|
204
|
+
const designed = items.map((item)=>{
|
|
205
|
+
if (item.icon && known(item.icon) && !used.has(item.icon)) {
|
|
206
|
+
used.add(item.icon);
|
|
207
|
+
return item.icon;
|
|
208
|
+
}
|
|
209
|
+
return '';
|
|
210
|
+
});
|
|
211
|
+
return designed.map((icon, index)=>{
|
|
212
|
+
var _ref;
|
|
213
|
+
var _ICON_WORDS_find;
|
|
214
|
+
if (icon) return icon;
|
|
215
|
+
const words = `${items[index].title} ${items[index].text}`;
|
|
216
|
+
const suggested = (_ICON_WORDS_find = ICON_WORDS.find(([pattern, word])=>pattern.test(words) && !used.has(word) && known(word))) == null ? void 0 : _ICON_WORDS_find[1];
|
|
217
|
+
const picked = (_ref = suggested != null ? suggested : ICON_FALLBACKS.find((word)=>!used.has(word) && known(word))) != null ? _ref : 'star';
|
|
218
|
+
used.add(picked);
|
|
219
|
+
return picked;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//# sourceMappingURL=ai-layout-storefront.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-storefront.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A STORE'S STOREFRONT (AGL-3676, second round).\n *\n * Zach, 2026-10-09, on the beta.237 Willow Wick Candles start (job\n * MtlXWjvt9P): \"again this … site does not look like a store front\". Its home\n * was a lifestyle hero, ONE row of featured products and four text-only bands\n * (\"Why soy, why small batch\", \"How our candles are made\", \"Made to be\n * loved\", \"Gifting and shipping\"); its Shop page opened with a tall hero and\n * the grid below the fold.\n *\n * A designer storefront (Shopify's Dawn, Craft, Sense; Squarespace's commerce\n * templates) is composed of a handful of PARTS, in a familiar order: a hero\n * that sells, the store's bestsellers, its collections as picture tiles, the\n * brand's story beside a photo, why to buy here with icons, what customers\n * say, and a newsletter sign-up. This module names those parts by the words a\n * plan gives its sections, so the plan step can make sure a store's home has\n * them and the compiler and the listings can draw each one its way.\n *\n * It is NOT a section library (Zach 10/7): the model still names, orders and\n * writes every section, and designs each in the layout language; what code\n * adds is a part the plan left out — named in plain words for the model to\n * design — and the platform elements that show the store's own records.\n *\n * Pure: names and plans only.\n */\n\nimport type { AiBuildPlanScreen, AiBuildPlanSection } from '../model/ai-build-plan'\n\n/** A part a storefront home is composed of. */\nexport type AiStorefrontPart = 'featured' | 'collections' | 'story' | 'values' | 'reviews' | 'signup'\n\n/** The section names that say each part, as a plan or a page heading words it. */\nexport const AI_STOREFRONT_PART_WORDS: Readonly<Record<AiStorefrontPart, RegExp>> = {\n featured:\n /\\b(bestsellers?|best[- ]sellers?|best[- ]selling|featured|new arrivals?|arrivals|just in|favou?rites?|signature|most loved|top picks?|our (?:candles|products|picks)|shop (?:all|now|the))\\b/i,\n collections: /\\b(collections?|categor(?:y|ies)|shop by|browse by|by scent|scents?|ranges?|families|occasions?|edits?)\\b/i,\n story: /\\b(story|about|our craft|craft|making|made|maker|process|how (?:we|it)|behind|studio|workshop)\\b/i,\n values: /\\b(why|promise|promises|values?|difference|shipping|returns?|guarantee|perks?|benefits?|care|small[- ]batch|ingredients?|sustainab\\w*)\\b/i,\n reviews: /\\b(reviews?|testimonials?|customers? (?:say|love)|what (?:people|customers|our customers) say|kind words|praise|loved by|social proof)\\b/i,\n signup: /\\b(newsletter|subscribe|sign[- ]?up|mailing list|join (?:us|the list|our list)|stay in touch|in the loop|first to know|inbox|letters? from)\\b/i,\n}\n\n/** Whether a planned section's name says a part. */\nexport function aiStorefrontSays(part: AiStorefrontPart, name: string): boolean {\n return AI_STOREFRONT_PART_WORDS[part].test(name)\n}\n\n/** The plan entry a part the plan left out is added as: plain words the page step designs. */\nconst ADDED: Readonly<Record<'featured' | 'collections' | 'reviews' | 'signup', AiBuildPlanSection>> = {\n featured: { name: 'Bestsellers', uses: [], items: 4 },\n collections: { name: 'Shop by collection', uses: [], items: 3 },\n reviews: { name: 'Customer reviews', uses: [], items: 0 },\n signup: { name: 'Newsletter sign-up', uses: [], items: 0 },\n}\n\n/** The most sections one page of a site start holds (`AI_SITE_MAX_SECTIONS`). */\nconst MOST_SECTIONS = 8\n\nconst firstSegment = (slug: string) => slug.trim().replace(/^\\/+/, '').split('/')[0].toLowerCase()\n\n/** A section that closes a page: its call to action, a contact or a sign-up band. */\nconst CLOSING = /\\b(cta|call to action|closing|close|contact|get in touch|visit)\\b/i\n\n/**\n * A store plan's screens with the storefront a shopper expects (AGL-3676):\n *\n * - THE HOME (paid starts only — the Free wall prices each section, and a\n * Free store sells nothing yet): its hero first, then any part the plan\n * left out, added under plain names while the page has room (eight\n * sections), in order of need: the bestsellers the platform lists,\n * right after the hero; the collections as picture tiles after them; the\n * newsletter sign-up as the page's last band; and customer reviews (the\n * store's real ones, which show only once shoppers leave them) before\n * the close.\n * - THE SHOP PAGE: grid first. Its product grid section moves to second,\n * straight under the page's short title, so the products open above the\n * fold; anything the plan put between them (an intro, a story) follows\n * the grid.\n *\n * Every section the plan named stays, in its order otherwise; nothing is\n * removed, and a page at eight sections is given nothing more.\n */\nexport function aiStorefrontPlanScreens<S extends Pick<AiBuildPlanScreen, 'slug' | 'title' | 'sections'>>(\n screens: readonly S[],\n options: { paid: boolean; isShopPage: (screen: S) => boolean; gridSection: (sections: S['sections']) => number },\n): S[] {\n return screens.map((screen) => {\n if (firstSegment(screen.slug) === '') return options.paid ? storefrontHome(screen) : screen\n if (!options.isShopPage(screen) || screen.sections.length < 3) return screen\n const grid = options.gridSection(screen.sections)\n if (grid <= 1) return screen\n const sections = [...screen.sections]\n const [moved] = sections.splice(grid, 1)\n sections.splice(1, 0, moved)\n return { ...screen, sections }\n })\n}\n\nfunction storefrontHome<S extends Pick<AiBuildPlanScreen, 'sections'>>(screen: S): S {\n const sections = [...screen.sections]\n if (!sections.length) return screen\n // A section about the collections that also names the bestsellers is the bestsellers'.\n const says = (part: AiStorefrontPart, name: string) =>\n aiStorefrontSays(part, name) && (part !== 'collections' || !aiStorefrontSays('featured', name))\n const has = (part: AiStorefrontPart) => sections.some((section, index) => index > 0 && says(part, section.name))\n const room = () => sections.length < MOST_SECTIONS\n // What a shopper meets first matters most, so the parts are added in that\n // order while the page has room: the bestsellers, the collections, the\n // sign-up, then the reviews — which show nothing until a shopper writes one.\n if (!has('featured') && room()) sections.splice(1, 0, { ...ADDED.featured })\n if (!has('collections') && room()) {\n // The collections follow the bestsellers.\n const featured = sections.findIndex((section, index) => index > 0 && says('featured', section.name))\n sections.splice(featured > 0 ? featured + 1 : 1, 0, { ...ADDED.collections })\n }\n // The newsletter is the page's last band, under any closing call to action.\n if (!has('signup') && room()) sections.push({ ...ADDED.signup })\n if (!has('reviews') && room()) {\n // The reviews come just before the page closes: above a closing sign-up\n // and above a closing call to action.\n let at = sections.length\n if (at - 1 > 0 && says('signup', sections[at - 1].name)) at -= 1\n if (at - 1 > 0 && CLOSING.test(sections[at - 1].name)) at -= 1\n sections.splice(at, 0, { ...ADDED.reviews })\n }\n return sections.length === screen.sections.length ? screen : { ...screen, sections }\n}\n\n// ── Icons for a store's reasons to buy ────────────────────────────────────\n\n/** Words that suggest an icon from the platform's icon library, first match wins. */\nconst ICON_WORDS: ReadonlyArray<readonly [RegExp, string]> = [\n [/\\b(ship|shipping|shipped|deliver\\w*|dispatch\\w*|post(?:age|ed)?|courier)\\b/i, 'truck'],\n [/\\b(returns?|refunds?|exchanges?)\\b/i, 'recycle'],\n [/\\b(gifts?|gifting|wrapped|wrapping|boxed)\\b/i, 'gift'],\n [/\\b(natural|soy|organic|plant|vegan|botanical|clean|non-toxic|eco|sustainab\\w*|cotton|wood)\\b/i, 'leaf'],\n [/\\b(hand|handmade|hand-poured|poured|small[- ]batch|batch|crafted|made|maker|artisan)\\b/i, 'care'],\n [/\\b(guarantee\\w*|secure|safe|safety|warranty|protected)\\b/i, 'shield'],\n [/\\b(fast|quick|same[- ]day|next[- ]day)\\b/i, 'fast'],\n [/\\b(quality|award\\w*|premium|finest)\\b/i, 'award'],\n [/\\b(scents?|fragrances?|aroma\\w*|candles?|glow|light|burn\\w*)\\b/i, 'sparkle'],\n [/\\b(local|town|city|community)\\b/i, 'location'],\n [/\\b(support|help|questions?|chat)\\b/i, 'chat'],\n]\n\n/** The fallbacks an item takes in turn, so a row of icons never repeats one. */\nconst ICON_FALLBACKS = ['star', 'heart', 'sparkle', 'check', 'smile', 'award'] as const\n\n/**\n * The icon each of a store's reasons to buy shows (AGL-3676): its own where\n * the design named one, else one its words suggest, else the next of a few\n * neutral ones — never the same twice in one row.\n */\nexport function aiStorefrontItemIcons(\n items: ReadonlyArray<{ title: string; text: string; icon?: string }>,\n known: (word: string | undefined) => boolean,\n): string[] {\n const used = new Set<string>()\n const designed = items.map((item) => {\n if (item.icon && known(item.icon) && !used.has(item.icon)) {\n used.add(item.icon)\n return item.icon\n }\n return ''\n })\n return designed.map((icon, index) => {\n if (icon) return icon\n const words = `${items[index].title} ${items[index].text}`\n const suggested = ICON_WORDS.find(([pattern, word]) => pattern.test(words) && !used.has(word) && known(word))?.[1]\n const picked = suggested ?? ICON_FALLBACKS.find((word) => !used.has(word) && known(word)) ?? 'star'\n used.add(picked)\n return picked\n })\n}\n"],"names":["AI_STOREFRONT_PART_WORDS","featured","collections","story","values","reviews","signup","aiStorefrontSays","part","name","test","ADDED","uses","items","MOST_SECTIONS","firstSegment","slug","trim","replace","split","toLowerCase","CLOSING","aiStorefrontPlanScreens","screens","options","map","screen","paid","storefrontHome","isShopPage","sections","length","grid","gridSection","moved","splice","says","has","some","section","index","room","findIndex","push","at","ICON_WORDS","ICON_FALLBACKS","aiStorefrontItemIcons","known","used","Set","designed","item","icon","add","suggested","words","title","text","find","pattern","word","picked"],"mappings":"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"}
|
|
@@ -44,6 +44,14 @@ export declare const AI_BUILD_LIMITS: {
|
|
|
44
44
|
/** A Free workspace's pages. */
|
|
45
45
|
readonly freePages: 2;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* What a Free build's plan card says when it holds as many pages as a Free
|
|
49
|
+
* build makes (AGL-3722): the plan is told the cap and plans the first pages
|
|
50
|
+
* a request asks for, so a request for three — "About us, Custom cakes and
|
|
51
|
+
* Catering", 2026-10-10 — came back as two with nothing saying why. `null`
|
|
52
|
+
* when the plan holds fewer, or on a paid workspace.
|
|
53
|
+
*/
|
|
54
|
+
export declare function aiBuildFreePageCapNote(plan: Pick<AiBuildPlan, 'screens'>, freeTaste: boolean): string | null;
|
|
47
55
|
/** What a build creates through its `create` list; everything else beyond pages is an item. */
|
|
48
56
|
export declare const AI_BUILD_CREATE_KINDS: readonly AiBuildPlanCreateKind[];
|
|
49
57
|
/**
|
|
@@ -127,7 +135,7 @@ export declare function aiBuildCreditEstimate(plan: AiBuildPlan, options?: {
|
|
|
127
135
|
export declare function aiJobCreditEstimate(kind: string, plan: AiBuildPlan): number;
|
|
128
136
|
/**
|
|
129
137
|
* What one unit is likely to cost, its p90 and its ceiling (AGL-3722): a page
|
|
130
|
-
*
|
|
138
|
+
* as a page written on its own measured (a fixed cost and one per section), a creation at what its kind
|
|
131
139
|
* measured, an item at the measured pass times the passes its capability
|
|
132
140
|
* estimates (none for a draft another plugin writes, which asks no model).
|
|
133
141
|
* The ceiling is `aiBuildUnitCreditEstimate`, unchanged.
|
|
@@ -195,6 +203,8 @@ export declare function aiBuildDegradation(unit: AiBuildUnit, context: {
|
|
|
195
203
|
ledger: readonly AiJobItemLedger[];
|
|
196
204
|
ops?: AiBuildOps;
|
|
197
205
|
}): AiBuildDegradation;
|
|
206
|
+
/** The dataset a form unit writes to, by its `new:` name lowercased; `null` for any other unit (AGL-3616). */
|
|
207
|
+
export declare function aiBuildUnitWritesTo(unit: Pick<AiBuildUnit, 'creation'>): string | null;
|
|
198
208
|
/** What each creation is called in a note. */
|
|
199
209
|
export declare function aiBuildCreationNoun(kind: AiBuildPlanCreateKind): string;
|
|
200
210
|
/**
|
|
@@ -16,7 +16,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { pluginAiCapabilityArgsProblems } from "@aglyn/aglyn/plugin-manager/plugin-ai-capabilities";
|
|
18
18
|
import { AI_PLAN_NEW_REF_PREFIX, isAiPlanNewRef } from "./ai-build-plan.js";
|
|
19
|
-
import { AI_SITE_HOME_FIRST_LABEL, AI_SITE_MAX_SECTIONS, AI_SITE_PASS_CREDITS, aiCreationMeasuredCredits, aiJobPlanCreditEstimate, aiJobPlanCreditRange,
|
|
19
|
+
import { AI_SITE_HOME_FIRST_LABEL, AI_SITE_MAX_SECTIONS, AI_SITE_PASS_CREDITS, aiCreationMeasuredCredits, aiJobPlanCreditEstimate, aiJobPlanCreditRange, aiSitePlanIsHome, aiStandaloneScreenCreditRange } from "./ai-site-job.js";
|
|
20
20
|
import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCreditRangeOf, aiCreditRangeOrdered } from "./ai-credit-estimate.js";
|
|
21
21
|
/**
|
|
22
22
|
* What a `build` job is (AGL-3616): one request — "a few pages, a contact
|
|
@@ -40,6 +40,17 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
40
40
|
pages: 8,
|
|
41
41
|
/** A Free workspace's pages. */ freePages: 2
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* What a Free build's plan card says when it holds as many pages as a Free
|
|
45
|
+
* build makes (AGL-3722): the plan is told the cap and plans the first pages
|
|
46
|
+
* a request asks for, so a request for three — "About us, Custom cakes and
|
|
47
|
+
* Catering", 2026-10-10 — came back as two with nothing saying why. `null`
|
|
48
|
+
* when the plan holds fewer, or on a paid workspace.
|
|
49
|
+
*/ export function aiBuildFreePageCapNote(plan, freeTaste) {
|
|
50
|
+
const cap = AI_BUILD_LIMITS.freePages;
|
|
51
|
+
if (!freeTaste || plan.screens.length < cap) return null;
|
|
52
|
+
return `A Free workspace’s build makes up to ${cap} pages at a time. If you asked for more, ask Assist for the rest once this build is done.`;
|
|
53
|
+
}
|
|
43
54
|
/** What a build creates through its `create` list; everything else beyond pages is an item. */ export const AI_BUILD_CREATE_KINDS = [
|
|
44
55
|
'layout',
|
|
45
56
|
'form',
|
|
@@ -96,15 +107,18 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
96
107
|
};
|
|
97
108
|
const units = [];
|
|
98
109
|
plan.create.forEach((creation, index)=>{
|
|
110
|
+
var _creation_writesTo;
|
|
99
111
|
if (!AI_BUILD_CREATE_KINDS.includes(creation.kind)) return;
|
|
100
112
|
const slot = `c${index}`;
|
|
113
|
+
// A form is built after the dataset it writes to (AGL-3616).
|
|
101
114
|
units.push({
|
|
102
115
|
slot,
|
|
103
116
|
op: creationOp(creation),
|
|
104
117
|
label: creation.name,
|
|
105
118
|
creation,
|
|
106
119
|
deps: depsOf([
|
|
107
|
-
creation.duplicateOf
|
|
120
|
+
creation.duplicateOf,
|
|
121
|
+
creation.kind === 'form' ? (_creation_writesTo = creation.writesTo) != null ? _creation_writesTo : null : null
|
|
108
122
|
], slot)
|
|
109
123
|
});
|
|
110
124
|
});
|
|
@@ -266,14 +280,17 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
266
280
|
}
|
|
267
281
|
/**
|
|
268
282
|
* What one unit is likely to cost, its p90 and its ceiling (AGL-3722): a page
|
|
269
|
-
*
|
|
283
|
+
* as a page written on its own measured (a fixed cost and one per section), a creation at what its kind
|
|
270
284
|
* measured, an item at the measured pass times the passes its capability
|
|
271
285
|
* estimates (none for a draft another plugin writes, which asks no model).
|
|
272
286
|
* The ceiling is `aiBuildUnitCreditEstimate`, unchanged.
|
|
273
287
|
*/ export function aiBuildUnitCreditRange(unit, ops) {
|
|
274
288
|
const ceiling = aiBuildUnitCreditEstimate(unit, ops);
|
|
275
|
-
|
|
276
|
-
if (unit.
|
|
289
|
+
// A build writes each page on its own, never in a site start's warm run (AGL-3722).
|
|
290
|
+
if (unit.screen) return aiStandaloneScreenCreditRange(unit.screen.sections.length);
|
|
291
|
+
if (unit.creation) return aiCreditRangeOf(aiCreationMeasuredCredits(unit.creation.kind, {
|
|
292
|
+
standalone: true
|
|
293
|
+
}), ceiling);
|
|
277
294
|
const passes = Math.ceil(ceiling / AI_SITE_PASS_CREDITS);
|
|
278
295
|
return aiCreditRangeOf({
|
|
279
296
|
median: passes * AI_MEASURED_PASS_CREDITS.median,
|
|
@@ -341,7 +358,7 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
341
358
|
const smaller = aiBuildFirstPagePlan(plan);
|
|
342
359
|
if (!smaller) return null;
|
|
343
360
|
const screen = smaller.screens[0];
|
|
344
|
-
const label = aiSitePlanIsHome(screen) ? AI_SITE_HOME_FIRST_LABEL : `Build the
|
|
361
|
+
const label = aiSitePlanIsHome(screen) ? AI_SITE_HOME_FIRST_LABEL : `Build the ${screen.title} page first`;
|
|
345
362
|
return _extends({
|
|
346
363
|
label
|
|
347
364
|
}, aiBuildCreditRange(smaller, {
|
|
@@ -421,6 +438,13 @@ const OPEN = [
|
|
|
421
438
|
const noun = dep.creation ? aiBuildCreationNoun(dep.creation.kind) : (_ref = (_context_ops = context.ops) == null ? void 0 : (_context_ops_get = _context_ops.get(dep.op)) == null ? void 0 : _context_ops_get.noun) != null ? _ref : dep.op;
|
|
422
439
|
const capability = dep.item ? (_context_ops1 = context.ops) == null ? void 0 : _context_ops1.get(dep.op) : undefined;
|
|
423
440
|
const degrade = (_ref1 = (_ref2 = (_dep_item = dep.item) == null ? void 0 : _dep_item.degrade) != null ? _ref2 : capability == null ? void 0 : capability.degrade) != null ? _ref1 : ((_dep_creation = dep.creation) == null ? void 0 : _dep_creation.kind) === 'layout' ? 'fallback' : 'omit';
|
|
441
|
+
// A form whose dataset could not be made is built without it (AGL-3616):
|
|
442
|
+
// its submissions reach the Inbox, as any form's do.
|
|
443
|
+
if (aiBuildUnitWritesTo(unit) === dep.label.toLowerCase()) {
|
|
444
|
+
result.degradedBy.push(slot);
|
|
445
|
+
result.notes.push(`Its submissions arrive in the Inbox only: the dataset “${dep.label}” could not be created.`);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
424
448
|
if (!unit.screen && degrade === 'omit') {
|
|
425
449
|
result.skip = `Not built: the ${noun} “${dep.label}” it needs could not be created.`;
|
|
426
450
|
result.degradedBy.push(slot);
|
|
@@ -444,6 +468,11 @@ const OPEN = [
|
|
|
444
468
|
}
|
|
445
469
|
return result;
|
|
446
470
|
}
|
|
471
|
+
/** The dataset a form unit writes to, by its `new:` name lowercased; `null` for any other unit (AGL-3616). */ export function aiBuildUnitWritesTo(unit) {
|
|
472
|
+
var _unit_creation;
|
|
473
|
+
const ref = ((_unit_creation = unit.creation) == null ? void 0 : _unit_creation.kind) === 'form' ? unit.creation.writesTo : null;
|
|
474
|
+
return isAiPlanNewRef(ref) ? aiBuildRefName(ref) : null;
|
|
475
|
+
}
|
|
447
476
|
/** What each creation is called in a note. */ export function aiBuildCreationNoun(kind) {
|
|
448
477
|
return kind === 'email' ? 'email design' : kind === 'theme-change' ? 'theme change' : kind;
|
|
449
478
|
}
|