@aglyn/plugins-ai 1.0.0-beta.246 → 1.0.0-beta.249
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 +5 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-links.js +14 -0
- package/src/lib/components/ai-job-links.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 +92 -19
- package/src/lib/components/ai-site-start-card.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 +56 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +3 -8
- package/src/lib/jobs/ai-job-build-step.js +49 -12
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- 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 +16 -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-page-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-page-language.js +9 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +4 -3
- 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 +10 -1
- package/src/lib/jobs/ai-job-plan-step.js +73 -15
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +0 -4
- package/src/lib/jobs/ai-job-settings-step.js +2 -7
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +2 -2
- package/src/lib/jobs/ai-job-site-content.js +5 -3
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +1 -1
- package/src/lib/jobs/ai-job-site-datasets.js +3 -2
- 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 +9 -3
- package/src/lib/jobs/ai-job-site-step.js +63 -17
- 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 +41 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +71 -3
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.js +2 -3
- 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/jobs/fixtures/ai-stock-photo-agl3769-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js +126 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -4
- package/src/lib/layout-language/ai-layout-compiler.js +38 -19
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +7 -5
- package/src/lib/layout-language/ai-layout-frame.js +12 -9
- 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 +14 -1
- package/src/lib/layout-language/ai-layout-listings.js +32 -5
- package/src/lib/layout-language/ai-layout-listings.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 +21 -0
- package/src/lib/model/ai-build-job.js +39 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +25 -14
- package/src/lib/model/ai-build-plan.js +33 -23
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +3 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +10 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- 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-site-inventory.d.ts +2 -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 +2 -2
- package/src/lib/model/ai-site-job.js +3 -3
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -3
- package/src/lib/runtime/ai-doctrine-validators.js +53 -6
- 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 +13 -9
- 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 +498 -10
- 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-template-examples.js +8 -2
- package/src/lib/runtime/ai-template-examples.js.map +1 -1
- package/src/lib/server.js +8 -0
- package/src/lib/server.js.map +1 -1
|
@@ -66,13 +66,15 @@ export interface AiLayoutFramePlan {
|
|
|
66
66
|
* linked by their addresses, as the blog is.
|
|
67
67
|
*/
|
|
68
68
|
storeLinks?: AiStoreFrameLinks | null;
|
|
69
|
+
/** The site's language (AGL-3760): the frame's own words are in it where `ai-layout-labels.ts` lists it. */
|
|
70
|
+
language?: string;
|
|
69
71
|
}
|
|
70
|
-
/** What the header's link to a store's account page says (AGL-3676). */
|
|
71
|
-
export declare const AI_LAYOUT_ACCOUNT_LINK
|
|
72
|
+
/** What the header's link to a store's account page says (AGL-3676), in English. */
|
|
73
|
+
export declare const AI_LAYOUT_ACCOUNT_LINK: "Account";
|
|
72
74
|
/** What a store's announcement bar says where its design gave it no line (AGL-3676): true of every store. */
|
|
73
|
-
export declare const AI_LAYOUT_STORE_ANNOUNCEMENT
|
|
75
|
+
export declare const AI_LAYOUT_STORE_ANNOUNCEMENT: "Shop the full collection";
|
|
74
76
|
/** The way into the shop beside the design's own announcement (AGL-3676). */
|
|
75
|
-
export declare const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK
|
|
77
|
+
export declare const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK: "Shop now";
|
|
76
78
|
export interface AiLayoutCompiledFrame {
|
|
77
79
|
tree: AiLayoutRawTree;
|
|
78
80
|
settled: AiLayoutSettlement[];
|
|
@@ -85,7 +87,7 @@ export declare function aiCompileLayoutFrame(frame: {
|
|
|
85
87
|
footer: AiLayoutSection | null;
|
|
86
88
|
}, plan: AiLayoutFramePlan, targets: AiLayoutTargets): AiLayoutCompiledFrame;
|
|
87
89
|
/** What a store's own column of account and policy links is headed where the footer design gave no store heading (AGL-3735). */
|
|
88
|
-
export declare const AI_LAYOUT_STORE_FOOTER_HEADING
|
|
90
|
+
export declare const AI_LAYOUT_STORE_FOOTER_HEADING: "Customer care";
|
|
89
91
|
/**
|
|
90
92
|
* The footer heading a store's account and policy links go under
|
|
91
93
|
* (AGL-3735): a heading of the design's that names the store or its help
|
|
@@ -20,11 +20,12 @@ import { aiLayoutFitText } from "./ai-layout-copy.js";
|
|
|
20
20
|
import { aiLayoutRepairFrameHollows } from "./ai-layout-frame-hollows.js";
|
|
21
21
|
import { AI_LAYOUT_CART_ELEMENT } from "./ai-layout-listings.js";
|
|
22
22
|
import { AI_LAYOUT_MAX_ITEMS } from "./ai-layout-language.js";
|
|
23
|
+
import { AI_LAYOUT_LABELS_EN, aiLayoutLabel } from "./ai-layout-labels.js";
|
|
23
24
|
import { aiLayoutRenamedLabel, aiLayoutResolveLink } from "./ai-layout-links.js";
|
|
24
25
|
import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
25
|
-
/** What the header's link to a store's account page says (AGL-3676). */ export const AI_LAYOUT_ACCOUNT_LINK =
|
|
26
|
-
/** What a store's announcement bar says where its design gave it no line (AGL-3676): true of every store. */ export const AI_LAYOUT_STORE_ANNOUNCEMENT =
|
|
27
|
-
/** The way into the shop beside the design's own announcement (AGL-3676). */ export const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK =
|
|
26
|
+
/** What the header's link to a store's account page says (AGL-3676), in English. */ export const AI_LAYOUT_ACCOUNT_LINK = AI_LAYOUT_LABELS_EN.account;
|
|
27
|
+
/** What a store's announcement bar says where its design gave it no line (AGL-3676): true of every store. */ export const AI_LAYOUT_STORE_ANNOUNCEMENT = AI_LAYOUT_LABELS_EN.storeAnnouncement;
|
|
28
|
+
/** The way into the shop beside the design's own announcement (AGL-3676). */ export const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK = AI_LAYOUT_LABELS_EN.shopNow;
|
|
28
29
|
/** The header's colors by band, as palette roles. */ function headerLook(band) {
|
|
29
30
|
if (band === 'brand') return {
|
|
30
31
|
props: {
|
|
@@ -113,9 +114,11 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
|
113
114
|
pageId: null
|
|
114
115
|
}),
|
|
115
116
|
// A footer is drawn compact: its lines are lines, never blocks rule 1 would ask to share.
|
|
116
|
-
options: {
|
|
117
|
+
options: _extends({
|
|
117
118
|
reusableComponents: true
|
|
118
|
-
},
|
|
119
|
+
}, plan.language ? {
|
|
120
|
+
language: plan.language
|
|
121
|
+
} : {}),
|
|
119
122
|
settled: [],
|
|
120
123
|
scrollTo: {},
|
|
121
124
|
// A frame's lines are its own, never a page section's items.
|
|
@@ -241,7 +244,7 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
|
241
244
|
const accountPath = (_ref1 = (_plan_storeLinks1 = plan.storeLinks) == null ? void 0 : _plan_storeLinks1.account) != null ? _ref1 : null;
|
|
242
245
|
const accountLink = (role, interactions, sx)=>{
|
|
243
246
|
const id = tree.add('muiScreenLink', {
|
|
244
|
-
children:
|
|
247
|
+
children: aiLayoutLabel('account', plan.language),
|
|
245
248
|
href: accountPath,
|
|
246
249
|
renderAs: 'link',
|
|
247
250
|
color: 'inherit'
|
|
@@ -353,7 +356,7 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
|
353
356
|
});
|
|
354
357
|
// The design's line, then the way into the shop; else the way in alone.
|
|
355
358
|
const link = tree.add('muiScreenLink', {
|
|
356
|
-
children: said ?
|
|
359
|
+
children: aiLayoutLabel(said ? 'shopNow' : 'storeAnnouncement', plan.language),
|
|
357
360
|
screenId: plan.shopId,
|
|
358
361
|
renderAs: 'link',
|
|
359
362
|
color: 'inherit'
|
|
@@ -421,7 +424,7 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
|
421
424
|
settled: page.settled
|
|
422
425
|
};
|
|
423
426
|
}
|
|
424
|
-
/** What a store's own column of account and policy links is headed where the footer design gave no store heading (AGL-3735). */ export const AI_LAYOUT_STORE_FOOTER_HEADING =
|
|
427
|
+
/** What a store's own column of account and policy links is headed where the footer design gave no store heading (AGL-3735). */ export const AI_LAYOUT_STORE_FOOTER_HEADING = AI_LAYOUT_LABELS_EN.customerCare;
|
|
425
428
|
/** A footer heading a store's account and policies belong under (AGL-3735). */ const STORE_HEADING = /\b(?:shop|store|shopping|help|customer|care|support|service|orders?|account|policies|policy|legal|info|information|shipping|returns)\b/i;
|
|
426
429
|
/**
|
|
427
430
|
* The footer heading a store's account and policy links go under
|
|
@@ -599,7 +602,7 @@ import { AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
|
599
602
|
columns.push([
|
|
600
603
|
{
|
|
601
604
|
kind: 'heading',
|
|
602
|
-
text:
|
|
605
|
+
text: aiLayoutLabel('customerCare', plan.language)
|
|
603
606
|
},
|
|
604
607
|
list
|
|
605
608
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-frame.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiStoreFrameLinks } from '../model/ai-site-store-pages'\nimport { AI_ICON_LIBRARY } from '../runtime/ai-icon-library'\nimport {\n AI_LAYOUT_MAX_IMAGES,\n bandSx,\n compileFlow,\n compileRow,\n destinationProps,\n type PageScope,\n type SectionScope,\n} from './ai-layout-compiler'\nimport { aiLayoutFitText } from './ai-layout-copy'\nimport { aiLayoutRepairFrameHollows } from './ai-layout-frame-hollows'\nimport { AI_LAYOUT_CART_ELEMENT } from './ai-layout-listings'\nimport {\n AI_LAYOUT_MAX_ITEMS,\n type AiLayoutBand,\n type AiLayoutBlock,\n type AiLayoutSection,\n type AiLayoutSettlement,\n} from './ai-layout-language'\nimport {\n aiLayoutRenamedLabel,\n aiLayoutResolveLink,\n type AiLayoutPage,\n type AiLayoutTargets,\n} from './ai-layout-links'\nimport { AiLayoutTreeBuilder, type AiLayoutRawTree } from './ai-layout-tree'\n\n/**\n * A site's frame — the header and the footer every page renders between —\n * compiled from the layout language (AGL-3660), the same language a page is\n * designed in.\n *\n * The design is the model's: the header's band and alignment and its one\n * call to action, and the footer's columns and every word in them. What a\n * frame owes the site is the platform's, written here, so it is never wrong\n * and never invented: the site's name as the brand, linking home; its pages\n * as the navigation, a row on a wide screen and the platform's Drawer behind\n * its Menu Button on a phone; the Layout Slot, the page's one main landmark,\n * held tall enough that the footer sits at the bottom of a short page; and\n * the site's pages and name in the footer, where the design gave neither.\n */\n\nexport interface AiLayoutFramePlan {\n /** The site's name, which the header and the footer carry as its brand. */\n siteName: string\n /** The site's home page, which the brand links; none where the site has none yet. */\n homeId: string | null\n /** The pages the navigation links, in order. */\n navPages: readonly AiLayoutPage[]\n /**\n * Whether the site's pages close on a dark photo band (AGL-3660,\n * `AiLayoutDesignChoices.coverClose`): a brand or dark footer under it\n * would read as a second loud band, so the footer takes the quiet one.\n */\n closesDark?: boolean\n /**\n * Whether the site sells (AGL-3676): its header carries the store's cart\n * button — the commerce plugin's Cart, which opens the cart in a drawer\n * from every page — beside the navigation.\n */\n cart?: boolean\n /**\n * A selling store's Shop page (AGL-3676), by its id. Its header is a\n * storefront's: the cart beside the navigation and no call-to-action button\n * repeating a link the navigation already has, and a thin announcement bar\n * above it — the line the design gave its header as a note, else one that\n * opens the shop — linking the Shop page.\n */\n shopId?: string | null\n /**\n * A store's own pages (AGL-3676), by path: the header links the account\n * beside the cart, and the footer the account and the store's policies\n * under the site's pages. They are written after the layout, so they are\n * linked by their addresses, as the blog is.\n */\n storeLinks?: AiStoreFrameLinks | null\n}\n\n/** What the header's link to a store's account page says (AGL-3676). */\nexport const AI_LAYOUT_ACCOUNT_LINK = 'Account'\n\n/** What a store's announcement bar says where its design gave it no line (AGL-3676): true of every store. */\nexport const AI_LAYOUT_STORE_ANNOUNCEMENT = 'Shop the full collection'\n\n/** The way into the shop beside the design's own announcement (AGL-3676). */\nexport const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK = 'Shop now'\n\nexport interface AiLayoutCompiledFrame {\n tree: AiLayoutRawTree\n settled: AiLayoutSettlement[]\n}\n\n/** The header's colors by band, as palette roles. */\nfunction headerLook(band: AiLayoutBand): {\n props: Record<string, unknown>\n sx: Record<string, unknown>\n} {\n if (band === 'brand')\n return { props: { color: 'primary' }, sx: { boxShadow: 'none' } }\n if (band === 'dark') {\n return {\n props: { color: 'primary' },\n sx: {\n boxShadow: 'none',\n bgcolor: 'primary.dark',\n color: 'primary.contrastText',\n },\n }\n }\n // A rule, a shadow or nothing under a plain header is the site theme's\n // MuiAppBar style (AGL-3660); the band's color stays here.\n return {\n props: { color: 'inherit' },\n sx: { bgcolor: 'background.paper' },\n }\n}\n\n/** A line that reads as a copyright notice: \"© 2026 …\", \"Copyright …\", \"All rights reserved.\" */\nexport function aiLayoutIsCopyright(text: unknown): boolean {\n return typeof text === 'string' && /^\\s*(?:©|\\(c\\)|copyright\\b)|all rights reserved/i.test(text)\n}\n\n/** What a footer leaves out: a picture slot, and the page sections a footer is no place for. */\nconst FOOTER_LEAVES_OUT: ReadonlySet<AiLayoutBlock['kind']> = new Set([\n 'image',\n 'faq',\n 'quotes',\n 'cards',\n 'steps',\n 'stats',\n 'form',\n 'component',\n])\n\n/** A frame scope: no headings of its own (a layout carries no outline), no pictures. */\nfunction frameScope(\n page: PageScope,\n at: string,\n band: AiLayoutBand,\n centered: boolean,\n): SectionScope {\n return {\n page,\n index: at === 'header' ? 0 : 1,\n at,\n band,\n centered,\n level: 2,\n headed: true,\n itemsLeft: AI_LAYOUT_MAX_ITEMS,\n frame: true,\n link: { sections: [], from: -1, formSection: null },\n }\n}\n\n/** Compiles a site's header and footer, designed in the layout language, into its layout. */\nexport function aiCompileLayoutFrame(\n frame: { header: AiLayoutSection | null; footer: AiLayoutSection | null },\n plan: AiLayoutFramePlan,\n targets: AiLayoutTargets,\n): AiLayoutCompiledFrame {\n const tree = new AiLayoutTreeBuilder('lf')\n // A store's own pages are destinations the footer's list links by path (AGL-3676).\n const storePages: AiLayoutPage[] = (plan.storeLinks?.footer ?? []).map((link) => ({\n id: `store:${link.href}`,\n label: link.label,\n slug: link.href,\n href: link.href,\n }))\n const page: PageScope = {\n tree,\n plan: { title: plan.siteName, sections: [] },\n targets: { ...targets, pages: [...targets.pages, ...storePages], pageId: null },\n // A footer is drawn compact: its lines are lines, never blocks rule 1 would ask to share.\n options: { reusableComponents: true },\n settled: [],\n scrollTo: {},\n // A frame's lines are its own, never a page section's items.\n itemIds: [],\n // A frame shows no picture slot: a header and a footer are the same on every page.\n images: AI_LAYOUT_MAX_IMAGES,\n strong: { brand: 0, dark: 0 },\n formSection: null,\n pageIcon: AI_ICON_LIBRARY.sparkle,\n formsPlaced: new Set(),\n // A frame is drawn the platform's one way on every site.\n design: null,\n pictures: 0,\n features: 0,\n splitAt: -2,\n quotesOnly: [],\n }\n const name = aiLayoutFitText(plan.siteName, 'heading') || 'Home'\n const header = frame.header ?? { blocks: [] }\n const band = header.band ?? 'plain'\n const look = headerLook(band)\n const scope = frameScope(\n page,\n 'header',\n band === 'brand' || band === 'dark' ? 'brand' : 'plain',\n false,\n )\n const navLink = (\n entry: AiLayoutPage,\n role: string,\n interactions?: unknown[],\n ) => {\n const id = tree.add(\n 'muiScreenLink',\n {\n children:\n aiLayoutFitText(entry.label, 'label') || entry.label.slice(0, 28),\n // The blog is a path, not a page (AGL-3660).\n ...(entry.href ? { href: entry.href } : { screenId: entry.id }),\n renderAs: 'link',\n color: 'inherit',\n },\n { textDecoration: 'none' },\n null,\n role,\n )\n if (interactions) tree.nodes[id].interactions = interactions\n return id\n }\n const pages = plan.navPages.filter((entry) => entry.label.trim())\n const centeredNav = header.align === 'center' && pages.length > 0\n // The brand takes the room the navigation does not, and gives way before\n // it does: a long name ends in an ellipsis rather than pushing into the links.\n const brandSx = {\n typography: 'h6',\n fontWeight: 800,\n textDecoration: 'none',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n minWidth: 0,\n ...(centeredNav ? {} : { flexGrow: 1 }),\n }\n const brand = plan.homeId\n ? tree.add(\n 'muiScreenLink',\n {\n children: name.slice(0, 40),\n screenId: plan.homeId,\n renderAs: 'link',\n color: 'inherit',\n },\n brandSx,\n null,\n 'brand',\n )\n : tree.add(\n 'muiTypography',\n { children: name, variant: 'h6', component: 'p' },\n brandSx,\n null,\n 'brand',\n )\n const nav = pages.length\n ? tree.add(\n 'muiStack',\n {\n component: 'nav',\n ariaLabel: 'Main',\n direction: 'row',\n spacing: '3',\n alignItems: 'center',\n },\n { display: { xs: 'none', md: 'flex' } },\n pages.map((entry) => navLink(entry, 'navLink')),\n 'nav',\n )\n : null\n // The design's call to action: its first button, where it goes somewhere.\n const ctaBlock = header.blocks.find((block) => block.kind === 'button')\n // A store's announcement: the first short line the design gave its header (AGL-3676).\n const store = !!plan.shopId\n const noteBlock = store ? header.blocks.find((block) => block.kind === 'note' || block.kind === 'text' || block.kind === 'lede') : undefined\n for (const block of header.blocks) {\n if (block !== ctaBlock && block !== noteBlock)\n page.settled.push({\n at: 'header',\n what: `a ${block.kind} in the header; left out`,\n })\n }\n const ctaLabel = ctaBlock ? aiLayoutFitText(ctaBlock.text, 'label') : ''\n const ctaResolved =\n ctaBlock && ctaLabel\n ? aiLayoutResolveLink(ctaBlock.to, ctaLabel, scope.link, page.targets)\n : null\n if (ctaBlock && !ctaResolved)\n page.settled.push({\n at: 'header',\n what: `the button \"${ctaLabel}\" has nowhere to go; left out`,\n })\n // A storefront's header is its navigation and its cart (AGL-3676): the\n // live Willow Wick header's \"Shop candles\" button repeated its nav's Shop.\n // A store's button that only repeats a page the navigation links is left\n // out, and a store that sells carries its cart in that place instead.\n const repeatsNav =\n ctaResolved?.kind === 'page' && pages.some((entry) => !entry.href && entry.id === ctaResolved.screenId)\n const ctaTo = ctaResolved && store && (plan.cart || repeatsNav) ? null : ctaResolved\n if (ctaResolved && !ctaTo)\n page.settled.push({\n at: 'header',\n what: `the button \"${ctaLabel}\" left out: a store's header carries its ${plan.cart ? 'cart' : 'navigation'}, not a button repeating it`,\n })\n const ctaProps = ctaTo\n ? {\n children: aiLayoutRenamedLabel(ctaLabel, ctaTo),\n variant: 'contained',\n color: scope.band === 'brand' ? 'secondary' : 'primary',\n ...destinationProps(ctaTo),\n }\n : null\n // On the smallest phones the call to action moves into the menu.\n const cta = ctaProps\n ? tree.add(\n 'muiButton',\n ctaProps,\n {\n display: { xs: 'none', sm: 'inline-flex' },\n whiteSpace: 'nowrap',\n flexShrink: 0,\n },\n null,\n 'cta',\n )\n : null\n // A store's account, beside its cart and in the phone's menu (AGL-3676).\n const accountPath = plan.storeLinks?.account ?? null\n const accountLink = (role: string, interactions?: unknown[], sx?: Record<string, unknown>) => {\n const id = tree.add(\n 'muiScreenLink',\n { children: AI_LAYOUT_ACCOUNT_LINK, href: accountPath, renderAs: 'link', color: 'inherit' },\n { textDecoration: 'none', whiteSpace: 'nowrap', ...sx },\n null,\n role,\n )\n if (interactions) tree.nodes[id].interactions = interactions\n return id\n }\n // A phone's menu: the platform's Drawer, opened by its Menu Button; a link\n // followed from it closes it.\n const close = [AI_LAYOUT_CLOSE_DRAWER]\n const drawer = pages.length\n ? tree.add(\n 'muiDrawer',\n { anchor: 'right' },\n null,\n [\n tree.add(\n 'muiStack',\n { component: 'nav', ariaLabel: 'Menu' },\n {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n p: 3,\n typography: 'h6',\n },\n [\n ...pages.map((entry) => navLink(entry, 'menuLink', close)),\n accountPath ? accountLink('menuAccount', close) : null,\n ctaProps\n ? tree.add(\n 'muiButton',\n { ...ctaProps, fullWidth: true },\n { mt: 2 },\n null,\n 'menuCta',\n )\n : null,\n ],\n 'menu',\n ),\n ],\n 'drawer',\n )\n : null\n const toggle = drawer\n ? tree.add(\n 'muiDrawerToggle',\n { ariaLabel: 'Open the menu' },\n { display: { xs: 'inline-flex', md: 'none' }, flexShrink: 0 },\n null,\n 'menuButton',\n )\n : null\n const middle =\n nav && centeredNav\n ? tree.add(\n 'muiBox',\n null,\n { flexGrow: 1, display: 'flex', justifyContent: 'center' },\n [nav],\n 'navRoom',\n )\n : nav\n // A store's cart, on every page, where a shopper looks for it (AGL-3676).\n const cart = plan.cart\n ? tree.add(AI_LAYOUT_CART_ELEMENT, { variant: 'button' }, { flexShrink: 0 }, null, 'cart')\n : null\n // On a phone the account is in the menu, and the cart stays in the bar.\n const account = accountPath ? accountLink('account', undefined, { display: { xs: 'none', md: 'inline-flex' }, flexShrink: 0 }) : null\n // The row lives in a Container inside the Toolbar Content, which may only sit in an App Bar.\n const row = tree.add(\n 'muiContainer',\n { maxWidth: 'lg' },\n { display: 'flex', alignItems: 'center', columnGap: 3 },\n [brand, middle, cta, account, cart, toggle, drawer],\n 'headerRow',\n )\n const toolbar = tree.add(\n 'muiToolbar',\n { disableGutters: true },\n { minHeight: { xs: 64, md: 76 } },\n [row],\n 'toolbar',\n )\n const bar = tree.add(\n 'muiAppBar',\n {\n ...look.props,\n position: 'sticky',\n // Over a page that opens with a photo cover, the bar sits on the photo (AGL-3660).\n overHero: true,\n component: 'header',\n ariaLabel: 'Site header',\n },\n look.sx,\n [toolbar],\n 'header',\n )\n // A selling store's announcement bar, above the header, linking its shop (AGL-3676).\n const announcement =\n store && plan.cart && plan.shopId\n ? (() => {\n const said = aiLayoutFitText(noteBlock?.text, 'note')\n if (noteBlock && !said) page.settled.push({ at: 'header', what: 'an announcement with no words left; the shop’s own line instead' })\n // The design's line, then the way into the shop; else the way in alone.\n const link = tree.add(\n 'muiScreenLink',\n {\n children: said ? AI_LAYOUT_STORE_ANNOUNCEMENT_LINK : AI_LAYOUT_STORE_ANNOUNCEMENT,\n screenId: plan.shopId,\n renderAs: 'link',\n color: 'inherit',\n },\n { fontWeight: 700 },\n null,\n 'announcementLink',\n )\n const words = said\n ? tree.add('muiTypography', { children: said, variant: 'body2', component: 'p' }, null, null, 'announcementText')\n : null\n return tree.add(\n 'muiStack',\n { direction: 'row', spacing: '1.5', justifyContent: 'center', alignItems: 'baseline', useFlexGap: true, flexWrap: 'wrap' },\n { bgcolor: 'primary.main', color: 'primary.contrastText', typography: 'body2', py: 1, px: 2 },\n [words, link],\n 'announcement',\n )\n })()\n : null\n // The page grows into the room the window leaves, so a short page keeps\n // its footer at the bottom.\n const slot = tree.add(\n 'layoutSlot',\n { component: 'main' },\n { flexGrow: 1 },\n null,\n 'slot',\n )\n const footer = compileFooter(page, frame.footer, plan, name)\n // No heading with nothing under it, no column of only a heading, columns\n // top-aligned (AGL-3735): a list under a heading can compile to nothing.\n if (aiLayoutRepairFrameHollows(tree.nodes as unknown as Record<string, unknown>)) {\n page.settled.push({ at: 'footer', what: 'a heading with nothing under it taken out, and its column with it' })\n }\n const column = tree.add(\n 'muiStack',\n null,\n { minHeight: '100vh' },\n [announcement, bar, slot, footer],\n 'frame',\n )\n tree.prune(column, new Set([slot]))\n const rootId = tree.add('div', null, null, [column], 'layout')\n return { tree: { rootId, nodes: tree.nodes }, settled: page.settled }\n}\n\n/** What a store's own column of account and policy links is headed where the footer design gave no store heading (AGL-3735). */\nexport const AI_LAYOUT_STORE_FOOTER_HEADING = 'Customer care'\n\n/** A footer heading a store's account and policies belong under (AGL-3735). */\nconst STORE_HEADING = /\\b(?:shop|store|shopping|help|customer|care|support|service|orders?|account|policies|policy|legal|info|information|shipping|returns)\\b/i\n\n/**\n * The footer heading a store's account and policy links go under\n * (AGL-3735): a heading of the design's that names the store or its help\n * (\"Our shop\", \"Help\", \"Customer care\"), the first with nothing under it\n * before one with words or links of its own. Never the heading over the\n * site's pages, whose list is the site's navigation. `end` is where the\n * links go: the end of what that heading heads.\n */\nexport function aiLayoutFooterStoreHeading(\n columns: readonly AiLayoutBlock[][],\n navItems: readonly { to: string }[],\n): { column: AiLayoutBlock[]; end: number; heading: string } | null {\n const navTo = new Set(navItems.map((item) => item.to))\n const candidates: Array<{ column: AiLayoutBlock[]; end: number; heading: string; empty: boolean }> = []\n for (const column of columns) {\n column.forEach((block, at) => {\n if (block.kind !== 'heading' || !STORE_HEADING.test(block.text ?? '')) return\n let end = at + 1\n while (end < column.length && column[end].kind !== 'heading') end += 1\n const under = column.slice(at + 1, end)\n const site = under.some((one) => one.kind === 'list' && (one.items ?? []).some((item) => navTo.has(item.to ?? '')))\n if (site) return\n candidates.push({ column: column as AiLayoutBlock[], end, heading: String(block.text), empty: under.length === 0 })\n })\n }\n const pick = candidates.find((one) => one.empty) ?? candidates[0]\n return pick ? { column: pick.column, end: pick.end, heading: pick.heading } : null\n}\n\n/** A link followed from the phone menu closes it. */\nconst AI_LAYOUT_CLOSE_DRAWER = {\n id: 'ai-close-menu',\n name: 'Close the menu',\n enabled: true,\n trigger: { event: 'elementClick', everyTime: true },\n steps: [{ type: 'closeDrawer' }],\n}\n\n/**\n * The footer the design describes, with what every footer owes a visitor\n * written by code: the site's pages, where the design links none of them,\n * and the site's name under a rule.\n */\nfunction compileFooter(\n page: PageScope,\n footer: AiLayoutSection | null,\n plan: AiLayoutFramePlan,\n name: string,\n): string {\n const tree = page.tree\n const asked = footer?.band ?? 'soft'\n const band = plan.closesDark && (asked === 'brand' || asked === 'dark') ? 'soft' : asked\n if (band !== asked) page.settled.push({ at: 'footer', what: `a ${asked} footer under a dark closing band drawn soft` })\n const scope = frameScope(page, 'footer', band, footer?.align === 'center')\n // The footer's page links are the site's pages, every one, as the header's\n // are (AGL-3660): a list the model wrote of page links is drawn with them\n // in its place, and a footer with none gets one. The model places the list;\n // it never chooses which pages it holds.\n const navItems = plan.navPages.map((entry) => ({\n title: entry.label,\n text: '',\n to: `page:${entry.id}`,\n }))\n let linksPages = false\n const blocks = (footer?.blocks ?? []).flatMap((given): AiLayoutBlock[] => {\n // The bottom bar carries the frame's one copyright line; a line of the\n // answer's own that reads as one would print it twice.\n if (aiLayoutIsCopyright(given.text)) {\n page.settled.push({ at: 'footer', what: 'a copyright line; the footer writes its own' })\n return []\n }\n const notice = (item: { title: string; text: string }) => aiLayoutIsCopyright(item.title) || aiLayoutIsCopyright(item.text)\n const block = given.items?.some(notice) ? { ...given, items: given.items.filter((item) => !notice(item)) } : given\n if (block !== given) page.settled.push({ at: 'footer', what: 'a copyright line in a list; the footer writes its own' })\n if (FOOTER_LEAVES_OUT.has(block.kind)) {\n page.settled.push({\n at: 'footer',\n what: `a ${block.kind} in the footer; left out`,\n })\n return []\n }\n const pageLinks =\n block.kind === 'list' &&\n (block.items ?? []).some((item) => /^page:/.test(item.to ?? ''))\n if (!pageLinks) return [block]\n if (linksPages || !navItems.length) {\n page.settled.push({ at: 'footer', what: 'a second list of page links; left out' })\n return []\n }\n linksPages = true\n return [{ ...block, items: navItems }]\n })\n const cols = footer?.cols && footer.cols.length >= 2 ? footer.cols : null\n const columns: AiLayoutBlock[][] = cols ? cols.map(() => []) : [[]]\n for (const block of blocks)\n columns[\n cols && block.col !== undefined ? Math.min(block.col, cols.length - 1) : 0\n ].push(block)\n const weights = cols ? [...cols] : [2]\n if (!linksPages && plan.navPages.length) {\n columns.push([\n {\n kind: 'list',\n items: plan.navPages.map((entry) => ({\n title: entry.label,\n text: '',\n to: `page:${entry.id}`,\n })),\n },\n ])\n weights.push(1)\n }\n // A store's account and policies (AGL-3676): under the store heading the\n // design already wrote (\"Our shop\", \"Help\"…), never as a column with no\n // heading beside a heading with nothing under it (AGL-3735); where the\n // design wrote none, a column of their own, headed.\n const storeLinks = plan.storeLinks?.footer ?? []\n if (storeLinks.length) {\n const list: AiLayoutBlock = { kind: 'list', items: storeLinks.map((link) => ({ title: link.label, text: '', to: `page:store:${link.href}` })) }\n const home = aiLayoutFooterStoreHeading(columns, navItems)\n if (home) {\n home.column.splice(home.end, 0, list)\n page.settled.push({ at: 'footer', what: `the store's account and policy links under \"${home.heading}\"` })\n } else {\n columns.push([{ kind: 'heading', text: AI_LAYOUT_STORE_FOOTER_HEADING }, list])\n weights.push(1)\n }\n }\n const filled = columns\n .map((column, position) => ({ column, weight: weights[position] ?? 1 }))\n .filter((entry) => entry.column.length)\n // A footer's columns are top-aligned: each starts at its heading (AGL-3735).\n const top =\n filled.length > 1\n ? compileRow(scope, filled, 'flex-start')\n : filled.length === 1\n ? compileFlow(scope, filled[0].column, 'full')\n : null\n const copyright = tree.add(\n 'muiTypography',\n { children: `© ${name}`, variant: 'body2', component: 'p' },\n scope.band === 'brand' ? null : { color: 'text.secondary' },\n null,\n 'copyright',\n )\n const bottom = tree.add(\n 'muiBox',\n null,\n { pt: 3, borderTop: 1, borderColor: 'divider' },\n [copyright],\n 'footerBottom',\n )\n const stack = tree.add(\n 'muiStack',\n { spacing: '6' },\n null,\n [top, bottom],\n 'footerStack',\n )\n const container = tree.add(\n 'muiContainer',\n { maxWidth: 'lg' },\n { py: { xs: 6, md: 8 } },\n [stack],\n 'footerInner',\n )\n return tree.add(\n 'section',\n {\n element: 'footer',\n ariaLabel: 'Site footer',\n ...(scope.band === 'dark' ? { colorScheme: 'dark' } : {}),\n },\n scope.band === 'soft'\n ? { bgcolor: 'background.paper', borderTop: 1, borderColor: 'divider' }\n : bandSx(scope.band),\n [container],\n 'footer',\n )\n}\n"],"names":["AI_ICON_LIBRARY","AI_LAYOUT_MAX_IMAGES","bandSx","compileFlow","compileRow","destinationProps","aiLayoutFitText","aiLayoutRepairFrameHollows","AI_LAYOUT_CART_ELEMENT","AI_LAYOUT_MAX_ITEMS","aiLayoutRenamedLabel","aiLayoutResolveLink","AiLayoutTreeBuilder","AI_LAYOUT_ACCOUNT_LINK","AI_LAYOUT_STORE_ANNOUNCEMENT","AI_LAYOUT_STORE_ANNOUNCEMENT_LINK","headerLook","band","props","color","sx","boxShadow","bgcolor","aiLayoutIsCopyright","text","test","FOOTER_LEAVES_OUT","Set","frameScope","page","at","centered","index","level","headed","itemsLeft","frame","link","sections","from","formSection","aiCompileLayoutFrame","plan","targets","header","tree","storePages","storeLinks","footer","map","id","href","label","slug","title","siteName","pages","pageId","options","reusableComponents","settled","scrollTo","itemIds","images","strong","brand","dark","pageIcon","sparkle","formsPlaced","design","pictures","features","splitAt","quotesOnly","name","blocks","look","scope","navLink","entry","role","interactions","add","children","slice","screenId","renderAs","textDecoration","nodes","navPages","filter","trim","centeredNav","align","length","brandSx","typography","fontWeight","whiteSpace","overflow","textOverflow","minWidth","flexGrow","homeId","variant","component","nav","ariaLabel","direction","spacing","alignItems","display","xs","md","ctaBlock","find","block","kind","store","shopId","noteBlock","undefined","push","what","ctaLabel","ctaResolved","to","repeatsNav","some","ctaTo","cart","ctaProps","cta","sm","flexShrink","accountPath","account","accountLink","close","AI_LAYOUT_CLOSE_DRAWER","drawer","anchor","flexDirection","gap","p","fullWidth","mt","toggle","middle","justifyContent","row","maxWidth","columnGap","toolbar","disableGutters","minHeight","bar","position","overHero","announcement","said","words","useFlexGap","flexWrap","py","px","slot","compileFooter","column","prune","rootId","AI_LAYOUT_STORE_FOOTER_HEADING","STORE_HEADING","aiLayoutFooterStoreHeading","columns","navItems","candidates","navTo","item","forEach","end","under","site","one","items","has","heading","String","empty","pick","enabled","trigger","event","everyTime","steps","type","asked","closesDark","linksPages","flatMap","given","notice","pageLinks","cols","col","Math","min","weights","list","home","splice","filled","weight","top","copyright","bottom","pt","borderTop","borderColor","stack","container","element","colorScheme"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-frame.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiStoreFrameLinks } from '../model/ai-site-store-pages'\nimport { AI_ICON_LIBRARY } from '../runtime/ai-icon-library'\nimport {\n AI_LAYOUT_MAX_IMAGES,\n bandSx,\n compileFlow,\n compileRow,\n destinationProps,\n type PageScope,\n type SectionScope,\n} from './ai-layout-compiler'\nimport { aiLayoutFitText } from './ai-layout-copy'\nimport { aiLayoutRepairFrameHollows } from './ai-layout-frame-hollows'\nimport { AI_LAYOUT_CART_ELEMENT } from './ai-layout-listings'\nimport {\n AI_LAYOUT_MAX_ITEMS,\n type AiLayoutBand,\n type AiLayoutBlock,\n type AiLayoutSection,\n type AiLayoutSettlement,\n} from './ai-layout-language'\nimport { AI_LAYOUT_LABELS_EN, aiLayoutLabel } from './ai-layout-labels'\nimport {\n aiLayoutRenamedLabel,\n aiLayoutResolveLink,\n type AiLayoutPage,\n type AiLayoutTargets,\n} from './ai-layout-links'\nimport { AiLayoutTreeBuilder, type AiLayoutRawTree } from './ai-layout-tree'\n\n/**\n * A site's frame — the header and the footer every page renders between —\n * compiled from the layout language (AGL-3660), the same language a page is\n * designed in.\n *\n * The design is the model's: the header's band and alignment and its one\n * call to action, and the footer's columns and every word in them. What a\n * frame owes the site is the platform's, written here, so it is never wrong\n * and never invented: the site's name as the brand, linking home; its pages\n * as the navigation, a row on a wide screen and the platform's Drawer behind\n * its Menu Button on a phone; the Layout Slot, the page's one main landmark,\n * held tall enough that the footer sits at the bottom of a short page; and\n * the site's pages and name in the footer, where the design gave neither.\n */\n\nexport interface AiLayoutFramePlan {\n /** The site's name, which the header and the footer carry as its brand. */\n siteName: string\n /** The site's home page, which the brand links; none where the site has none yet. */\n homeId: string | null\n /** The pages the navigation links, in order. */\n navPages: readonly AiLayoutPage[]\n /**\n * Whether the site's pages close on a dark photo band (AGL-3660,\n * `AiLayoutDesignChoices.coverClose`): a brand or dark footer under it\n * would read as a second loud band, so the footer takes the quiet one.\n */\n closesDark?: boolean\n /**\n * Whether the site sells (AGL-3676): its header carries the store's cart\n * button — the commerce plugin's Cart, which opens the cart in a drawer\n * from every page — beside the navigation.\n */\n cart?: boolean\n /**\n * A selling store's Shop page (AGL-3676), by its id. Its header is a\n * storefront's: the cart beside the navigation and no call-to-action button\n * repeating a link the navigation already has, and a thin announcement bar\n * above it — the line the design gave its header as a note, else one that\n * opens the shop — linking the Shop page.\n */\n shopId?: string | null\n /**\n * A store's own pages (AGL-3676), by path: the header links the account\n * beside the cart, and the footer the account and the store's policies\n * under the site's pages. They are written after the layout, so they are\n * linked by their addresses, as the blog is.\n */\n storeLinks?: AiStoreFrameLinks | null\n /** The site's language (AGL-3760): the frame's own words are in it where `ai-layout-labels.ts` lists it. */\n language?: string\n}\n\n/** What the header's link to a store's account page says (AGL-3676), in English. */\nexport const AI_LAYOUT_ACCOUNT_LINK = AI_LAYOUT_LABELS_EN.account\n\n/** What a store's announcement bar says where its design gave it no line (AGL-3676): true of every store. */\nexport const AI_LAYOUT_STORE_ANNOUNCEMENT = AI_LAYOUT_LABELS_EN.storeAnnouncement\n\n/** The way into the shop beside the design's own announcement (AGL-3676). */\nexport const AI_LAYOUT_STORE_ANNOUNCEMENT_LINK = AI_LAYOUT_LABELS_EN.shopNow\n\nexport interface AiLayoutCompiledFrame {\n tree: AiLayoutRawTree\n settled: AiLayoutSettlement[]\n}\n\n/** The header's colors by band, as palette roles. */\nfunction headerLook(band: AiLayoutBand): {\n props: Record<string, unknown>\n sx: Record<string, unknown>\n} {\n if (band === 'brand')\n return { props: { color: 'primary' }, sx: { boxShadow: 'none' } }\n if (band === 'dark') {\n return {\n props: { color: 'primary' },\n sx: {\n boxShadow: 'none',\n bgcolor: 'primary.dark',\n color: 'primary.contrastText',\n },\n }\n }\n // A rule, a shadow or nothing under a plain header is the site theme's\n // MuiAppBar style (AGL-3660); the band's color stays here.\n return {\n props: { color: 'inherit' },\n sx: { bgcolor: 'background.paper' },\n }\n}\n\n/** A line that reads as a copyright notice: \"© 2026 …\", \"Copyright …\", \"All rights reserved.\" */\nexport function aiLayoutIsCopyright(text: unknown): boolean {\n return typeof text === 'string' && /^\\s*(?:©|\\(c\\)|copyright\\b)|all rights reserved/i.test(text)\n}\n\n/** What a footer leaves out: a picture slot, and the page sections a footer is no place for. */\nconst FOOTER_LEAVES_OUT: ReadonlySet<AiLayoutBlock['kind']> = new Set([\n 'image',\n 'faq',\n 'quotes',\n 'cards',\n 'steps',\n 'stats',\n 'form',\n 'component',\n])\n\n/** A frame scope: no headings of its own (a layout carries no outline), no pictures. */\nfunction frameScope(\n page: PageScope,\n at: string,\n band: AiLayoutBand,\n centered: boolean,\n): SectionScope {\n return {\n page,\n index: at === 'header' ? 0 : 1,\n at,\n band,\n centered,\n level: 2,\n headed: true,\n itemsLeft: AI_LAYOUT_MAX_ITEMS,\n frame: true,\n link: { sections: [], from: -1, formSection: null },\n }\n}\n\n/** Compiles a site's header and footer, designed in the layout language, into its layout. */\nexport function aiCompileLayoutFrame(\n frame: { header: AiLayoutSection | null; footer: AiLayoutSection | null },\n plan: AiLayoutFramePlan,\n targets: AiLayoutTargets,\n): AiLayoutCompiledFrame {\n const tree = new AiLayoutTreeBuilder('lf')\n // A store's own pages are destinations the footer's list links by path (AGL-3676).\n const storePages: AiLayoutPage[] = (plan.storeLinks?.footer ?? []).map((link) => ({\n id: `store:${link.href}`,\n label: link.label,\n slug: link.href,\n href: link.href,\n }))\n const page: PageScope = {\n tree,\n plan: { title: plan.siteName, sections: [] },\n targets: { ...targets, pages: [...targets.pages, ...storePages], pageId: null },\n // A footer is drawn compact: its lines are lines, never blocks rule 1 would ask to share.\n options: { reusableComponents: true, ...(plan.language ? { language: plan.language } : {}) },\n settled: [],\n scrollTo: {},\n // A frame's lines are its own, never a page section's items.\n itemIds: [],\n // A frame shows no picture slot: a header and a footer are the same on every page.\n images: AI_LAYOUT_MAX_IMAGES,\n strong: { brand: 0, dark: 0 },\n formSection: null,\n pageIcon: AI_ICON_LIBRARY.sparkle,\n formsPlaced: new Set(),\n // A frame is drawn the platform's one way on every site.\n design: null,\n pictures: 0,\n features: 0,\n splitAt: -2,\n quotesOnly: [],\n }\n const name = aiLayoutFitText(plan.siteName, 'heading') || 'Home'\n const header = frame.header ?? { blocks: [] }\n const band = header.band ?? 'plain'\n const look = headerLook(band)\n const scope = frameScope(\n page,\n 'header',\n band === 'brand' || band === 'dark' ? 'brand' : 'plain',\n false,\n )\n const navLink = (\n entry: AiLayoutPage,\n role: string,\n interactions?: unknown[],\n ) => {\n const id = tree.add(\n 'muiScreenLink',\n {\n children:\n aiLayoutFitText(entry.label, 'label') || entry.label.slice(0, 28),\n // The blog is a path, not a page (AGL-3660).\n ...(entry.href ? { href: entry.href } : { screenId: entry.id }),\n renderAs: 'link',\n color: 'inherit',\n },\n { textDecoration: 'none' },\n null,\n role,\n )\n if (interactions) tree.nodes[id].interactions = interactions\n return id\n }\n const pages = plan.navPages.filter((entry) => entry.label.trim())\n const centeredNav = header.align === 'center' && pages.length > 0\n // The brand takes the room the navigation does not, and gives way before\n // it does: a long name ends in an ellipsis rather than pushing into the links.\n const brandSx = {\n typography: 'h6',\n fontWeight: 800,\n textDecoration: 'none',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n minWidth: 0,\n ...(centeredNav ? {} : { flexGrow: 1 }),\n }\n const brand = plan.homeId\n ? tree.add(\n 'muiScreenLink',\n {\n children: name.slice(0, 40),\n screenId: plan.homeId,\n renderAs: 'link',\n color: 'inherit',\n },\n brandSx,\n null,\n 'brand',\n )\n : tree.add(\n 'muiTypography',\n { children: name, variant: 'h6', component: 'p' },\n brandSx,\n null,\n 'brand',\n )\n const nav = pages.length\n ? tree.add(\n 'muiStack',\n {\n component: 'nav',\n ariaLabel: 'Main',\n direction: 'row',\n spacing: '3',\n alignItems: 'center',\n },\n { display: { xs: 'none', md: 'flex' } },\n pages.map((entry) => navLink(entry, 'navLink')),\n 'nav',\n )\n : null\n // The design's call to action: its first button, where it goes somewhere.\n const ctaBlock = header.blocks.find((block) => block.kind === 'button')\n // A store's announcement: the first short line the design gave its header (AGL-3676).\n const store = !!plan.shopId\n const noteBlock = store ? header.blocks.find((block) => block.kind === 'note' || block.kind === 'text' || block.kind === 'lede') : undefined\n for (const block of header.blocks) {\n if (block !== ctaBlock && block !== noteBlock)\n page.settled.push({\n at: 'header',\n what: `a ${block.kind} in the header; left out`,\n })\n }\n const ctaLabel = ctaBlock ? aiLayoutFitText(ctaBlock.text, 'label') : ''\n const ctaResolved =\n ctaBlock && ctaLabel\n ? aiLayoutResolveLink(ctaBlock.to, ctaLabel, scope.link, page.targets)\n : null\n if (ctaBlock && !ctaResolved)\n page.settled.push({\n at: 'header',\n what: `the button \"${ctaLabel}\" has nowhere to go; left out`,\n })\n // A storefront's header is its navigation and its cart (AGL-3676): the\n // live Willow Wick header's \"Shop candles\" button repeated its nav's Shop.\n // A store's button that only repeats a page the navigation links is left\n // out, and a store that sells carries its cart in that place instead.\n const repeatsNav =\n ctaResolved?.kind === 'page' && pages.some((entry) => !entry.href && entry.id === ctaResolved.screenId)\n const ctaTo = ctaResolved && store && (plan.cart || repeatsNav) ? null : ctaResolved\n if (ctaResolved && !ctaTo)\n page.settled.push({\n at: 'header',\n what: `the button \"${ctaLabel}\" left out: a store's header carries its ${plan.cart ? 'cart' : 'navigation'}, not a button repeating it`,\n })\n const ctaProps = ctaTo\n ? {\n children: aiLayoutRenamedLabel(ctaLabel, ctaTo),\n variant: 'contained',\n color: scope.band === 'brand' ? 'secondary' : 'primary',\n ...destinationProps(ctaTo),\n }\n : null\n // On the smallest phones the call to action moves into the menu.\n const cta = ctaProps\n ? tree.add(\n 'muiButton',\n ctaProps,\n {\n display: { xs: 'none', sm: 'inline-flex' },\n whiteSpace: 'nowrap',\n flexShrink: 0,\n },\n null,\n 'cta',\n )\n : null\n // A store's account, beside its cart and in the phone's menu (AGL-3676).\n const accountPath = plan.storeLinks?.account ?? null\n const accountLink = (role: string, interactions?: unknown[], sx?: Record<string, unknown>) => {\n const id = tree.add(\n 'muiScreenLink',\n { children: aiLayoutLabel('account', plan.language), href: accountPath, renderAs: 'link', color: 'inherit' },\n { textDecoration: 'none', whiteSpace: 'nowrap', ...sx },\n null,\n role,\n )\n if (interactions) tree.nodes[id].interactions = interactions\n return id\n }\n // A phone's menu: the platform's Drawer, opened by its Menu Button; a link\n // followed from it closes it.\n const close = [AI_LAYOUT_CLOSE_DRAWER]\n const drawer = pages.length\n ? tree.add(\n 'muiDrawer',\n { anchor: 'right' },\n null,\n [\n tree.add(\n 'muiStack',\n { component: 'nav', ariaLabel: 'Menu' },\n {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n p: 3,\n typography: 'h6',\n },\n [\n ...pages.map((entry) => navLink(entry, 'menuLink', close)),\n accountPath ? accountLink('menuAccount', close) : null,\n ctaProps\n ? tree.add(\n 'muiButton',\n { ...ctaProps, fullWidth: true },\n { mt: 2 },\n null,\n 'menuCta',\n )\n : null,\n ],\n 'menu',\n ),\n ],\n 'drawer',\n )\n : null\n const toggle = drawer\n ? tree.add(\n 'muiDrawerToggle',\n { ariaLabel: 'Open the menu' },\n { display: { xs: 'inline-flex', md: 'none' }, flexShrink: 0 },\n null,\n 'menuButton',\n )\n : null\n const middle =\n nav && centeredNav\n ? tree.add(\n 'muiBox',\n null,\n { flexGrow: 1, display: 'flex', justifyContent: 'center' },\n [nav],\n 'navRoom',\n )\n : nav\n // A store's cart, on every page, where a shopper looks for it (AGL-3676).\n const cart = plan.cart\n ? tree.add(AI_LAYOUT_CART_ELEMENT, { variant: 'button' }, { flexShrink: 0 }, null, 'cart')\n : null\n // On a phone the account is in the menu, and the cart stays in the bar.\n const account = accountPath ? accountLink('account', undefined, { display: { xs: 'none', md: 'inline-flex' }, flexShrink: 0 }) : null\n // The row lives in a Container inside the Toolbar Content, which may only sit in an App Bar.\n const row = tree.add(\n 'muiContainer',\n { maxWidth: 'lg' },\n { display: 'flex', alignItems: 'center', columnGap: 3 },\n [brand, middle, cta, account, cart, toggle, drawer],\n 'headerRow',\n )\n const toolbar = tree.add(\n 'muiToolbar',\n { disableGutters: true },\n { minHeight: { xs: 64, md: 76 } },\n [row],\n 'toolbar',\n )\n const bar = tree.add(\n 'muiAppBar',\n {\n ...look.props,\n position: 'sticky',\n // Over a page that opens with a photo cover, the bar sits on the photo (AGL-3660).\n overHero: true,\n component: 'header',\n ariaLabel: 'Site header',\n },\n look.sx,\n [toolbar],\n 'header',\n )\n // A selling store's announcement bar, above the header, linking its shop (AGL-3676).\n const announcement =\n store && plan.cart && plan.shopId\n ? (() => {\n const said = aiLayoutFitText(noteBlock?.text, 'note')\n if (noteBlock && !said) page.settled.push({ at: 'header', what: 'an announcement with no words left; the shop’s own line instead' })\n // The design's line, then the way into the shop; else the way in alone.\n const link = tree.add(\n 'muiScreenLink',\n {\n children: aiLayoutLabel(said ? 'shopNow' : 'storeAnnouncement', plan.language),\n screenId: plan.shopId,\n renderAs: 'link',\n color: 'inherit',\n },\n { fontWeight: 700 },\n null,\n 'announcementLink',\n )\n const words = said\n ? tree.add('muiTypography', { children: said, variant: 'body2', component: 'p' }, null, null, 'announcementText')\n : null\n return tree.add(\n 'muiStack',\n { direction: 'row', spacing: '1.5', justifyContent: 'center', alignItems: 'baseline', useFlexGap: true, flexWrap: 'wrap' },\n { bgcolor: 'primary.main', color: 'primary.contrastText', typography: 'body2', py: 1, px: 2 },\n [words, link],\n 'announcement',\n )\n })()\n : null\n // The page grows into the room the window leaves, so a short page keeps\n // its footer at the bottom.\n const slot = tree.add(\n 'layoutSlot',\n { component: 'main' },\n { flexGrow: 1 },\n null,\n 'slot',\n )\n const footer = compileFooter(page, frame.footer, plan, name)\n // No heading with nothing under it, no column of only a heading, columns\n // top-aligned (AGL-3735): a list under a heading can compile to nothing.\n if (aiLayoutRepairFrameHollows(tree.nodes as unknown as Record<string, unknown>)) {\n page.settled.push({ at: 'footer', what: 'a heading with nothing under it taken out, and its column with it' })\n }\n const column = tree.add(\n 'muiStack',\n null,\n { minHeight: '100vh' },\n [announcement, bar, slot, footer],\n 'frame',\n )\n tree.prune(column, new Set([slot]))\n const rootId = tree.add('div', null, null, [column], 'layout')\n return { tree: { rootId, nodes: tree.nodes }, settled: page.settled }\n}\n\n/** What a store's own column of account and policy links is headed where the footer design gave no store heading (AGL-3735). */\nexport const AI_LAYOUT_STORE_FOOTER_HEADING = AI_LAYOUT_LABELS_EN.customerCare\n\n/** A footer heading a store's account and policies belong under (AGL-3735). */\nconst STORE_HEADING = /\\b(?:shop|store|shopping|help|customer|care|support|service|orders?|account|policies|policy|legal|info|information|shipping|returns)\\b/i\n\n/**\n * The footer heading a store's account and policy links go under\n * (AGL-3735): a heading of the design's that names the store or its help\n * (\"Our shop\", \"Help\", \"Customer care\"), the first with nothing under it\n * before one with words or links of its own. Never the heading over the\n * site's pages, whose list is the site's navigation. `end` is where the\n * links go: the end of what that heading heads.\n */\nexport function aiLayoutFooterStoreHeading(\n columns: readonly AiLayoutBlock[][],\n navItems: readonly { to: string }[],\n): { column: AiLayoutBlock[]; end: number; heading: string } | null {\n const navTo = new Set(navItems.map((item) => item.to))\n const candidates: Array<{ column: AiLayoutBlock[]; end: number; heading: string; empty: boolean }> = []\n for (const column of columns) {\n column.forEach((block, at) => {\n if (block.kind !== 'heading' || !STORE_HEADING.test(block.text ?? '')) return\n let end = at + 1\n while (end < column.length && column[end].kind !== 'heading') end += 1\n const under = column.slice(at + 1, end)\n const site = under.some((one) => one.kind === 'list' && (one.items ?? []).some((item) => navTo.has(item.to ?? '')))\n if (site) return\n candidates.push({ column: column as AiLayoutBlock[], end, heading: String(block.text), empty: under.length === 0 })\n })\n }\n const pick = candidates.find((one) => one.empty) ?? candidates[0]\n return pick ? { column: pick.column, end: pick.end, heading: pick.heading } : null\n}\n\n/** A link followed from the phone menu closes it. */\nconst AI_LAYOUT_CLOSE_DRAWER = {\n id: 'ai-close-menu',\n name: 'Close the menu',\n enabled: true,\n trigger: { event: 'elementClick', everyTime: true },\n steps: [{ type: 'closeDrawer' }],\n}\n\n/**\n * The footer the design describes, with what every footer owes a visitor\n * written by code: the site's pages, where the design links none of them,\n * and the site's name under a rule.\n */\nfunction compileFooter(\n page: PageScope,\n footer: AiLayoutSection | null,\n plan: AiLayoutFramePlan,\n name: string,\n): string {\n const tree = page.tree\n const asked = footer?.band ?? 'soft'\n const band = plan.closesDark && (asked === 'brand' || asked === 'dark') ? 'soft' : asked\n if (band !== asked) page.settled.push({ at: 'footer', what: `a ${asked} footer under a dark closing band drawn soft` })\n const scope = frameScope(page, 'footer', band, footer?.align === 'center')\n // The footer's page links are the site's pages, every one, as the header's\n // are (AGL-3660): a list the model wrote of page links is drawn with them\n // in its place, and a footer with none gets one. The model places the list;\n // it never chooses which pages it holds.\n const navItems = plan.navPages.map((entry) => ({\n title: entry.label,\n text: '',\n to: `page:${entry.id}`,\n }))\n let linksPages = false\n const blocks = (footer?.blocks ?? []).flatMap((given): AiLayoutBlock[] => {\n // The bottom bar carries the frame's one copyright line; a line of the\n // answer's own that reads as one would print it twice.\n if (aiLayoutIsCopyright(given.text)) {\n page.settled.push({ at: 'footer', what: 'a copyright line; the footer writes its own' })\n return []\n }\n const notice = (item: { title: string; text: string }) => aiLayoutIsCopyright(item.title) || aiLayoutIsCopyright(item.text)\n const block = given.items?.some(notice) ? { ...given, items: given.items.filter((item) => !notice(item)) } : given\n if (block !== given) page.settled.push({ at: 'footer', what: 'a copyright line in a list; the footer writes its own' })\n if (FOOTER_LEAVES_OUT.has(block.kind)) {\n page.settled.push({\n at: 'footer',\n what: `a ${block.kind} in the footer; left out`,\n })\n return []\n }\n const pageLinks =\n block.kind === 'list' &&\n (block.items ?? []).some((item) => /^page:/.test(item.to ?? ''))\n if (!pageLinks) return [block]\n if (linksPages || !navItems.length) {\n page.settled.push({ at: 'footer', what: 'a second list of page links; left out' })\n return []\n }\n linksPages = true\n return [{ ...block, items: navItems }]\n })\n const cols = footer?.cols && footer.cols.length >= 2 ? footer.cols : null\n const columns: AiLayoutBlock[][] = cols ? cols.map(() => []) : [[]]\n for (const block of blocks)\n columns[\n cols && block.col !== undefined ? Math.min(block.col, cols.length - 1) : 0\n ].push(block)\n const weights = cols ? [...cols] : [2]\n if (!linksPages && plan.navPages.length) {\n columns.push([\n {\n kind: 'list',\n items: plan.navPages.map((entry) => ({\n title: entry.label,\n text: '',\n to: `page:${entry.id}`,\n })),\n },\n ])\n weights.push(1)\n }\n // A store's account and policies (AGL-3676): under the store heading the\n // design already wrote (\"Our shop\", \"Help\"…), never as a column with no\n // heading beside a heading with nothing under it (AGL-3735); where the\n // design wrote none, a column of their own, headed.\n const storeLinks = plan.storeLinks?.footer ?? []\n if (storeLinks.length) {\n const list: AiLayoutBlock = { kind: 'list', items: storeLinks.map((link) => ({ title: link.label, text: '', to: `page:store:${link.href}` })) }\n const home = aiLayoutFooterStoreHeading(columns, navItems)\n if (home) {\n home.column.splice(home.end, 0, list)\n page.settled.push({ at: 'footer', what: `the store's account and policy links under \"${home.heading}\"` })\n } else {\n columns.push([{ kind: 'heading', text: aiLayoutLabel('customerCare', plan.language) }, list])\n weights.push(1)\n }\n }\n const filled = columns\n .map((column, position) => ({ column, weight: weights[position] ?? 1 }))\n .filter((entry) => entry.column.length)\n // A footer's columns are top-aligned: each starts at its heading (AGL-3735).\n const top =\n filled.length > 1\n ? compileRow(scope, filled, 'flex-start')\n : filled.length === 1\n ? compileFlow(scope, filled[0].column, 'full')\n : null\n const copyright = tree.add(\n 'muiTypography',\n { children: `© ${name}`, variant: 'body2', component: 'p' },\n scope.band === 'brand' ? null : { color: 'text.secondary' },\n null,\n 'copyright',\n )\n const bottom = tree.add(\n 'muiBox',\n null,\n { pt: 3, borderTop: 1, borderColor: 'divider' },\n [copyright],\n 'footerBottom',\n )\n const stack = tree.add(\n 'muiStack',\n { spacing: '6' },\n null,\n [top, bottom],\n 'footerStack',\n )\n const container = tree.add(\n 'muiContainer',\n { maxWidth: 'lg' },\n { py: { xs: 6, md: 8 } },\n [stack],\n 'footerInner',\n )\n return tree.add(\n 'section',\n {\n element: 'footer',\n ariaLabel: 'Site footer',\n ...(scope.band === 'dark' ? { colorScheme: 'dark' } : {}),\n },\n scope.band === 'soft'\n ? { bgcolor: 'background.paper', borderTop: 1, borderColor: 'divider' }\n : bandSx(scope.band),\n [container],\n 'footer',\n )\n}\n"],"names":["AI_ICON_LIBRARY","AI_LAYOUT_MAX_IMAGES","bandSx","compileFlow","compileRow","destinationProps","aiLayoutFitText","aiLayoutRepairFrameHollows","AI_LAYOUT_CART_ELEMENT","AI_LAYOUT_MAX_ITEMS","AI_LAYOUT_LABELS_EN","aiLayoutLabel","aiLayoutRenamedLabel","aiLayoutResolveLink","AiLayoutTreeBuilder","AI_LAYOUT_ACCOUNT_LINK","account","AI_LAYOUT_STORE_ANNOUNCEMENT","storeAnnouncement","AI_LAYOUT_STORE_ANNOUNCEMENT_LINK","shopNow","headerLook","band","props","color","sx","boxShadow","bgcolor","aiLayoutIsCopyright","text","test","FOOTER_LEAVES_OUT","Set","frameScope","page","at","centered","index","level","headed","itemsLeft","frame","link","sections","from","formSection","aiCompileLayoutFrame","plan","targets","header","tree","storePages","storeLinks","footer","map","id","href","label","slug","title","siteName","pages","pageId","options","reusableComponents","language","settled","scrollTo","itemIds","images","strong","brand","dark","pageIcon","sparkle","formsPlaced","design","pictures","features","splitAt","quotesOnly","name","blocks","look","scope","navLink","entry","role","interactions","add","children","slice","screenId","renderAs","textDecoration","nodes","navPages","filter","trim","centeredNav","align","length","brandSx","typography","fontWeight","whiteSpace","overflow","textOverflow","minWidth","flexGrow","homeId","variant","component","nav","ariaLabel","direction","spacing","alignItems","display","xs","md","ctaBlock","find","block","kind","store","shopId","noteBlock","undefined","push","what","ctaLabel","ctaResolved","to","repeatsNav","some","ctaTo","cart","ctaProps","cta","sm","flexShrink","accountPath","accountLink","close","AI_LAYOUT_CLOSE_DRAWER","drawer","anchor","flexDirection","gap","p","fullWidth","mt","toggle","middle","justifyContent","row","maxWidth","columnGap","toolbar","disableGutters","minHeight","bar","position","overHero","announcement","said","words","useFlexGap","flexWrap","py","px","slot","compileFooter","column","prune","rootId","AI_LAYOUT_STORE_FOOTER_HEADING","customerCare","STORE_HEADING","aiLayoutFooterStoreHeading","columns","navItems","candidates","navTo","item","forEach","end","under","site","one","items","has","heading","String","empty","pick","enabled","trigger","event","everyTime","steps","type","asked","closesDark","linksPages","flatMap","given","notice","pageLinks","cols","col","Math","min","weights","list","home","splice","filled","weight","top","copyright","bottom","pt","borderTop","borderColor","stack","container","element","colorScheme"],"mappings":"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"}
|
|
@@ -38,7 +38,9 @@ import { aiStoredScrollTargets } from "../runtime/ai-page-links.js";
|
|
|
38
38
|
'collectionEntries',
|
|
39
39
|
'cart',
|
|
40
40
|
// A music site's player, placed empty for the owner's tracks (AGL-3716).
|
|
41
|
-
'musicPlayer'
|
|
41
|
+
'musicPlayer',
|
|
42
|
+
// The site's events, listed by the events-calendar plugin (AGL-3755).
|
|
43
|
+
'eventList'
|
|
42
44
|
]);
|
|
43
45
|
const HEADING_VARIANTS = new Set([
|
|
44
46
|
'h1',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-gaps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { aiBracketedFacts, type AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { aiStoredScrollTargets } from '../runtime/ai-page-links'\n\n/**\n * No gap on a published page (AGL-3660). A guided start publishes what it\n * builds, so a fact the brief never gave — a phone number, an address, the\n * opening hours — is left out of a page or a frame built in the layout\n * language, never written as the bracketed gap (\"[phone number]\") a draft a\n * person edits first may carry. An answer holding one is asked again; the\n * last answer a generation takes has each one taken out instead: the line\n * that carries it, the row or card it leaves with nothing to say, and a\n * section left with only its heading.\n */\n\ninterface GapNode {\n componentId: string\n props?: Record<string, unknown>\n nodes?: string[]\n}\n\ntype GapNodes = Record<string, GapNode>\n\n/** Props that hold no words a visitor reads: an icon's drawing is path data. */\nconst NOT_WORDS = new Set(['iconPath', 'iconId'])\n\n/** The props a node's own words are in. */\nconst WORD_PROPS = ['children', 'primary', 'secondary', 'label'] as const\n\n/** Elements that say something with no words of their own. */\nconst SPEAKS_ALONE = new Set([\n 'image',\n 'form',\n 'layoutSlot',\n 'reusableInstance',\n 'muiDrawerToggle',\n 'muiDrawer',\n // The site's own records, listed by the elements that keep them (AGL-3676).\n 'product-grid',\n 'collectionEntries',\n 'cart',\n // A music site's player, placed empty for the owner's tracks (AGL-3716).\n 'musicPlayer',\n])\n\nconst HEADING_VARIANTS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'overline'])\n\n/** The violation code a gap is named by. */\nexport const AI_LAYOUT_GAP_CODE = 'layout-gap'\n\n/** Every string a value holds, however deep, its icon drawings aside. */\nfunction stringsOf(value: unknown, key = ''): string[] {\n if (NOT_WORDS.has(key)) return []\n if (typeof value === 'string') return [value]\n if (Array.isArray(value)) return value.flatMap((entry) => stringsOf(entry))\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>).flatMap(([name, entry]) => stringsOf(entry, name))\n }\n return []\n}\n\n/** Each node whose words carry a gap, with the gaps it carries. */\nfunction gapsOf(nodes: GapNodes): Array<{ id: string; facts: string[]; text: string }> {\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = stringsOf(node.props ?? {})\n const facts = aiBracketedFacts(texts)\n return facts.length ? [{ id, facts, text: texts.filter((text) => aiBracketedFacts([text]).length).join(' ') }] : []\n })\n}\n\n/** The gaps a language-built page or frame carries, as the violation that asks again. */\nexport function aiLayoutGapViolations(nodes: NodesMap): AiDoctrineViolation[] {\n const found = gapsOf(nodes as unknown as GapNodes)\n if (!found.length) return []\n const facts = aiBracketedFacts(found.flatMap((entry) => entry.facts))\n return [\n {\n rule: 14,\n code: AI_LAYOUT_GAP_CODE,\n message: `${facts.join(', ')} ${facts.length === 1 ? 'is a gap' : 'are gaps'} for a fact the brief does not give, and this site is published exactly as written. Leave each fact out: write the sentence without it, and make no list line, card or contact row for it. Never write a gap in square brackets, and never invent the fact.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\nfunction parentsOf(nodes: GapNodes): Map<string, string> {\n const parents = new Map<string, string>()\n for (const [id, node] of Object.entries(nodes)) for (const child of node.nodes ?? []) parents.set(child, id)\n return parents\n}\n\nfunction subtree(nodes: GapNodes, id: string): string[] {\n const ids: string[] = []\n const stack = [id]\n while (stack.length) {\n const next = stack.pop() as string\n if (!nodes[next] || ids.includes(next)) continue\n ids.push(next)\n stack.push(...(nodes[next].nodes ?? []))\n }\n return ids\n}\n\nfunction wordsOf(node: GapNode): string {\n for (const prop of WORD_PROPS) {\n const value = node.props?.[prop]\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return ''\n}\n\nfunction isHeading(node: GapNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n return HEADING_VARIANTS.has(String(node.props?.['variant'] ?? '')) || /^h[1-6]$/.test(String(node.props?.['component'] ?? ''))\n}\n\n/** Whether anything under a node says something; with `beyondHeadings`, anything but a heading. */\nfunction speaks(nodes: GapNodes, id: string, beyondHeadings = false): boolean {\n return subtree(nodes, id).some((each) => {\n const node = nodes[each]\n const says = SPEAKS_ALONE.has(node.componentId) || !!wordsOf(node)\n return says && !(beyondHeadings && isHeading(node))\n })\n}\n\n/** A node's props, its component's prop values copied too, so a rewrite never reaches the input. */\nfunction copyProps(props: Record<string, unknown>): Record<string, unknown> {\n const values = props['propValues']\n return values && typeof values === 'object' && !Array.isArray(values)\n ? { ...props, propValues: { ...(values as Record<string, unknown>) } }\n : { ...props }\n}\n\nfunction cloneNodes(input: NodesMap): GapNodes {\n return Object.fromEntries(\n Object.entries(input as unknown as GapNodes).map(([id, node]) => [\n id,\n {\n ...node,\n ...(node.props ? { props: copyProps(node.props) } : {}),\n ...(node.nodes ? { nodes: [...node.nodes] } : {}),\n },\n ]),\n )\n}\n\n/**\n * Takes each found node out, and each element above it left with nothing to\n * say, up to its section; a section other than the page's first left with\n * only its heading goes too, with any link that scrolled to it.\n */\nfunction pruneFound(nodes: GapNodes, rootId: string, found: ReadonlyArray<{ id: string; text: string }>): string[] {\n const dropped: string[] = []\n const sections = new Set(nodes[rootId]?.nodes ?? [])\n const touched = new Set<string>()\n const remove = (id: string) => {\n const parent = parentsOf(nodes).get(id)\n for (const each of subtree(nodes, id)) delete nodes[each]\n if (parent && nodes[parent]) nodes[parent].nodes = (nodes[parent].nodes ?? []).filter((child) => child !== id)\n }\n /** Takes a node out, and each element above it it leaves with nothing to say, short of a section. */\n const prune = (id: string) => {\n const parents = parentsOf(nodes)\n let section: string | undefined = id\n while (section && !sections.has(section)) section = parents.get(section)\n if (section && section !== id) touched.add(section)\n let parent = parents.get(id)\n remove(id)\n while (parent && parent !== rootId && nodes[parent]) {\n if (sections.has(parent) || speaks(nodes, parent)) break\n const above = parentsOf(nodes).get(parent)\n remove(parent)\n parent = above\n }\n }\n for (const entry of found) {\n if (!nodes[entry.id]) continue\n dropped.push(entry.text)\n prune(entry.id)\n }\n const first = nodes[rootId]?.nodes?.[0]\n for (const section of touched) {\n if (section === first || !nodes[section] || speaks(nodes, section, true)) continue\n const label = String(nodes[section].props?.['ariaLabel'] ?? section)\n remove(section)\n dropped.push(`the section \"${label}\", left with only its heading`)\n for (const [id, node] of Object.entries(nodes)) {\n if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {\n dropped.push(`\"${wordsOf(node)}\", a link to that section`)\n prune(id)\n }\n }\n }\n return dropped\n}\n\n/** The page or frame with every gap taken out, and what was taken. */\nexport function aiLayoutWithoutGaps(input: NodesMap, rootId: string): { nodes: NodesMap; dropped: string[] } {\n const found = gapsOf(input as unknown as GapNodes)\n if (!found.length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const dropped = pruneFound(nodes, rootId, found)\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n// ── Internal references ─────────────────────────────────────────────────\n\n/**\n * What a link may name, which never belongs in words a visitor reads: the\n * site's pages, with the title a reference to one is written as, and every\n * other id the page or frame was built against (its forms, components and\n * sections).\n */\nexport interface AiLayoutRefNames {\n pages: ReadonlyArray<{ id: string; label: string }>\n ids: readonly string[]\n}\n\n/** The violation code an internal reference in copy is named by. */\nexport const AI_LAYOUT_REF_CODE = 'layout-internal-ref'\n\n/** A link's own notation, or a template token other than the site's own (`{{host.…}}`). */\nconst REF = /\\b(?:page|new|form|component|screen):[A-Za-z0-9_-]+|\\{\\{(?!\\s*host\\.)[^{}]*\\}\\}/g\nconst PAGE_REF = /\\bpage:([A-Za-z0-9_-]+)/g\n\n/** The props whose words a visitor reads, and a component's prop values. */\nconst SHOWN_PROPS = [...WORD_PROPS, 'alt', 'title']\n\nfunction shownTexts(node: GapNode): Array<{ get: () => string; set: (value: string) => void }> {\n const props = node.props ?? {}\n const slots: Array<{ get: () => string; set: (value: string) => void }> = []\n for (const prop of SHOWN_PROPS) {\n if (typeof props[prop] === 'string') slots.push({ get: () => props[prop] as string, set: (value) => (props[prop] = value) })\n }\n const values = props['propValues']\n if (values && typeof values === 'object' && !Array.isArray(values)) {\n const record = values as Record<string, unknown>\n for (const name of Object.keys(record)) {\n if (typeof record[name] === 'string') slots.push({ get: () => record[name] as string, set: (value) => (record[name] = value) })\n }\n }\n return slots\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction refsIn(text: string, names: AiLayoutRefNames): string[] {\n const found = (text.match(REF) ?? []).map((ref) => ref.trim())\n for (const id of names.ids) {\n if (id.length >= 6 && new RegExp(`(^|[^A-Za-z0-9_-])${escapeRegExp(id)}($|[^A-Za-z0-9_-])`).test(text)) found.push(id)\n }\n return [...new Set(found)]\n}\n\n/** An entry's own token, which a collection repeater fills per entry when the page renders. */\nconst ENTRY_TOKEN = /\\{\\{\\s*entry\\.[A-Za-z]+\\s*\\}\\}/g\n\n/** Every node under a Collection Entries block: its template, repeated once per entry (AGL-3676). */\nfunction repeatedByEntries(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (node.componentId !== 'collectionEntries') continue\n for (const each of subtree(nodes, id)) if (each !== id) repeated.add(each)\n }\n return repeated\n}\n\n/** A record's own token, which a repeat over a dataset fills per record when the page renders. */\nconst ITEM_TOKEN = /\\{\\{\\s*item\\.[A-Za-z][A-Za-z0-9_]*(?:\\.[A-Za-z][A-Za-z0-9_]*)?\\s*\\}\\}/g\n\n/** Every node a repeat over a dataset copies once per record (AGL-3616), the repeating node included. */\nfunction repeatedByDataset(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n const key = node.props?.['repeatDataset']\n if (typeof key !== 'string' || !key.trim()) continue\n for (const each of subtree(nodes, id)) repeated.add(each)\n }\n return repeated\n}\n\nfunction refsOf(nodes: GapNodes, names: AiLayoutRefNames): Array<{ id: string; refs: string[]; text: string }> {\n // A post's card names its post by the entry's tokens, which the page fills\n // per post: words a visitor reads as the post's own, never a reference.\n // A dataset's card names its record by the record's, the same way (AGL-3616).\n const repeated = repeatedByEntries(nodes)\n const records = repeatedByDataset(nodes)\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = shownTexts(node).map((slot) => {\n const text = repeated.has(id) ? slot.get().replace(ENTRY_TOKEN, '') : slot.get()\n return records.has(id) ? text.replace(ITEM_TOKEN, '') : text\n })\n const refs = [...new Set(texts.flatMap((text) => refsIn(text, names)))]\n return refs.length ? [{ id, refs, text: texts.filter((text) => refsIn(text, names).length).join(' ') }] : []\n })\n}\n\n/** Internal references in a language-built page's or frame's words, as the violation that asks again. */\nexport function aiLayoutRefViolations(nodes: NodesMap, names: AiLayoutRefNames): AiDoctrineViolation[] {\n const found = refsOf(nodes as unknown as GapNodes, names)\n if (!found.length) return []\n const refs = [...new Set(found.flatMap((entry) => entry.refs))].map((ref) => `\"${ref}\"`)\n return [\n {\n rule: 14,\n code: AI_LAYOUT_REF_CODE,\n message: `${refs.join(', ')} ${refs.length === 1 ? 'is a link reference' : 'are link references'} written into words a visitor reads. A reference goes only in a button's or a list item's to; in text, name the page by its title, or leave the reference out.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\n/**\n * The page or frame with no internal reference in its words: a page's\n * reference is written as that page's title, and a line that still names a\n * reference is taken out, as a gap is. Returns what was rewritten and taken.\n */\nexport function aiLayoutWithoutRefs(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n): { nodes: NodesMap; dropped: string[] } {\n if (!refsOf(input as unknown as GapNodes, names).length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const labels = new Map(names.pages.map((page) => [page.id, page.label]))\n const dropped: string[] = []\n for (const node of Object.values(nodes)) {\n for (const slot of shownTexts(node)) {\n const before = slot.get()\n const after = before.replace(PAGE_REF, (ref, id: string) => labels.get(id) ?? ref)\n if (after !== before) {\n slot.set(after)\n dropped.push(`\"${before}\" written as \"${after}\"`)\n }\n }\n }\n dropped.push(...pruneFound(nodes, rootId, refsOf(nodes, names)))\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n/**\n * A language-built page's or frame's words held to both (AGL-3660): before\n * the last answer, each gap and each internal reference is a violation that\n * asks again; in the last, they are taken out and the rest is kept.\n */\nexport function aiLayoutCopyCheck(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n last: boolean,\n): { nodes: NodesMap; dropped: string[]; violations: AiDoctrineViolation[] } {\n if (!last) {\n return { nodes: input, dropped: [], violations: [...aiLayoutRefViolations(input, names), ...aiLayoutGapViolations(input)] }\n }\n const refs = aiLayoutWithoutRefs(input, rootId, names)\n const gaps = aiLayoutWithoutGaps(refs.nodes, rootId)\n return { nodes: gaps.nodes, dropped: [...refs.dropped, ...gaps.dropped], violations: [] }\n}\n"],"names":["aiBracketedFacts","aiStoredScrollTargets","NOT_WORDS","Set","WORD_PROPS","SPEAKS_ALONE","HEADING_VARIANTS","AI_LAYOUT_GAP_CODE","stringsOf","value","key","has","Array","isArray","flatMap","entry","Object","entries","name","gapsOf","nodes","id","node","texts","props","facts","length","text","filter","join","aiLayoutGapViolations","found","rule","code","message","nodeIds","map","parentsOf","parents","Map","child","set","subtree","ids","stack","next","pop","includes","push","wordsOf","prop","trim","isHeading","componentId","String","test","speaks","beyondHeadings","some","each","says","copyProps","values","propValues","cloneNodes","input","fromEntries","pruneFound","rootId","dropped","sections","touched","remove","parent","get","prune","section","add","above","first","label","aiLayoutWithoutGaps","AI_LAYOUT_REF_CODE","REF","PAGE_REF","SHOWN_PROPS","shownTexts","slots","record","keys","escapeRegExp","replace","refsIn","names","match","ref","RegExp","ENTRY_TOKEN","repeatedByEntries","repeated","ITEM_TOKEN","repeatedByDataset","refsOf","records","slot","refs","aiLayoutRefViolations","aiLayoutWithoutRefs","labels","pages","page","before","after","aiLayoutCopyCheck","last","violations","gaps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-gaps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { aiBracketedFacts, type AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { aiStoredScrollTargets } from '../runtime/ai-page-links'\n\n/**\n * No gap on a published page (AGL-3660). A guided start publishes what it\n * builds, so a fact the brief never gave — a phone number, an address, the\n * opening hours — is left out of a page or a frame built in the layout\n * language, never written as the bracketed gap (\"[phone number]\") a draft a\n * person edits first may carry. An answer holding one is asked again; the\n * last answer a generation takes has each one taken out instead: the line\n * that carries it, the row or card it leaves with nothing to say, and a\n * section left with only its heading.\n */\n\ninterface GapNode {\n componentId: string\n props?: Record<string, unknown>\n nodes?: string[]\n}\n\ntype GapNodes = Record<string, GapNode>\n\n/** Props that hold no words a visitor reads: an icon's drawing is path data. */\nconst NOT_WORDS = new Set(['iconPath', 'iconId'])\n\n/** The props a node's own words are in. */\nconst WORD_PROPS = ['children', 'primary', 'secondary', 'label'] as const\n\n/** Elements that say something with no words of their own. */\nconst SPEAKS_ALONE = new Set([\n 'image',\n 'form',\n 'layoutSlot',\n 'reusableInstance',\n 'muiDrawerToggle',\n 'muiDrawer',\n // The site's own records, listed by the elements that keep them (AGL-3676).\n 'product-grid',\n 'collectionEntries',\n 'cart',\n // A music site's player, placed empty for the owner's tracks (AGL-3716).\n 'musicPlayer',\n // The site's events, listed by the events-calendar plugin (AGL-3755).\n 'eventList',\n])\n\nconst HEADING_VARIANTS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'overline'])\n\n/** The violation code a gap is named by. */\nexport const AI_LAYOUT_GAP_CODE = 'layout-gap'\n\n/** Every string a value holds, however deep, its icon drawings aside. */\nfunction stringsOf(value: unknown, key = ''): string[] {\n if (NOT_WORDS.has(key)) return []\n if (typeof value === 'string') return [value]\n if (Array.isArray(value)) return value.flatMap((entry) => stringsOf(entry))\n if (value && typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>).flatMap(([name, entry]) => stringsOf(entry, name))\n }\n return []\n}\n\n/** Each node whose words carry a gap, with the gaps it carries. */\nfunction gapsOf(nodes: GapNodes): Array<{ id: string; facts: string[]; text: string }> {\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = stringsOf(node.props ?? {})\n const facts = aiBracketedFacts(texts)\n return facts.length ? [{ id, facts, text: texts.filter((text) => aiBracketedFacts([text]).length).join(' ') }] : []\n })\n}\n\n/** The gaps a language-built page or frame carries, as the violation that asks again. */\nexport function aiLayoutGapViolations(nodes: NodesMap): AiDoctrineViolation[] {\n const found = gapsOf(nodes as unknown as GapNodes)\n if (!found.length) return []\n const facts = aiBracketedFacts(found.flatMap((entry) => entry.facts))\n return [\n {\n rule: 14,\n code: AI_LAYOUT_GAP_CODE,\n message: `${facts.join(', ')} ${facts.length === 1 ? 'is a gap' : 'are gaps'} for a fact the brief does not give, and this site is published exactly as written. Leave each fact out: write the sentence without it, and make no list line, card or contact row for it. Never write a gap in square brackets, and never invent the fact.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\nfunction parentsOf(nodes: GapNodes): Map<string, string> {\n const parents = new Map<string, string>()\n for (const [id, node] of Object.entries(nodes)) for (const child of node.nodes ?? []) parents.set(child, id)\n return parents\n}\n\nfunction subtree(nodes: GapNodes, id: string): string[] {\n const ids: string[] = []\n const stack = [id]\n while (stack.length) {\n const next = stack.pop() as string\n if (!nodes[next] || ids.includes(next)) continue\n ids.push(next)\n stack.push(...(nodes[next].nodes ?? []))\n }\n return ids\n}\n\nfunction wordsOf(node: GapNode): string {\n for (const prop of WORD_PROPS) {\n const value = node.props?.[prop]\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return ''\n}\n\nfunction isHeading(node: GapNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n return HEADING_VARIANTS.has(String(node.props?.['variant'] ?? '')) || /^h[1-6]$/.test(String(node.props?.['component'] ?? ''))\n}\n\n/** Whether anything under a node says something; with `beyondHeadings`, anything but a heading. */\nfunction speaks(nodes: GapNodes, id: string, beyondHeadings = false): boolean {\n return subtree(nodes, id).some((each) => {\n const node = nodes[each]\n const says = SPEAKS_ALONE.has(node.componentId) || !!wordsOf(node)\n return says && !(beyondHeadings && isHeading(node))\n })\n}\n\n/** A node's props, its component's prop values copied too, so a rewrite never reaches the input. */\nfunction copyProps(props: Record<string, unknown>): Record<string, unknown> {\n const values = props['propValues']\n return values && typeof values === 'object' && !Array.isArray(values)\n ? { ...props, propValues: { ...(values as Record<string, unknown>) } }\n : { ...props }\n}\n\nfunction cloneNodes(input: NodesMap): GapNodes {\n return Object.fromEntries(\n Object.entries(input as unknown as GapNodes).map(([id, node]) => [\n id,\n {\n ...node,\n ...(node.props ? { props: copyProps(node.props) } : {}),\n ...(node.nodes ? { nodes: [...node.nodes] } : {}),\n },\n ]),\n )\n}\n\n/**\n * Takes each found node out, and each element above it left with nothing to\n * say, up to its section; a section other than the page's first left with\n * only its heading goes too, with any link that scrolled to it.\n */\nfunction pruneFound(nodes: GapNodes, rootId: string, found: ReadonlyArray<{ id: string; text: string }>): string[] {\n const dropped: string[] = []\n const sections = new Set(nodes[rootId]?.nodes ?? [])\n const touched = new Set<string>()\n const remove = (id: string) => {\n const parent = parentsOf(nodes).get(id)\n for (const each of subtree(nodes, id)) delete nodes[each]\n if (parent && nodes[parent]) nodes[parent].nodes = (nodes[parent].nodes ?? []).filter((child) => child !== id)\n }\n /** Takes a node out, and each element above it it leaves with nothing to say, short of a section. */\n const prune = (id: string) => {\n const parents = parentsOf(nodes)\n let section: string | undefined = id\n while (section && !sections.has(section)) section = parents.get(section)\n if (section && section !== id) touched.add(section)\n let parent = parents.get(id)\n remove(id)\n while (parent && parent !== rootId && nodes[parent]) {\n if (sections.has(parent) || speaks(nodes, parent)) break\n const above = parentsOf(nodes).get(parent)\n remove(parent)\n parent = above\n }\n }\n for (const entry of found) {\n if (!nodes[entry.id]) continue\n dropped.push(entry.text)\n prune(entry.id)\n }\n const first = nodes[rootId]?.nodes?.[0]\n for (const section of touched) {\n if (section === first || !nodes[section] || speaks(nodes, section, true)) continue\n const label = String(nodes[section].props?.['ariaLabel'] ?? section)\n remove(section)\n dropped.push(`the section \"${label}\", left with only its heading`)\n for (const [id, node] of Object.entries(nodes)) {\n if (nodes[id] && aiStoredScrollTargets(node).includes(section)) {\n dropped.push(`\"${wordsOf(node)}\", a link to that section`)\n prune(id)\n }\n }\n }\n return dropped\n}\n\n/** The page or frame with every gap taken out, and what was taken. */\nexport function aiLayoutWithoutGaps(input: NodesMap, rootId: string): { nodes: NodesMap; dropped: string[] } {\n const found = gapsOf(input as unknown as GapNodes)\n if (!found.length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const dropped = pruneFound(nodes, rootId, found)\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n// ── Internal references ─────────────────────────────────────────────────\n\n/**\n * What a link may name, which never belongs in words a visitor reads: the\n * site's pages, with the title a reference to one is written as, and every\n * other id the page or frame was built against (its forms, components and\n * sections).\n */\nexport interface AiLayoutRefNames {\n pages: ReadonlyArray<{ id: string; label: string }>\n ids: readonly string[]\n}\n\n/** The violation code an internal reference in copy is named by. */\nexport const AI_LAYOUT_REF_CODE = 'layout-internal-ref'\n\n/** A link's own notation, or a template token other than the site's own (`{{host.…}}`). */\nconst REF = /\\b(?:page|new|form|component|screen):[A-Za-z0-9_-]+|\\{\\{(?!\\s*host\\.)[^{}]*\\}\\}/g\nconst PAGE_REF = /\\bpage:([A-Za-z0-9_-]+)/g\n\n/** The props whose words a visitor reads, and a component's prop values. */\nconst SHOWN_PROPS = [...WORD_PROPS, 'alt', 'title']\n\nfunction shownTexts(node: GapNode): Array<{ get: () => string; set: (value: string) => void }> {\n const props = node.props ?? {}\n const slots: Array<{ get: () => string; set: (value: string) => void }> = []\n for (const prop of SHOWN_PROPS) {\n if (typeof props[prop] === 'string') slots.push({ get: () => props[prop] as string, set: (value) => (props[prop] = value) })\n }\n const values = props['propValues']\n if (values && typeof values === 'object' && !Array.isArray(values)) {\n const record = values as Record<string, unknown>\n for (const name of Object.keys(record)) {\n if (typeof record[name] === 'string') slots.push({ get: () => record[name] as string, set: (value) => (record[name] = value) })\n }\n }\n return slots\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction refsIn(text: string, names: AiLayoutRefNames): string[] {\n const found = (text.match(REF) ?? []).map((ref) => ref.trim())\n for (const id of names.ids) {\n if (id.length >= 6 && new RegExp(`(^|[^A-Za-z0-9_-])${escapeRegExp(id)}($|[^A-Za-z0-9_-])`).test(text)) found.push(id)\n }\n return [...new Set(found)]\n}\n\n/** An entry's own token, which a collection repeater fills per entry when the page renders. */\nconst ENTRY_TOKEN = /\\{\\{\\s*entry\\.[A-Za-z]+\\s*\\}\\}/g\n\n/** Every node under a Collection Entries block: its template, repeated once per entry (AGL-3676). */\nfunction repeatedByEntries(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (node.componentId !== 'collectionEntries') continue\n for (const each of subtree(nodes, id)) if (each !== id) repeated.add(each)\n }\n return repeated\n}\n\n/** A record's own token, which a repeat over a dataset fills per record when the page renders. */\nconst ITEM_TOKEN = /\\{\\{\\s*item\\.[A-Za-z][A-Za-z0-9_]*(?:\\.[A-Za-z][A-Za-z0-9_]*)?\\s*\\}\\}/g\n\n/** Every node a repeat over a dataset copies once per record (AGL-3616), the repeating node included. */\nfunction repeatedByDataset(nodes: GapNodes): Set<string> {\n const repeated = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n const key = node.props?.['repeatDataset']\n if (typeof key !== 'string' || !key.trim()) continue\n for (const each of subtree(nodes, id)) repeated.add(each)\n }\n return repeated\n}\n\nfunction refsOf(nodes: GapNodes, names: AiLayoutRefNames): Array<{ id: string; refs: string[]; text: string }> {\n // A post's card names its post by the entry's tokens, which the page fills\n // per post: words a visitor reads as the post's own, never a reference.\n // A dataset's card names its record by the record's, the same way (AGL-3616).\n const repeated = repeatedByEntries(nodes)\n const records = repeatedByDataset(nodes)\n return Object.entries(nodes).flatMap(([id, node]) => {\n const texts = shownTexts(node).map((slot) => {\n const text = repeated.has(id) ? slot.get().replace(ENTRY_TOKEN, '') : slot.get()\n return records.has(id) ? text.replace(ITEM_TOKEN, '') : text\n })\n const refs = [...new Set(texts.flatMap((text) => refsIn(text, names)))]\n return refs.length ? [{ id, refs, text: texts.filter((text) => refsIn(text, names).length).join(' ') }] : []\n })\n}\n\n/** Internal references in a language-built page's or frame's words, as the violation that asks again. */\nexport function aiLayoutRefViolations(nodes: NodesMap, names: AiLayoutRefNames): AiDoctrineViolation[] {\n const found = refsOf(nodes as unknown as GapNodes, names)\n if (!found.length) return []\n const refs = [...new Set(found.flatMap((entry) => entry.refs))].map((ref) => `\"${ref}\"`)\n return [\n {\n rule: 14,\n code: AI_LAYOUT_REF_CODE,\n message: `${refs.join(', ')} ${refs.length === 1 ? 'is a link reference' : 'are link references'} written into words a visitor reads. A reference goes only in a button's or a list item's to; in text, name the page by its title, or leave the reference out.`,\n nodeIds: found.map((entry) => entry.id),\n },\n ]\n}\n\n/**\n * The page or frame with no internal reference in its words: a page's\n * reference is written as that page's title, and a line that still names a\n * reference is taken out, as a gap is. Returns what was rewritten and taken.\n */\nexport function aiLayoutWithoutRefs(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n): { nodes: NodesMap; dropped: string[] } {\n if (!refsOf(input as unknown as GapNodes, names).length) return { nodes: input, dropped: [] }\n const nodes = cloneNodes(input)\n const labels = new Map(names.pages.map((page) => [page.id, page.label]))\n const dropped: string[] = []\n for (const node of Object.values(nodes)) {\n for (const slot of shownTexts(node)) {\n const before = slot.get()\n const after = before.replace(PAGE_REF, (ref, id: string) => labels.get(id) ?? ref)\n if (after !== before) {\n slot.set(after)\n dropped.push(`\"${before}\" written as \"${after}\"`)\n }\n }\n }\n dropped.push(...pruneFound(nodes, rootId, refsOf(nodes, names)))\n return { nodes: nodes as unknown as NodesMap, dropped }\n}\n\n/**\n * A language-built page's or frame's words held to both (AGL-3660): before\n * the last answer, each gap and each internal reference is a violation that\n * asks again; in the last, they are taken out and the rest is kept.\n */\nexport function aiLayoutCopyCheck(\n input: NodesMap,\n rootId: string,\n names: AiLayoutRefNames,\n last: boolean,\n): { nodes: NodesMap; dropped: string[]; violations: AiDoctrineViolation[] } {\n if (!last) {\n return { nodes: input, dropped: [], violations: [...aiLayoutRefViolations(input, names), ...aiLayoutGapViolations(input)] }\n }\n const refs = aiLayoutWithoutRefs(input, rootId, names)\n const gaps = aiLayoutWithoutGaps(refs.nodes, rootId)\n return { nodes: gaps.nodes, dropped: [...refs.dropped, ...gaps.dropped], violations: [] }\n}\n"],"names":["aiBracketedFacts","aiStoredScrollTargets","NOT_WORDS","Set","WORD_PROPS","SPEAKS_ALONE","HEADING_VARIANTS","AI_LAYOUT_GAP_CODE","stringsOf","value","key","has","Array","isArray","flatMap","entry","Object","entries","name","gapsOf","nodes","id","node","texts","props","facts","length","text","filter","join","aiLayoutGapViolations","found","rule","code","message","nodeIds","map","parentsOf","parents","Map","child","set","subtree","ids","stack","next","pop","includes","push","wordsOf","prop","trim","isHeading","componentId","String","test","speaks","beyondHeadings","some","each","says","copyProps","values","propValues","cloneNodes","input","fromEntries","pruneFound","rootId","dropped","sections","touched","remove","parent","get","prune","section","add","above","first","label","aiLayoutWithoutGaps","AI_LAYOUT_REF_CODE","REF","PAGE_REF","SHOWN_PROPS","shownTexts","slots","record","keys","escapeRegExp","replace","refsIn","names","match","ref","RegExp","ENTRY_TOKEN","repeatedByEntries","repeated","ITEM_TOKEN","repeatedByDataset","refsOf","records","slot","refs","aiLayoutRefViolations","aiLayoutWithoutRefs","labels","pages","page","before","after","aiLayoutCopyCheck","last","violations","gaps"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
* THE WORDS THE PLATFORM WRITES ON A PAGE ITSELF, IN THE SITE'S LANGUAGE (AGL-3760).
|
|
19
|
+
*
|
|
20
|
+
* The model writes a page's copy in the language its brief is in; a few
|
|
21
|
+
* labels are the compiler's own — the button under a featured list that opens
|
|
22
|
+
* the whole of it, a post card's link, a store's fixed lines. A Spanish
|
|
23
|
+
* guided start (beta.246) showed "See all" under "Lo que horneamos". Each
|
|
24
|
+
* label is English, then the same words in the languages below; a site in a
|
|
25
|
+
* language not listed keeps the English.
|
|
26
|
+
*/
|
|
27
|
+
/** The labels the compiler and the frame place, in English. */
|
|
28
|
+
export declare const AI_LAYOUT_LABELS_EN: {
|
|
29
|
+
/** The button under a featured band of the site's records that opens the whole list. */
|
|
30
|
+
readonly seeAll: "See all";
|
|
31
|
+
/** The same, for the blog's posts. */
|
|
32
|
+
readonly allPosts: "All posts";
|
|
33
|
+
/** The same, for the site's music. */
|
|
34
|
+
readonly allMusic: "All music";
|
|
35
|
+
/** The same, for the site's events. */
|
|
36
|
+
readonly allEvents: "All events";
|
|
37
|
+
/** The button a storefront's hero, or a featured band of products, opens the shop with. */
|
|
38
|
+
readonly shopAll: "Shop all";
|
|
39
|
+
/** A post card's link to the post. */
|
|
40
|
+
readonly readPost: "Read the post";
|
|
41
|
+
/** A record card's link to the record's own page. */
|
|
42
|
+
readonly viewDetails: "View details";
|
|
43
|
+
/** A store's newsletter sign-up button. */
|
|
44
|
+
readonly subscribe: "Subscribe";
|
|
45
|
+
/** A store's reviews band, where neither the design nor the plan heads it. */
|
|
46
|
+
readonly reviewsHeading: "What our customers say";
|
|
47
|
+
/** A store's sign-up band, where neither the design nor the plan heads it. */
|
|
48
|
+
readonly signupHeading: "Join our newsletter";
|
|
49
|
+
/** The header's link to a store's account page. */
|
|
50
|
+
readonly account: "Account";
|
|
51
|
+
/** The footer's heading over a store's account and policy links. */
|
|
52
|
+
readonly customerCare: "Customer care";
|
|
53
|
+
/** A store's announcement bar, where its design gave it no line. */
|
|
54
|
+
readonly storeAnnouncement: "Shop the full collection";
|
|
55
|
+
/** The way into the shop beside the design's own announcement. */
|
|
56
|
+
readonly shopNow: "Shop now";
|
|
57
|
+
/** A store's Product grid while the store lists nothing yet. */
|
|
58
|
+
readonly storeEmptyTitle: "New pieces are on the way";
|
|
59
|
+
readonly storeEmptyText: "Our first pieces are being finished now. Get in touch and we will let you know the moment they land.";
|
|
60
|
+
readonly storeEmptyTextAlone: "Our first pieces are being finished now. Check back soon.";
|
|
61
|
+
/** The way to get in touch under that empty grid, where the site has a page for it. */
|
|
62
|
+
readonly getInTouch: "Get in touch";
|
|
63
|
+
};
|
|
64
|
+
export type AiLayoutLabel = keyof typeof AI_LAYOUT_LABELS_EN;
|
|
65
|
+
/** A language code's two-letter language, lowercase: `es` of `es-MX`; empty for none. */
|
|
66
|
+
export declare function aiLayoutLanguageOf(language: string | null | undefined): string;
|
|
67
|
+
/** Whether a site's language is one other than English: a code that names one. */
|
|
68
|
+
export declare function aiLayoutSpeaksOtherThanEnglish(language: string | null | undefined): boolean;
|
|
69
|
+
/** A label the platform places, in the site's language where it is listed, else in English. */
|
|
70
|
+
export declare function aiLayoutLabel(key: AiLayoutLabel, language?: string | null): string;
|