@aglyn/plugins-ai 1.0.0-beta.244 → 1.0.0-beta.247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-links.js +21 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +3 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-hub-card.component.js +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.js +17 -9
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +98 -23
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.js +1 -1
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/declarations.server.js +12 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +216 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +3 -1
- package/src/lib/jobs/ai-job-build-step.js +124 -18
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-duplicate-step.d.ts +123 -0
- package/src/lib/jobs/ai-job-duplicate-step.js +269 -0
- package/src/lib/jobs/ai-job-duplicate-step.js.map +1 -0
- package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
- package/src/lib/jobs/ai-job-form-dataset.js +28 -10
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
- package/src/lib/jobs/ai-job-form-step.js +98 -28
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
- package/src/lib/jobs/ai-job-layout-language.js +29 -5
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +4 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
- package/src/lib/jobs/ai-job-media-step.js +610 -0
- package/src/lib/jobs/ai-job-media-step.js.map +1 -0
- package/src/lib/jobs/ai-job-navigation-step.d.ts +157 -0
- package/src/lib/jobs/ai-job-navigation-step.js +439 -0
- package/src/lib/jobs/ai-job-navigation-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +4 -1
- package/src/lib/jobs/ai-job-page-language.js +30 -5
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +5 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +3 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-plan-step.js +87 -20
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-products-step.js +2 -2
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +268 -0
- package/src/lib/jobs/ai-job-settings-step.js +690 -0
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -0
- package/src/lib/jobs/ai-job-site-content.d.ts +22 -8
- package/src/lib/jobs/ai-job-site-content.js +33 -15
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +3 -1
- package/src/lib/jobs/ai-job-site-datasets.js +13 -5
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
- package/src/lib/jobs/ai-job-site-events.js +313 -0
- package/src/lib/jobs/ai-job-site-events.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-step.js +132 -26
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
- package/src/lib/jobs/ai-job-tracking-step.js +501 -0
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +21 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +41 -4
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +62 -4
- package/src/lib/jobs/ai-site-publish.js +202 -56
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
- package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
- package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -7
- package/src/lib/layout-language/ai-layout-compiler.js +62 -42
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-frame-hollows.js +330 -0
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +24 -5
- package/src/lib/layout-language/ai-layout-frame.js +93 -20
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +3 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
- package/src/lib/layout-language/ai-layout-labels.js +162 -0
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.js +5 -2
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +49 -1
- package/src/lib/layout-language/ai-layout-listings.js +66 -5
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-tree.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-tree.js +17 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -1
- package/src/lib/model/ai-build-event-page.d.ts +70 -0
- package/src/lib/model/ai-build-event-page.js +145 -0
- package/src/lib/model/ai-build-event-page.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +44 -0
- package/src/lib/model/ai-build-job.js +141 -13
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +39 -3
- package/src/lib/model/ai-build-plan.js +59 -5
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +52 -0
- package/src/lib/model/ai-credit-estimate.js +51 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +6 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +18 -2
- package/src/lib/model/ai-jobs.types.js +6 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-layout-menus.d.ts +104 -0
- package/src/lib/model/ai-layout-menus.js +518 -0
- package/src/lib/model/ai-layout-menus.js.map +1 -0
- package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-products.d.ts +31 -1
- package/src/lib/model/ai-products.js +34 -1
- package/src/lib/model/ai-products.js.map +1 -1
- package/src/lib/model/ai-site-inventory.d.ts +17 -0
- package/src/lib/model/ai-site-inventory.js +5 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +12 -2
- package/src/lib/model/ai-site-job.js +39 -12
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-store-pages.js +1 -1
- package/src/lib/model/ai-site-store-pages.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +78 -3
- package/src/lib/runtime/ai-doctrine-validators.js +219 -31
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -1
- package/src/lib/runtime/ai-doctrine.js +16 -7
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +3 -1
- package/src/lib/runtime/ai-palette.generated.js +499 -11
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +5 -3
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-products-generation.d.ts +4 -2
- package/src/lib/runtime/ai-products-generation.js +12 -6
- package/src/lib/runtime/ai-products-generation.js.map +1 -1
- package/src/lib/runtime/ai-template-examples.js +8 -2
- package/src/lib/runtime/ai-template-examples.js.map +1 -1
- package/src/lib/runtime/site-inventory.d.ts +12 -0
- package/src/lib/runtime/site-inventory.js +29 -1
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server.js +20 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-dataset-index.js +37 -3
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
- package/src/lib/tools/ai-products-tool.d.ts +3 -1
- package/src/lib/tools/ai-products-tool.js +15 -5
- package/src/lib/tools/ai-products-tool.js.map +1 -1
|
@@ -130,6 +130,11 @@ export interface AiDoctrineTreeContext extends AiNodeTreeContext {
|
|
|
130
130
|
* to when the site has none for what it promises. Absent: none is known.
|
|
131
131
|
*/
|
|
132
132
|
homeScreenIds?: readonly string[];
|
|
133
|
+
/**
|
|
134
|
+
* The home pages' own titles (AGL-3760): a link home under one of them says
|
|
135
|
+
* where it goes in the site's language, as "Inicio" does on a Spanish site.
|
|
136
|
+
*/
|
|
137
|
+
homeLabels?: readonly string[];
|
|
133
138
|
/**
|
|
134
139
|
* The site's pages that place its form, by id (AGL-3729): where a link whose
|
|
135
140
|
* words ask a visitor to get in touch (`aiAsksToGetInTouch`) does what it
|
|
@@ -266,7 +271,9 @@ export declare function detectLayoutRegions(tree: AiDoctrineTree, outputKind: Ai
|
|
|
266
271
|
/**
|
|
267
272
|
* Rule 3. A form on a page is a Form bound by id to a form built on the
|
|
268
273
|
* Forms page — fields, validation, consent and routing kept in one place.
|
|
269
|
-
* Loose fields
|
|
274
|
+
* Loose fields — a Form Field, or any of MUI's answer elements (an Input,
|
|
275
|
+
* Select, Radio Group, Checkbox or Rating) — an unbound Form, and fields
|
|
276
|
+
* drawn inside a bound Form (which
|
|
270
277
|
* the page never renders: the placed form's own design replaces them) are
|
|
271
278
|
* refused. A form's own design is the one place fields are drawn.
|
|
272
279
|
*
|
|
@@ -509,7 +516,7 @@ export declare function aiAsksToGetInTouch(label: string): boolean;
|
|
|
509
516
|
* The root is never taken out. A tree with nothing to repair comes back as
|
|
510
517
|
* the same object; a node that is changed is copied, never edited in place.
|
|
511
518
|
*/
|
|
512
|
-
export declare function aiSettleUnrelatedScreenLinks<T extends AiDoctrineTree>(tree: T, outputKind: AiOutputKind, context?: Pick<AiDoctrineTreeContext, 'homeScreenIds' | 'formScreenIds'> & {
|
|
519
|
+
export declare function aiSettleUnrelatedScreenLinks<T extends AiDoctrineTree>(tree: T, outputKind: AiOutputKind, context?: Pick<AiDoctrineTreeContext, 'homeScreenIds' | 'formScreenIds' | 'homeLabels'> & {
|
|
513
520
|
/** The page being checked, by every id it goes by: a link never goes to itself. */
|
|
514
521
|
ownScreenIds?: readonly string[];
|
|
515
522
|
}): T;
|
|
@@ -524,7 +531,7 @@ export declare function aiSettleUnrelatedScreenLinks<T extends AiDoctrineTree>(t
|
|
|
524
531
|
* not say home is refused, with a re-ask to link the screen that does what it
|
|
525
532
|
* says, or to leave the link out.
|
|
526
533
|
*/
|
|
527
|
-
export declare function detectUnrelatedScreenLinks(tree: AiDoctrineTree, outputKind: AiOutputKind, context?: Pick<AiDoctrineTreeContext, 'homeScreenIds' | 'formScreenIds'>): AiDoctrineViolation[];
|
|
534
|
+
export declare function detectUnrelatedScreenLinks(tree: AiDoctrineTree, outputKind: AiOutputKind, context?: Pick<AiDoctrineTreeContext, 'homeScreenIds' | 'formScreenIds' | 'homeLabels'>): AiDoctrineViolation[];
|
|
528
535
|
/**
|
|
529
536
|
* Rule 10, for a link that goes nowhere (AGL-3072). A Button or a Screen Link
|
|
530
537
|
* goes where its `screenId` or its `href` says, and those are the only
|
|
@@ -890,6 +897,38 @@ export declare function aiSettlePlanFormDatasets(plan: AiBuildPlan, inventory: A
|
|
|
890
897
|
* so the rule still asks.
|
|
891
898
|
*/
|
|
892
899
|
export declare function aiSettlePlanLayouts(plan: AiBuildPlan, inventory: AiSiteInventory | null, capabilities?: AiPlanCapabilities | null): AiBuildPlan;
|
|
900
|
+
/**
|
|
901
|
+
* A page section that is a layout region — a header, navigation or footer,
|
|
902
|
+
* read by the name rule 2's `plan-layout-region-section` reads — is taken off
|
|
903
|
+
* the page before the rules read the plan (AGL-3750). The region is already
|
|
904
|
+
* the layout's, so the rule's own answer, "take the section off the page", is
|
|
905
|
+
* the only one: a live Free start of 2026-10-12 (a bakery that sells cakes to
|
|
906
|
+
* order) planned a Header and a Footer on its pages three times and stopped.
|
|
907
|
+
*
|
|
908
|
+
* Only a section that places nothing, or nothing but a layout (which rule 2
|
|
909
|
+
* refuses placing as well), is taken off: one that places a form or a
|
|
910
|
+
* component may be a band misnamed, so the rule still asks. A page whose
|
|
911
|
+
* every section is a region keeps them all, and is re-asked as before, since
|
|
912
|
+
* a page of no sections has no answer here. A home this leaves under its
|
|
913
|
+
* fewest sections, or a Free plan still past its wall, is the existing
|
|
914
|
+
* checks' to settle or refuse.
|
|
915
|
+
*/
|
|
916
|
+
export declare function aiSettlePlanLayoutRegionSections(plan: AiBuildPlan, inventory: AiSiteInventory | null): AiBuildPlan;
|
|
917
|
+
/**
|
|
918
|
+
* A section that shows the work — a gallery, a portfolio, selected works, by
|
|
919
|
+
* the name `aiSiteSectionShowsWork` reads — planned with fewer pieces than
|
|
920
|
+
* the job's `galleryMinItems` is raised to it before the rules read the plan
|
|
921
|
+
* (AGL-3754). The site start's `plan-empty-gallery` refusal names one answer,
|
|
922
|
+
* "plan it with three to six items", and the fewest is the one that keeps
|
|
923
|
+
* the plan's own count closest: a section's items move no section cap, Free
|
|
924
|
+
* wall or credit figure, which count sections and pages.
|
|
925
|
+
*
|
|
926
|
+
* A section the rule reads as one item of a list split into sections is
|
|
927
|
+
* left for rule 1's re-ask to join, as the Free wall's trim leaves it: raised
|
|
928
|
+
* here, it would hide the split. A job that sets no `galleryMinItems` (every
|
|
929
|
+
* job but a site start) is returned unchanged.
|
|
930
|
+
*/
|
|
931
|
+
export declare function aiSettlePlanGalleries(plan: AiBuildPlan, inventory: AiSiteInventory | null, capabilities?: AiPlanCapabilities | null): AiBuildPlan;
|
|
893
932
|
/** The contact form a plan is given when a section collects answers and the plan makes no form. */
|
|
894
933
|
export declare const AI_PLAN_SETTLED_FORM: {
|
|
895
934
|
readonly name: "Contact form";
|
|
@@ -1027,6 +1066,42 @@ export declare function aiFreePageSectionsWithin(creations: {
|
|
|
1027
1066
|
layouts: number;
|
|
1028
1067
|
pages?: number;
|
|
1029
1068
|
}, credits?: Readonly<AiFreePageWorstCase>): number;
|
|
1069
|
+
/**
|
|
1070
|
+
* The most sections past the Free wall a plan is trimmed by rather than
|
|
1071
|
+
* re-asked (AGL-3737). The live candle-shop start of 2026-10-12 planned 9
|
|
1072
|
+
* sections against a wall of 8 on all three answers, each with a home of 7:
|
|
1073
|
+
* the model reaching for the budget the site redo names ("plan 7 to 8") and
|
|
1074
|
+
* landing one band past it, which no re-ask cured. Within two, the plan's
|
|
1075
|
+
* shape is right and only a band or two the brief needs least is extra, so
|
|
1076
|
+
* taking those from the end loses nothing the plan depends on. Further over,
|
|
1077
|
+
* the shape itself is off — a list planned as sections, a page the wall
|
|
1078
|
+
* cannot pay for — and cutting more would drop content the model meant, so
|
|
1079
|
+
* that plan is still re-asked.
|
|
1080
|
+
*/
|
|
1081
|
+
export declare const AI_FREE_WALL_SETTLE_MAX_OVER = 2;
|
|
1082
|
+
/**
|
|
1083
|
+
* A Free plan a section or two past the wall is trimmed to fit before the
|
|
1084
|
+
* rules read the plan (AGL-3737), rather than re-asked: which sections go has
|
|
1085
|
+
* one answer, so the re-ask only costs the member a stopped start. The live
|
|
1086
|
+
* candle-shop start of 2026-10-12 asked for a home of 7 and a second page of
|
|
1087
|
+
* 2 against a wall of 8, three answers running, and stopped needing input.
|
|
1088
|
+
*
|
|
1089
|
+
* One section goes at a time, the last of its page that `freeWallKeeps`
|
|
1090
|
+
* does not hold, from the page furthest past its share: the home at / past
|
|
1091
|
+
* its fewest (`aiSiteHomeMinSections`, five on a site start whose wall pays
|
|
1092
|
+
* for a full home), any other page past two; on a tie, the longer page, then
|
|
1093
|
+
* the home. No home goes under its fewest and no page under one section. The
|
|
1094
|
+
* shares are what a site start is told to plan (a home of five or six, the
|
|
1095
|
+
* other page two or three): the candle shop's 7 + 2 becomes 6 + 2, never
|
|
1096
|
+
* 7 + 1, and the recorded yoga answer's 6 + 4 becomes 5 + 3, the shape its
|
|
1097
|
+
* own redo chose.
|
|
1098
|
+
*
|
|
1099
|
+
* A plan more than `AI_FREE_WALL_SETTLE_MAX_OVER` past the wall, a site
|
|
1100
|
+
* start past its page cap, a plan with a list split into sections, and a
|
|
1101
|
+
* plan the trim cannot fit without a section it keeps are returned
|
|
1102
|
+
* unchanged, for the wall to re-ask.
|
|
1103
|
+
*/
|
|
1104
|
+
export declare function aiSettlePlanFreeWall(plan: AiBuildPlan, inventory: AiSiteInventory | null, capabilities?: AiPlanCapabilities | null): AiBuildPlan;
|
|
1030
1105
|
/**
|
|
1031
1106
|
* The Free wall (plan): a plan asks for no more sections than the Free taste
|
|
1032
1107
|
* pays for at its worst beside what the job builds first (AGL-3070). A live
|
|
@@ -15,6 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { sanitizeAuthorHtml } from "@aglyn/aglyn/app-utils/author-html";
|
|
18
|
+
import { isFormInputNode } from "@aglyn/aglyn/app-utils/forms";
|
|
18
19
|
import { formatBytes, measureNodeMap, nodeMapBytes } from "@aglyn/aglyn/app-utils/measure-node-map";
|
|
19
20
|
import { MEDIA_CDN_VARIANT_WIDTHS, parseMediaRef } from "@aglyn/aglyn/app-utils/media-ref";
|
|
20
21
|
import { ESTIMATED_PAGE_TRANSFER_BYTES } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
@@ -25,7 +26,9 @@ import { parseBreakpointSpan } from "@aglyn/shared-data-enums/breakpoint-span";
|
|
|
25
26
|
import { renderEmailHtml } from "@aglyn/shared-util-email/email-render";
|
|
26
27
|
import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, AI_BUILD_PLAN_LIMITS, AI_PLAN_ASKS_FOR_VIDEO, aiEmbedHostOf, aiEmbedVideoKey, AI_PLAN_DATASET_ITEM_OP, aiPlanCreateFor, aiPlanFormDatasetNames, aiPlanItemFor, aiPlanSlugKey, aiPlanUndeclaredRefs, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
27
28
|
import { aiCreationNoun, aiPlanCanPlaceForm, aiPlanUncreatable, aiPlanUncreatableKind } from "../model/ai-plan-capabilities.js";
|
|
28
|
-
import { aiFreeSiteSectionsWithin, aiSiteHomeMinSections, aiSitePlanIsHome } from "../model/ai-site-job.js";
|
|
29
|
+
import { aiFreeSiteSectionsWithin, aiSiteHomeMinSections, aiSitePlanIsHome, aiSiteSectionShowsWork } from "../model/ai-site-job.js";
|
|
30
|
+
import { aiLayoutIsShopPage, aiLayoutShopGridSection } from "../layout-language/ai-layout-listings.js";
|
|
31
|
+
import { aiStorefrontSays } from "../layout-language/ai-layout-storefront.js";
|
|
29
32
|
import { AI_INSTANCE_REF_PROP, validateAiNodeTree } from "./ai-node-tree.js";
|
|
30
33
|
import { AI_PAGE_SCROLL_TO_PROP, aiPageLinkTarget, aiStoredScrollTargets } from "./ai-page-links.js";
|
|
31
34
|
import { AI_EMAIL_CLIP_BYTES, AI_OUTPUT_BUDGETS, AI_OUTPUT_SURFACE, AI_TEXT_LIMITS, isHeadlineVariant } from "./ai-palette.js";
|
|
@@ -525,7 +528,9 @@ export const AI_LAYOUT_REGION_NAMES = Object.keys(AI_LAYOUT_REGIONS);
|
|
|
525
528
|
/**
|
|
526
529
|
* Rule 3. A form on a page is a Form bound by id to a form built on the
|
|
527
530
|
* Forms page — fields, validation, consent and routing kept in one place.
|
|
528
|
-
* Loose fields
|
|
531
|
+
* Loose fields — a Form Field, or any of MUI's answer elements (an Input,
|
|
532
|
+
* Select, Radio Group, Checkbox or Rating) — an unbound Form, and fields
|
|
533
|
+
* drawn inside a bound Form (which
|
|
529
534
|
* the page never renders: the placed form's own design replaces them) are
|
|
530
535
|
* refused. A form's own design is the one place fields are drawn.
|
|
531
536
|
*
|
|
@@ -541,7 +546,7 @@ export const AI_LAYOUT_REGION_NAMES = Object.keys(AI_LAYOUT_REGIONS);
|
|
|
541
546
|
const unbound = [];
|
|
542
547
|
const shadowed = [];
|
|
543
548
|
for (const visit of walkTree(tree)){
|
|
544
|
-
if (visit.node
|
|
549
|
+
if (isFormInputNode(visit.node) && !hasAncestor(tree, visit, (node)=>node.componentId === 'form')) {
|
|
545
550
|
loose.push(visit.id);
|
|
546
551
|
}
|
|
547
552
|
if (visit.node.componentId === 'form') {
|
|
@@ -1841,6 +1846,7 @@ const GRID = 'muiGrid';
|
|
|
1841
1846
|
];
|
|
1842
1847
|
}
|
|
1843
1848
|
/** A link's words that say it goes home. */ const HOME_WORDS = /\bhome(?:\s?page)?\b/i;
|
|
1849
|
+
/** A label's words, lowercase, for comparing one label with another. */ const labelWords = (label)=>label.toLowerCase().split(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "u")).filter(Boolean).join(' ');
|
|
1844
1850
|
/** Words that ask a visitor to get in touch, which the site's form answers. */ const GET_IN_TOUCH_WORDS = /\b(?:contact|book|booking|call|quote|estimate|appointment|schedule|reserve|reservation|get in touch|request|visit|enquire|inquire|enquiry|inquiry|message|talk|consult|consultation|order|pre-?order|sign up|join|apply|start)\b/i;
|
|
1845
1851
|
/**
|
|
1846
1852
|
* Whether a link's words ask a visitor to get in touch — contact, book, quote,
|
|
@@ -1854,10 +1860,11 @@ const GRID = 'muiGrid';
|
|
|
1854
1860
|
* in document order. A link whose words ask a visitor to get in touch, sent to
|
|
1855
1861
|
* a home page that places the site's form, does what it says (AGL-3729).
|
|
1856
1862
|
*/ function unrelatedScreenLinks(tree, outputKind, context) {
|
|
1857
|
-
var _context_homeScreenIds, _context_formScreenIds;
|
|
1863
|
+
var _context_homeScreenIds, _context_formScreenIds, _context_homeLabels;
|
|
1858
1864
|
if (outputKind === 'email' || outputKind === 'form') return [];
|
|
1859
1865
|
const home = new Set((_context_homeScreenIds = context.homeScreenIds) != null ? _context_homeScreenIds : []);
|
|
1860
1866
|
const forms = new Set((_context_formScreenIds = context.formScreenIds) != null ? _context_formScreenIds : []);
|
|
1867
|
+
const homeLabels = new Set(((_context_homeLabels = context.homeLabels) != null ? _context_homeLabels : []).map(labelWords).filter(Boolean));
|
|
1861
1868
|
const sent = [];
|
|
1862
1869
|
for (const visit of walkTree(tree)){
|
|
1863
1870
|
var _node_props, _node_props1, _node_props2;
|
|
@@ -1866,7 +1873,7 @@ const GRID = 'muiGrid';
|
|
|
1866
1873
|
const screenId = (_node_props = node.props) == null ? void 0 : _node_props['screenId'];
|
|
1867
1874
|
const linksHome = typeof screenId === 'string' && home.has(screenId) || ((_node_props1 = node.props) == null ? void 0 : _node_props1['href']) === '/';
|
|
1868
1875
|
const label = typeof ((_node_props2 = node.props) == null ? void 0 : _node_props2['children']) === 'string' ? node.props['children'].trim() : '';
|
|
1869
|
-
if (!linksHome || !label || HOME_WORDS.test(label)) continue;
|
|
1876
|
+
if (!linksHome || !label || HOME_WORDS.test(label) || homeLabels.has(labelWords(label))) continue;
|
|
1870
1877
|
// Home holds the site's form, and the words ask for what it does (AGL-3729).
|
|
1871
1878
|
if (typeof screenId === 'string' && forms.has(screenId) && aiAsksToGetInTouch(label)) continue;
|
|
1872
1879
|
// The header and the navigation link home by name and by the site's own brand.
|
|
@@ -3286,7 +3293,10 @@ const BUDGET_METRICS = [
|
|
|
3286
3293
|
};
|
|
3287
3294
|
const violations = [
|
|
3288
3295
|
...publish,
|
|
3289
|
-
|
|
3296
|
+
// A form's fields share a shape because a field IS one shape — a Form
|
|
3297
|
+
// Control over a Form Label and an answer — and a form places no
|
|
3298
|
+
// instances, so rule 1 has nothing to ask of one.
|
|
3299
|
+
...outputKind === 'form' ? [] : detectRepeatedSubtrees(tree, otherPages, context),
|
|
3290
3300
|
...detectLayoutRegions(tree, outputKind),
|
|
3291
3301
|
...detectInlineForms(tree, outputKind),
|
|
3292
3302
|
...detectLiteralStyles(tree, outputKind),
|
|
@@ -3769,6 +3779,76 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
3769
3779
|
}))
|
|
3770
3780
|
});
|
|
3771
3781
|
}
|
|
3782
|
+
/**
|
|
3783
|
+
* A page section that is a layout region — a header, navigation or footer,
|
|
3784
|
+
* read by the name rule 2's `plan-layout-region-section` reads — is taken off
|
|
3785
|
+
* the page before the rules read the plan (AGL-3750). The region is already
|
|
3786
|
+
* the layout's, so the rule's own answer, "take the section off the page", is
|
|
3787
|
+
* the only one: a live Free start of 2026-10-12 (a bakery that sells cakes to
|
|
3788
|
+
* order) planned a Header and a Footer on its pages three times and stopped.
|
|
3789
|
+
*
|
|
3790
|
+
* Only a section that places nothing, or nothing but a layout (which rule 2
|
|
3791
|
+
* refuses placing as well), is taken off: one that places a form or a
|
|
3792
|
+
* component may be a band misnamed, so the rule still asks. A page whose
|
|
3793
|
+
* every section is a region keeps them all, and is re-asked as before, since
|
|
3794
|
+
* a page of no sections has no answer here. A home this leaves under its
|
|
3795
|
+
* fewest sections, or a Free plan still past its wall, is the existing
|
|
3796
|
+
* checks' to settle or refuse.
|
|
3797
|
+
*/ export function aiSettlePlanLayoutRegionSections(plan, inventory) {
|
|
3798
|
+
const kinds = inventoryKinds(inventory);
|
|
3799
|
+
const removable = (section)=>LAYOUT_REGION_NAME.test(section.name) && section.uses.every((ref)=>refKind(ref, plan, kinds) === 'layout');
|
|
3800
|
+
let changed = false;
|
|
3801
|
+
const screens = plan.screens.map((screen)=>{
|
|
3802
|
+
const kept = screen.sections.filter((section)=>!removable(section));
|
|
3803
|
+
if (kept.length === screen.sections.length || !kept.length) return screen;
|
|
3804
|
+
changed = true;
|
|
3805
|
+
return _extends({}, screen, {
|
|
3806
|
+
sections: kept
|
|
3807
|
+
});
|
|
3808
|
+
});
|
|
3809
|
+
return changed ? _extends({}, plan, {
|
|
3810
|
+
screens
|
|
3811
|
+
}) : plan;
|
|
3812
|
+
}
|
|
3813
|
+
/**
|
|
3814
|
+
* A section that shows the work — a gallery, a portfolio, selected works, by
|
|
3815
|
+
* the name `aiSiteSectionShowsWork` reads — planned with fewer pieces than
|
|
3816
|
+
* the job's `galleryMinItems` is raised to it before the rules read the plan
|
|
3817
|
+
* (AGL-3754). The site start's `plan-empty-gallery` refusal names one answer,
|
|
3818
|
+
* "plan it with three to six items", and the fewest is the one that keeps
|
|
3819
|
+
* the plan's own count closest: a section's items move no section cap, Free
|
|
3820
|
+
* wall or credit figure, which count sections and pages.
|
|
3821
|
+
*
|
|
3822
|
+
* A section the rule reads as one item of a list split into sections is
|
|
3823
|
+
* left for rule 1's re-ask to join, as the Free wall's trim leaves it: raised
|
|
3824
|
+
* here, it would hide the split. A job that sets no `galleryMinItems` (every
|
|
3825
|
+
* job but a site start) is returned unchanged.
|
|
3826
|
+
*/ export function aiSettlePlanGalleries(plan, inventory, capabilities = null) {
|
|
3827
|
+
var _ref;
|
|
3828
|
+
const least = (_ref = capabilities == null ? void 0 : capabilities.galleryMinItems) != null ? _ref : 0;
|
|
3829
|
+
const thin = (section)=>aiSiteSectionShowsWork(section.name) && section.items < least;
|
|
3830
|
+
if (!plan.screens.some((screen)=>screen.sections.some(thin))) return plan;
|
|
3831
|
+
const split = new Set(detectPlanSplitLists(plan, inventory, capabilities).flatMap((violation)=>{
|
|
3832
|
+
var _violation_paths;
|
|
3833
|
+
return (_violation_paths = violation.paths) != null ? _violation_paths : [];
|
|
3834
|
+
}));
|
|
3835
|
+
let changed = false;
|
|
3836
|
+
const screens = plan.screens.map((screen, screenIndex)=>{
|
|
3837
|
+
if (!screen.sections.some(thin)) return screen;
|
|
3838
|
+
return _extends({}, screen, {
|
|
3839
|
+
sections: screen.sections.map((section, sectionIndex)=>{
|
|
3840
|
+
if (!thin(section) || split.has(`screens[${screenIndex}].sections[${sectionIndex}]`)) return section;
|
|
3841
|
+
changed = true;
|
|
3842
|
+
return _extends({}, section, {
|
|
3843
|
+
items: least
|
|
3844
|
+
});
|
|
3845
|
+
})
|
|
3846
|
+
});
|
|
3847
|
+
});
|
|
3848
|
+
return changed ? _extends({}, plan, {
|
|
3849
|
+
screens
|
|
3850
|
+
}) : plan;
|
|
3851
|
+
}
|
|
3772
3852
|
/** Every saved form a plan could place, the plan's own creations first. */ function planFormChoices(plan, inventory) {
|
|
3773
3853
|
var _ref;
|
|
3774
3854
|
return [
|
|
@@ -4419,6 +4499,135 @@ function slugKey(slug) {
|
|
|
4419
4499
|
const needs = !((_ref = inventory == null ? void 0 : inventory.layouts.length) != null ? _ref : 0) && capabilities.create.layout.allowed ? 1 : 0;
|
|
4420
4500
|
return Math.max(creates, needs);
|
|
4421
4501
|
}
|
|
4502
|
+
/** The Free wall a plan is held to, or null where the job is not on the Free taste. */ function freePlanWall(plan, inventory, capabilities) {
|
|
4503
|
+
if (!(capabilities == null ? void 0 : capabilities.freeTaste)) return null;
|
|
4504
|
+
const asked = plan.screens.reduce((sum, screen)=>sum + screen.sections.length, 0);
|
|
4505
|
+
const layouts = freePlanLayouts(plan, inventory, capabilities);
|
|
4506
|
+
const pages = Math.max(plan.screens.length, 1);
|
|
4507
|
+
// A Free site scaffold (AGL-3594) is held to its page cap first, and then to
|
|
4508
|
+
// what a SITE plan's worst case leaves for sections, with room for a retry.
|
|
4509
|
+
const sitePages = capabilities.freeSitePages;
|
|
4510
|
+
const overPages = sitePages !== undefined && plan.screens.length > sitePages;
|
|
4511
|
+
// A site start's saved form is a step of its own (AGL-3596), counted on the wall since AGL-3660.
|
|
4512
|
+
const forms = plan.create.filter((entry)=>entry.kind === 'form').length - aiPlanUncreatable(plan, capabilities).filter((entry)=>entry.kind === 'form').length;
|
|
4513
|
+
const fits = sitePages !== undefined ? aiFreeSiteSectionsWithin({
|
|
4514
|
+
layouts,
|
|
4515
|
+
pages,
|
|
4516
|
+
forms: Math.max(0, forms)
|
|
4517
|
+
}, FREE_AI_TASTE_CREDITS_PER_MONTH) : aiFreePageSectionsWithin({
|
|
4518
|
+
layouts,
|
|
4519
|
+
pages
|
|
4520
|
+
});
|
|
4521
|
+
// A site start's home keeps its full five (AGL-3660): told only "plan at
|
|
4522
|
+
// most 8", the live yoga start of 2026-10-08 cut a home of 6 to 4 and kept
|
|
4523
|
+
// its other page, so the redo names where the sections come from.
|
|
4524
|
+
const homeMin = sitePages !== undefined && plan.screens.some(aiSitePlanIsHome) ? aiSiteHomeMinSections({
|
|
4525
|
+
pages,
|
|
4526
|
+
across: fits
|
|
4527
|
+
}) : 0;
|
|
4528
|
+
return {
|
|
4529
|
+
asked,
|
|
4530
|
+
fits,
|
|
4531
|
+
layouts,
|
|
4532
|
+
pages,
|
|
4533
|
+
sitePages,
|
|
4534
|
+
overPages,
|
|
4535
|
+
homeMin
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
/**
|
|
4539
|
+
* The most sections past the Free wall a plan is trimmed by rather than
|
|
4540
|
+
* re-asked (AGL-3737). The live candle-shop start of 2026-10-12 planned 9
|
|
4541
|
+
* sections against a wall of 8 on all three answers, each with a home of 7:
|
|
4542
|
+
* the model reaching for the budget the site redo names ("plan 7 to 8") and
|
|
4543
|
+
* landing one band past it, which no re-ask cured. Within two, the plan's
|
|
4544
|
+
* shape is right and only a band or two the brief needs least is extra, so
|
|
4545
|
+
* taking those from the end loses nothing the plan depends on. Further over,
|
|
4546
|
+
* the shape itself is off — a list planned as sections, a page the wall
|
|
4547
|
+
* cannot pay for — and cutting more would drop content the model meant, so
|
|
4548
|
+
* that plan is still re-asked.
|
|
4549
|
+
*/ export const AI_FREE_WALL_SETTLE_MAX_OVER = 2;
|
|
4550
|
+
/** A section through which a visitor reaches the owner: contact, a visit, a booking. */ const REACH_SECTION_NAME = /\b(?:contact|get in touch|visit(?: us)?|find us|locations?|hours|book(?:ing)?|reserve|reservations?)\b/i;
|
|
4551
|
+
/**
|
|
4552
|
+
* Whether the Free wall's trim keeps a section of a page (AGL-3737): the
|
|
4553
|
+
* page's opening, any section that places something (a dataset it lists, a
|
|
4554
|
+
* form, a component — dropping it would leave a creation unplaced), one that
|
|
4555
|
+
* collects answers or says how to reach the owner, and a storefront's
|
|
4556
|
+
* listing parts — the bestsellers, the collections, the sign-up, and a Shop
|
|
4557
|
+
* page's product grid.
|
|
4558
|
+
*/ function freeWallKeeps(screen, index) {
|
|
4559
|
+
const section = screen.sections[index];
|
|
4560
|
+
if (index === 0 || section.uses.length > 0) return true;
|
|
4561
|
+
if (FORM_SECTION_NAME.test(section.name) || REACH_SECTION_NAME.test(section.name)) return true;
|
|
4562
|
+
if (aiStorefrontSays('featured', section.name) || aiStorefrontSays('collections', section.name) || aiStorefrontSays('signup', section.name)) {
|
|
4563
|
+
return true;
|
|
4564
|
+
}
|
|
4565
|
+
return aiLayoutIsShopPage(screen) && aiLayoutShopGridSection(screen.sections) === index;
|
|
4566
|
+
}
|
|
4567
|
+
/**
|
|
4568
|
+
* A Free plan a section or two past the wall is trimmed to fit before the
|
|
4569
|
+
* rules read the plan (AGL-3737), rather than re-asked: which sections go has
|
|
4570
|
+
* one answer, so the re-ask only costs the member a stopped start. The live
|
|
4571
|
+
* candle-shop start of 2026-10-12 asked for a home of 7 and a second page of
|
|
4572
|
+
* 2 against a wall of 8, three answers running, and stopped needing input.
|
|
4573
|
+
*
|
|
4574
|
+
* One section goes at a time, the last of its page that `freeWallKeeps`
|
|
4575
|
+
* does not hold, from the page furthest past its share: the home at / past
|
|
4576
|
+
* its fewest (`aiSiteHomeMinSections`, five on a site start whose wall pays
|
|
4577
|
+
* for a full home), any other page past two; on a tie, the longer page, then
|
|
4578
|
+
* the home. No home goes under its fewest and no page under one section. The
|
|
4579
|
+
* shares are what a site start is told to plan (a home of five or six, the
|
|
4580
|
+
* other page two or three): the candle shop's 7 + 2 becomes 6 + 2, never
|
|
4581
|
+
* 7 + 1, and the recorded yoga answer's 6 + 4 becomes 5 + 3, the shape its
|
|
4582
|
+
* own redo chose.
|
|
4583
|
+
*
|
|
4584
|
+
* A plan more than `AI_FREE_WALL_SETTLE_MAX_OVER` past the wall, a site
|
|
4585
|
+
* start past its page cap, a plan with a list split into sections, and a
|
|
4586
|
+
* plan the trim cannot fit without a section it keeps are returned
|
|
4587
|
+
* unchanged, for the wall to re-ask.
|
|
4588
|
+
*/ export function aiSettlePlanFreeWall(plan, inventory, capabilities = null) {
|
|
4589
|
+
const wall = freePlanWall(plan, inventory, capabilities);
|
|
4590
|
+
if (!wall || wall.overPages) return plan;
|
|
4591
|
+
const over = wall.asked - wall.fits;
|
|
4592
|
+
if (over <= 0 || over > AI_FREE_WALL_SETTLE_MAX_OVER) return plan;
|
|
4593
|
+
// A list split into sections is joined by its re-ask, which frees the room;
|
|
4594
|
+
// a trim would cut one of its items and hide the split from the rule.
|
|
4595
|
+
if (detectPlanSplitLists(plan, inventory, capabilities).length) return plan;
|
|
4596
|
+
const kept = plan.screens.map((screen)=>screen.sections.map((section, index)=>({
|
|
4597
|
+
section,
|
|
4598
|
+
index
|
|
4599
|
+
})));
|
|
4600
|
+
const home = plan.screens.findIndex(aiSitePlanIsHome);
|
|
4601
|
+
const floor = (screenIndex)=>screenIndex === home ? Math.max(wall.homeMin, 1) : 1;
|
|
4602
|
+
const target = (screenIndex)=>screenIndex === home ? Math.max(wall.homeMin, 1) : 2;
|
|
4603
|
+
// The last section of a page the trim may take, or -1.
|
|
4604
|
+
const last = (screenIndex)=>{
|
|
4605
|
+
const entries = kept[screenIndex];
|
|
4606
|
+
if (entries.length <= floor(screenIndex)) return -1;
|
|
4607
|
+
for(let at = entries.length - 1; at > 0; at -= 1){
|
|
4608
|
+
if (!freeWallKeeps(plan.screens[screenIndex], entries[at].index)) return at;
|
|
4609
|
+
}
|
|
4610
|
+
return -1;
|
|
4611
|
+
};
|
|
4612
|
+
for(let left = over; left > 0; left -= 1){
|
|
4613
|
+
let pick = -1;
|
|
4614
|
+
for(let index = 0; index < kept.length; index += 1){
|
|
4615
|
+
if (last(index) === -1) continue;
|
|
4616
|
+
const room = kept[index].length - target(index);
|
|
4617
|
+
const best = pick === -1 ? null : kept[pick].length - target(pick);
|
|
4618
|
+
if (best === null || room > best || room === best && (kept[index].length > kept[pick].length || kept[index].length === kept[pick].length && index === home)) {
|
|
4619
|
+
pick = index;
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
if (pick === -1) return plan;
|
|
4623
|
+
kept[pick].splice(last(pick), 1);
|
|
4624
|
+
}
|
|
4625
|
+
return _extends({}, plan, {
|
|
4626
|
+
screens: plan.screens.map((screen, index)=>kept[index].length === screen.sections.length ? screen : _extends({}, screen, {
|
|
4627
|
+
sections: kept[index].map((entry)=>entry.section)
|
|
4628
|
+
}))
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
4422
4631
|
/**
|
|
4423
4632
|
* The Free wall (plan): a plan asks for no more sections than the Free taste
|
|
4424
4633
|
* pays for at its worst beside what the job builds first (AGL-3070). A live
|
|
@@ -4429,14 +4638,10 @@ function slugKey(slug) {
|
|
|
4429
4638
|
* proven with, so no plan the proof admits is refused. It is no building rule,
|
|
4430
4639
|
* so it names none.
|
|
4431
4640
|
*/ export function detectPlanOverFreeWall(plan, inventory, capabilities) {
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
const layouts
|
|
4435
|
-
|
|
4436
|
-
// A Free site scaffold (AGL-3594) is held to its page cap first, and then to
|
|
4437
|
-
// what a SITE plan's worst case leaves for sections, with room for a retry.
|
|
4438
|
-
const sitePages = capabilities.freeSitePages;
|
|
4439
|
-
if (sitePages !== undefined && plan.screens.length > sitePages) {
|
|
4641
|
+
const wall = freePlanWall(plan, inventory, capabilities);
|
|
4642
|
+
if (!wall) return [];
|
|
4643
|
+
const { asked, layouts, pages, sitePages, fits, homeMin } = wall;
|
|
4644
|
+
if (sitePages !== undefined && wall.overPages) {
|
|
4440
4645
|
return [
|
|
4441
4646
|
{
|
|
4442
4647
|
rule: null,
|
|
@@ -4448,26 +4653,9 @@ function slugKey(slug) {
|
|
|
4448
4653
|
}
|
|
4449
4654
|
];
|
|
4450
4655
|
}
|
|
4451
|
-
// A site start's saved form is a step of its own (AGL-3596), counted on the wall since AGL-3660.
|
|
4452
|
-
const forms = plan.create.filter((entry)=>entry.kind === 'form').length - aiPlanUncreatable(plan, capabilities).filter((entry)=>entry.kind === 'form').length;
|
|
4453
|
-
const fits = sitePages !== undefined ? aiFreeSiteSectionsWithin({
|
|
4454
|
-
layouts,
|
|
4455
|
-
pages,
|
|
4456
|
-
forms: Math.max(0, forms)
|
|
4457
|
-
}, FREE_AI_TASTE_CREDITS_PER_MONTH) : aiFreePageSectionsWithin({
|
|
4458
|
-
layouts,
|
|
4459
|
-
pages
|
|
4460
|
-
});
|
|
4461
4656
|
if (asked <= fits) return [];
|
|
4462
4657
|
const job = pages > 1 ? `a Free plan of ${pages} pages` : 'a Free page';
|
|
4463
4658
|
const beside = layouts ? ` that creates ${layouts > 1 ? `${layouts} layouts` : 'its layout'}` : '';
|
|
4464
|
-
// A site start's home keeps its full five (AGL-3660): told only "plan at
|
|
4465
|
-
// most 8", the live yoga start of 2026-10-08 cut a home of 6 to 4 and kept
|
|
4466
|
-
// its other page, so the redo names where the sections come from.
|
|
4467
|
-
const homeMin = sitePages !== undefined && plan.screens.some(aiSitePlanIsHome) ? aiSiteHomeMinSections({
|
|
4468
|
-
pages,
|
|
4469
|
-
across: fits
|
|
4470
|
-
}) : 0;
|
|
4471
4659
|
const keepHome = homeMin ? ` Keep the home page at / at ${homeMin} or more sections, and take the rest from the other ${pages > 2 ? 'pages' : 'page'}.` : '';
|
|
4472
4660
|
// A site start's redo keeps the plan full (AGL-3660): the wall's sections
|
|
4473
4661
|
// are its budget to use, and a redo told only "at most" cuts far under it.
|