@aglyn/plugins-ai 1.0.0-beta.285 → 1.0.0-beta.288
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/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/components/ai-theme-proposal-card.component.js +12 -3
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/declarations.js +5 -2
- package/src/lib/declarations.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.d.ts +3 -1
- package/src/lib/jobs/ai-build-capabilities.js +27 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +6 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-image-reads.d.ts +59 -0
- package/src/lib/jobs/ai-image-reads.js +138 -0
- package/src/lib/jobs/ai-image-reads.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.d.ts +29 -0
- package/src/lib/jobs/ai-job-build-step.js +83 -12
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-duplicate-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-duplicate-step.js +6 -6
- package/src/lib/jobs/ai-job-duplicate-step.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +4 -2
- package/src/lib/jobs/ai-job-import-step.js +15 -11
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.d.ts +10 -0
- package/src/lib/jobs/ai-job-insight-step.js +23 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.js +2 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-media-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-media-step.js +6 -8
- package/src/lib/jobs/ai-job-media-step.js.map +1 -1
- package/src/lib/jobs/ai-job-navigation-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-navigation-step.js +7 -9
- package/src/lib/jobs/ai-job-navigation-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.js +7 -1
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +16 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +9 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +30 -8
- package/src/lib/jobs/ai-job-plan-step.js +78 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-privacy-step.d.ts +4 -2
- package/src/lib/jobs/ai-job-privacy-step.js +14 -15
- package/src/lib/jobs/ai-job-privacy-step.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.js +5 -3
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-settings-step.js +7 -8
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +34 -1
- package/src/lib/jobs/ai-job-site-content.js +67 -8
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +53 -3
- package/src/lib/jobs/ai-job-site-datasets.js +300 -30
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +6 -3
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-store-pages.js +3 -3
- package/src/lib/jobs/ai-job-site-store-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.js +2 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-tracking-step.js +7 -8
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-beat.d.ts +11 -7
- package/src/lib/jobs/ai-jobs-beat.js +18 -8
- package/src/lib/jobs/ai-jobs-beat.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +66 -1
- package/src/lib/jobs/ai-jobs-notify.js +136 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +155 -7
- package/src/lib/jobs/ai-layout-stock-photos.js +1517 -220
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-product-image.d.ts +14 -0
- package/src/lib/jobs/ai-product-image.js +73 -21
- package/src/lib/jobs/ai-product-image.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +9 -1
- package/src/lib/jobs/ai-theme-brand-inputs.js +28 -2
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.d.ts +175 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.js +601 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.d.ts +46 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.js +2862 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.d.ts +74 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.js +888 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.d.ts +120 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.js +410 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.d.ts +29 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.js +81 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.d.ts +87 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.js +225 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.d.ts +26 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.js +714 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +11 -0
- package/src/lib/layout-language/ai-layout-compiler.js +194 -18
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-copy.d.ts +37 -0
- package/src/lib/layout-language/ai-layout-copy.js +383 -3
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +19 -0
- package/src/lib/layout-language/ai-layout-design.js +31 -11
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +7 -1
- package/src/lib/layout-language/ai-layout-listings.js +9 -1
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +39 -1
- package/src/lib/layout-language/ai-layout-pictures.js +141 -4
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +18 -4
- package/src/lib/layout-language/ai-layout-storefront.js +100 -20
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +7 -0
- package/src/lib/model/ai-build-job.js +36 -7
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +28 -4
- package/src/lib/model/ai-build-plan.js +49 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.js +3 -1
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +8 -0
- package/src/lib/model/ai-job-notice.js +33 -0
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-job-plan-lines.d.ts +2 -1
- package/src/lib/model/ai-job-plan-lines.js +6 -1
- package/src/lib/model/ai-job-plan-lines.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +8 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-permissions.d.ts +13 -0
- package/src/lib/model/ai-permissions.js +29 -1
- package/src/lib/model/ai-permissions.js.map +1 -1
- package/src/lib/model/ai-plan-said-sections.d.ts +12 -3
- package/src/lib/model/ai-plan-said-sections.js +22 -5
- package/src/lib/model/ai-plan-said-sections.js.map +1 -1
- package/src/lib/model/ai-products.d.ts +18 -0
- package/src/lib/model/ai-products.js +32 -0
- package/src/lib/model/ai-products.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +6 -2
- package/src/lib/model/ai-site-context.js +8 -2
- package/src/lib/model/ai-site-context.js.map +1 -1
- package/src/lib/model/ai-site-inspiration.d.ts +3 -2
- package/src/lib/model/ai-site-inspiration.js +3 -2
- package/src/lib/model/ai-site-inspiration.js.map +1 -1
- package/src/lib/plugin.js +4 -13
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.js +9 -2
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.js +14 -5
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +24 -1
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/routing.js +5 -2
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +120 -1
- package/src/lib/runtime/ai-dataset-generation.js +352 -3
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-dev-replay.d.ts +1 -0
- package/src/lib/runtime/ai-dev-replay.js +4 -2
- package/src/lib/runtime/ai-dev-replay.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +47 -3
- package/src/lib/runtime/ai-doctrine-validators.js +345 -28
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +5 -4
- package/src/lib/runtime/ai-doctrine.js +10 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +27 -1
- package/src/lib/runtime/ai-icon-library.js +30 -3
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-products-generation.d.ts +11 -1
- package/src/lib/runtime/ai-products-generation.js +51 -4
- package/src/lib/runtime/ai-products-generation.js.map +1 -1
- package/src/lib/runtime/assist-answer-cache.d.ts +2 -0
- package/src/lib/runtime/assist-answer-cache.js +5 -2
- package/src/lib/runtime/assist-answer-cache.js.map +1 -1
- package/src/lib/server/ai-allotments.js +7 -2
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-experiment-versions.js +5 -5
- package/src/lib/server/ai-experiment-versions.js.map +1 -1
- package/src/lib/server/ai-host-permissions-roster.d.ts +25 -0
- package/src/lib/server/ai-host-permissions-roster.js +33 -0
- package/src/lib/server/ai-host-permissions-roster.js.map +1 -0
- package/src/lib/server/ai-host-permissions.js +8 -6
- package/src/lib/server/ai-host-permissions.js.map +1 -1
- package/src/lib/server/ai-image-reads-route.d.ts +17 -0
- package/src/lib/server/ai-image-reads-route.js +106 -0
- package/src/lib/server/ai-image-reads-route.js.map +1 -0
- package/src/lib/server/ai-seo-apply.js +5 -5
- package/src/lib/server/ai-seo-apply.js.map +1 -1
- package/src/lib/server/ai-site-import-route.js +7 -4
- package/src/lib/server/ai-site-import-route.js.map +1 -1
- package/src/lib/server/assist-build.d.ts +15 -1
- package/src/lib/server/assist-build.js +30 -3
- package/src/lib/server/assist-build.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +11 -7
- package/src/lib/server/assist-chat.js +185 -48
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-docs-query.d.ts +51 -0
- package/src/lib/server/assist-docs-query.js +57 -0
- package/src/lib/server/assist-docs-query.js.map +1 -0
- package/src/lib/server/assist-jobs-context.d.ts +102 -0
- package/src/lib/server/assist-jobs-context.js +227 -0
- package/src/lib/server/assist-jobs-context.js.map +1 -0
- package/src/lib/server/assist-view-context.d.ts +8 -4
- package/src/lib/server/assist-view-context.js +12 -7
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +12 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +11 -1
- package/src/lib/tools/ai-dataset-tool.js +24 -7
- package/src/lib/tools/ai-dataset-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +5 -6
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/components/ai-collaborator-permissions-column.component.d.ts +0 -32
- package/src/lib/components/ai-collaborator-permissions-column.component.js +0 -132
- package/src/lib/components/ai-collaborator-permissions-column.component.js.map +0 -1
|
@@ -30,7 +30,7 @@ import { aiFreeSiteSectionsWithin, aiSiteHomeMinSections, aiSitePlanIsHome, aiSi
|
|
|
30
30
|
import { aiLayoutIsShopPage, aiLayoutShopGridSection } from "../layout-language/ai-layout-listings.js";
|
|
31
31
|
import { aiStorefrontSays } from "../layout-language/ai-layout-storefront.js";
|
|
32
32
|
import { AI_INSTANCE_REF_PROP, validateAiNodeTree } from "./ai-node-tree.js";
|
|
33
|
-
import { AI_PAGE_SCROLL_TO_PROP, aiPageLinkTarget, aiStoredScrollTargets } from "./ai-page-links.js";
|
|
33
|
+
import { AI_PAGE_SCROLL_TO_PROP, aiPageLinkTarget, aiStoredScrollTargets, isAiFragmentHref } from "./ai-page-links.js";
|
|
34
34
|
import { AI_EMAIL_CLIP_BYTES, AI_OUTPUT_BUDGETS, AI_OUTPUT_SURFACE, AI_TEXT_LIMITS, isHeadlineVariant } from "./ai-palette.js";
|
|
35
35
|
import { AI_PALETTE, AI_SX_TOKENS } from "./ai-palette.generated.js";
|
|
36
36
|
import { aiIsOnlyPlaceholders, aiPlaceholderTokens } from "./ai-placeholder-tokens.js";
|
|
@@ -2151,6 +2151,162 @@ const GRID = 'muiGrid';
|
|
|
2151
2151
|
}
|
|
2152
2152
|
return violations;
|
|
2153
2153
|
}
|
|
2154
|
+
/** The longest send label a Form takes, as its palette entry bounds `submitLabel`. */ const FORM_SUBMIT_LABEL_MAX = 40;
|
|
2155
|
+
/** The frames a link may be the last thing in, taken out with it rather than left empty. */ const LINK_FRAMES = new Set([
|
|
2156
|
+
'muiBox',
|
|
2157
|
+
'muiStack',
|
|
2158
|
+
'muiContainer',
|
|
2159
|
+
'muiGrid',
|
|
2160
|
+
'section',
|
|
2161
|
+
'div',
|
|
2162
|
+
'muiCardActions'
|
|
2163
|
+
]);
|
|
2164
|
+
/**
|
|
2165
|
+
* The links `detectLinksWithoutDestination` would refuse as going nowhere
|
|
2166
|
+
* (`link-without-destination`) or to an anchor no section has
|
|
2167
|
+
* (`link-fragment`), read conservatively: a link whose `screenId` names a page
|
|
2168
|
+
* the site has, whose `href` is anything but a fragment, that carries a Scroll
|
|
2169
|
+
* to element interaction to a section of its page, or whose `scrollTo` the
|
|
2170
|
+
* check reads on its own terms (`scroll-target-unknown`), is not one. A tree
|
|
2171
|
+
* as written is read as the check will read it once the palette validator has
|
|
2172
|
+
* dropped the fragment.
|
|
2173
|
+
*/ function linksWithoutDestination(tree, outputKind, context) {
|
|
2174
|
+
var _context_pageSections;
|
|
2175
|
+
if (outputKind === 'email' || outputKind === 'form') return [];
|
|
2176
|
+
const sections = (_context_pageSections = context.pageSections) != null ? _context_pageSections : [];
|
|
2177
|
+
const targets = context.scrollTargetIds ? new Set(context.scrollTargetIds) : null;
|
|
2178
|
+
const known = context.screenIds ? new Set(context.screenIds) : null;
|
|
2179
|
+
const written = (id)=>context.writtenNode ? context.writtenNode(id) : tree.nodes[id];
|
|
2180
|
+
const found = [];
|
|
2181
|
+
for (const visit of walkTree(tree)){
|
|
2182
|
+
var _reverse_find;
|
|
2183
|
+
var _node_props, _node_props1, _node_props2;
|
|
2184
|
+
const { node } = visit;
|
|
2185
|
+
if (!LINK_COMPONENTS.has(node.componentId)) continue;
|
|
2186
|
+
const screenId = (_node_props = node.props) == null ? void 0 : _node_props['screenId'];
|
|
2187
|
+
if (isFilled(screenId) && (!known || known.has(String(screenId).trim()))) continue;
|
|
2188
|
+
const href = (_node_props1 = node.props) == null ? void 0 : _node_props1['href'];
|
|
2189
|
+
if (isFilled(href) && !isAiFragmentHref(href)) continue;
|
|
2190
|
+
const scrolls = aiStoredScrollTargets(node);
|
|
2191
|
+
if (scrolls.length && (!targets || scrolls.some((target)=>targets.has(target)))) continue;
|
|
2192
|
+
const target = aiPageLinkTarget(written(visit.id), sections);
|
|
2193
|
+
if ((target == null ? void 0 : target.kind) === 'section' || (target == null ? void 0 : target.kind) === 'unknown' && target.via === 'scrollTo') continue;
|
|
2194
|
+
const label = typeof ((_node_props2 = node.props) == null ? void 0 : _node_props2['children']) === 'string' ? node.props['children'].trim() : '';
|
|
2195
|
+
const form = (_reverse_find = [
|
|
2196
|
+
...visit.ancestors
|
|
2197
|
+
].reverse().find((id)=>{
|
|
2198
|
+
var _tree_nodes_id;
|
|
2199
|
+
return ((_tree_nodes_id = tree.nodes[id]) == null ? void 0 : _tree_nodes_id.componentId) === 'form';
|
|
2200
|
+
})) != null ? _reverse_find : null;
|
|
2201
|
+
found.push({
|
|
2202
|
+
id: visit.id,
|
|
2203
|
+
label,
|
|
2204
|
+
form
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
return found;
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* RULE 10'S LINK THAT GOES NOWHERE, SETTLED IN CODE (AGL-4076).
|
|
2211
|
+
*
|
|
2212
|
+
* A Button or a Page Link with no destination (`link-without-destination`),
|
|
2213
|
+
* or one sent to an anchor no section of its page has (`link-fragment`),
|
|
2214
|
+
* stopped 5 of the 12 findings behind the 10/10 to 10/28 doctrine stops: the
|
|
2215
|
+
* re-ask is spent, the job stops, and Try again asks the same. Each has one
|
|
2216
|
+
* answer the rule itself gives, so it is applied before the check reads the
|
|
2217
|
+
* tree, as `aiSettleUnrelatedScreenLinks` settles a link sent home:
|
|
2218
|
+
*
|
|
2219
|
+
* - a button drawn inside a Form is taken out, since the Form draws its own
|
|
2220
|
+
* send button; its words become the Form's `submitLabel` when the Form has
|
|
2221
|
+
* none of its own;
|
|
2222
|
+
* - a link whose words ask a visitor to get in touch is sent to the page that
|
|
2223
|
+
* places the site's form, when the site has one that is not this page;
|
|
2224
|
+
* - any other is left out: no page can be chosen for it from its words alone.
|
|
2225
|
+
*
|
|
2226
|
+
* A frame the link was the last thing in is taken out with it rather than
|
|
2227
|
+
* left empty, up to the page's own sections, which always stay. A link to a
|
|
2228
|
+
* section of its page, to a page the site has, or to any address is never
|
|
2229
|
+
* touched, and nor is one whose `scrollTo` names no section, which the check
|
|
2230
|
+
* re-asks as before. The root is never taken out. A tree with nothing to
|
|
2231
|
+
* settle comes back as the same object; a node that is changed is copied.
|
|
2232
|
+
*/ export function aiSettleLinksWithoutDestination(tree, outputKind, context = {}) {
|
|
2233
|
+
var _context_ownScreenIds, _find, _context_formScreenIds, _tree_nodes_tree_rootId_nodes, _context_scrollTargetIds;
|
|
2234
|
+
if (!tree.nodes[tree.rootId]) return tree;
|
|
2235
|
+
const found = linksWithoutDestination(tree, outputKind, context);
|
|
2236
|
+
if (!found.length) return tree;
|
|
2237
|
+
const own = new Set((_context_ownScreenIds = context.ownScreenIds) != null ? _context_ownScreenIds : []);
|
|
2238
|
+
const formScreen = (_find = ((_context_formScreenIds = context.formScreenIds) != null ? _context_formScreenIds : []).find((id)=>!own.has(id))) != null ? _find : null;
|
|
2239
|
+
// A page's sections, and anything a link scrolls to, always stay.
|
|
2240
|
+
const kept = new Set([
|
|
2241
|
+
tree.rootId,
|
|
2242
|
+
...(_tree_nodes_tree_rootId_nodes = tree.nodes[tree.rootId].nodes) != null ? _tree_nodes_tree_rootId_nodes : [],
|
|
2243
|
+
...(_context_scrollTargetIds = context.scrollTargetIds) != null ? _context_scrollTargetIds : []
|
|
2244
|
+
]);
|
|
2245
|
+
const nodes = _extends({}, tree.nodes);
|
|
2246
|
+
const parents = new Map();
|
|
2247
|
+
for (const visit of walkTree(tree)){
|
|
2248
|
+
const parent = visit.ancestors[visit.ancestors.length - 1];
|
|
2249
|
+
if (parent !== undefined) parents.set(visit.id, parent);
|
|
2250
|
+
}
|
|
2251
|
+
let changed = false;
|
|
2252
|
+
const takeOut = (id)=>{
|
|
2253
|
+
let next = id;
|
|
2254
|
+
while(next !== undefined && nodes[next] && !kept.has(next)){
|
|
2255
|
+
var _nodes_parent_nodes;
|
|
2256
|
+
const parent = parents.get(next);
|
|
2257
|
+
if (parent === undefined || !nodes[parent]) return;
|
|
2258
|
+
const children = ((_nodes_parent_nodes = nodes[parent].nodes) != null ? _nodes_parent_nodes : []).filter((child)=>child !== next);
|
|
2259
|
+
nodes[parent] = _extends({}, nodes[parent], {
|
|
2260
|
+
nodes: children
|
|
2261
|
+
});
|
|
2262
|
+
changed = true;
|
|
2263
|
+
const stack = [
|
|
2264
|
+
next
|
|
2265
|
+
];
|
|
2266
|
+
while(stack.length){
|
|
2267
|
+
var _ref;
|
|
2268
|
+
var _nodes_gone;
|
|
2269
|
+
const gone = stack.pop();
|
|
2270
|
+
stack.push(...(_ref = (_nodes_gone = nodes[gone]) == null ? void 0 : _nodes_gone.nodes) != null ? _ref : []);
|
|
2271
|
+
delete nodes[gone];
|
|
2272
|
+
}
|
|
2273
|
+
next = !children.some((child)=>nodes[child]) && LINK_FRAMES.has(nodes[parent].componentId) ? parent : undefined;
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
for (const { id, label, form } of found){
|
|
2277
|
+
if (!nodes[id]) continue;
|
|
2278
|
+
if (form && nodes[form]) {
|
|
2279
|
+
var _nodes_form_props;
|
|
2280
|
+
if (label && label.length <= FORM_SUBMIT_LABEL_MAX && !isFilled((_nodes_form_props = nodes[form].props) == null ? void 0 : _nodes_form_props['submitLabel'])) {
|
|
2281
|
+
var _nodes_form_props1;
|
|
2282
|
+
nodes[form] = _extends({}, nodes[form], {
|
|
2283
|
+
props: _extends({}, (_nodes_form_props1 = nodes[form].props) != null ? _nodes_form_props1 : {}, {
|
|
2284
|
+
submitLabel: label
|
|
2285
|
+
})
|
|
2286
|
+
});
|
|
2287
|
+
changed = true;
|
|
2288
|
+
}
|
|
2289
|
+
takeOut(id);
|
|
2290
|
+
continue;
|
|
2291
|
+
}
|
|
2292
|
+
if (formScreen && label && aiAsksToGetInTouch(label)) {
|
|
2293
|
+
var _nodes_id_props;
|
|
2294
|
+
const props = _extends({}, (_nodes_id_props = nodes[id].props) != null ? _nodes_id_props : {}, {
|
|
2295
|
+
screenId: formScreen
|
|
2296
|
+
});
|
|
2297
|
+
delete props['href'];
|
|
2298
|
+
nodes[id] = _extends({}, nodes[id], {
|
|
2299
|
+
props
|
|
2300
|
+
});
|
|
2301
|
+
changed = true;
|
|
2302
|
+
continue;
|
|
2303
|
+
}
|
|
2304
|
+
takeOut(id);
|
|
2305
|
+
}
|
|
2306
|
+
return changed ? _extends({}, tree, {
|
|
2307
|
+
nodes
|
|
2308
|
+
}) : tree;
|
|
2309
|
+
}
|
|
2154
2310
|
/** Answer fields that say "publish", wherever the answer put them. */ const PUBLISH_KEYS = /^(?:publish|published|publishNow|publishAt|goLive|isLive|live|makeLive)$/i;
|
|
2155
2311
|
/**
|
|
2156
2312
|
* Rule 13. The AI writes drafts. An answer that asks for anything to be
|
|
@@ -2613,6 +2769,29 @@ const EMBED_COMPONENTS = new Set([
|
|
|
2613
2769
|
]);
|
|
2614
2770
|
const DUPLICATE_SX_MIN_KEYS = 2;
|
|
2615
2771
|
const DUPLICATE_SX_MIN_NODES = 4;
|
|
2772
|
+
/**
|
|
2773
|
+
* Rule 16's repeated style: the elements whose `sx` is one style of at least
|
|
2774
|
+
* `DUPLICATE_SX_MIN_KEYS` keys, grouped by that style, each group of at least
|
|
2775
|
+
* `DUPLICATE_SX_MIN_NODES` elements, in document order.
|
|
2776
|
+
*/ function duplicatedSxGroups(tree) {
|
|
2777
|
+
const seen = new Map();
|
|
2778
|
+
for (const { id, node } of walkTree(tree)){
|
|
2779
|
+
var _node_sx, _seen_get;
|
|
2780
|
+
const sx = (_node_sx = node.sx) != null ? _node_sx : {};
|
|
2781
|
+
if (Object.keys(sx).length < DUPLICATE_SX_MIN_KEYS) continue;
|
|
2782
|
+
const key = JSON.stringify(Object.keys(sx).sort().map((name)=>[
|
|
2783
|
+
name,
|
|
2784
|
+
sx[name]
|
|
2785
|
+
]));
|
|
2786
|
+
seen.set(key, [
|
|
2787
|
+
...(_seen_get = seen.get(key)) != null ? _seen_get : [],
|
|
2788
|
+
id
|
|
2789
|
+
]);
|
|
2790
|
+
}
|
|
2791
|
+
return [
|
|
2792
|
+
...seen.values()
|
|
2793
|
+
].filter((ids)=>ids.length >= DUPLICATE_SX_MIN_NODES);
|
|
2794
|
+
}
|
|
2616
2795
|
/**
|
|
2617
2796
|
* Whether a third-party player is one the confirmed plan lists (AGL-3433). A
|
|
2618
2797
|
* Video embed is admitted where the plan lists a player, playing the video
|
|
@@ -2648,11 +2827,10 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
2648
2827
|
const video = [];
|
|
2649
2828
|
const embeds = [];
|
|
2650
2829
|
const embedLinks = [];
|
|
2651
|
-
const sxSeen = new Map();
|
|
2652
2830
|
let images = 0;
|
|
2653
2831
|
for (const { id, node } of visits){
|
|
2654
|
-
var _node_nodes, _node_props, _node_sx
|
|
2655
|
-
var _node_props1,
|
|
2832
|
+
var _node_nodes, _node_props, _node_sx;
|
|
2833
|
+
var _node_props1, _node_sx1, _node_props2, _node_props3, _node_props4, _node_props5;
|
|
2656
2834
|
const entry = AI_PALETTE[node.componentId];
|
|
2657
2835
|
const children = ((_node_nodes = node.nodes) != null ? _node_nodes : []).filter((child)=>tree.nodes[child]);
|
|
2658
2836
|
const ownProps = Object.entries((_node_props = node.props) != null ? _node_props : {}).filter(([name, value])=>!(name === 'component' && value === 'div'));
|
|
@@ -2663,7 +2841,7 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
2663
2841
|
if ((entry == null ? void 0 : entry.acceptsChildren) && !children.length && !boundForm && !OWNS_ITS_EMPTY_STATE.has(node.componentId)) {
|
|
2664
2842
|
empty.push(id);
|
|
2665
2843
|
}
|
|
2666
|
-
if (outputKind !== 'email' && ((
|
|
2844
|
+
if (outputKind !== 'email' && ((_node_sx1 = node.sx) == null ? void 0 : _node_sx1['fontFamily']) !== undefined) fonts.push(id);
|
|
2667
2845
|
if (node.componentId === 'image') {
|
|
2668
2846
|
var _node_props6;
|
|
2669
2847
|
images += 1;
|
|
@@ -2678,22 +2856,8 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
2678
2856
|
if (verdict === 'unplanned') embeds.push(id);
|
|
2679
2857
|
if (verdict === 'unplanned-link') embedLinks.push(id);
|
|
2680
2858
|
}
|
|
2681
|
-
const sx = (_node_sx1 = node.sx) != null ? _node_sx1 : {};
|
|
2682
|
-
if (Object.keys(sx).length >= DUPLICATE_SX_MIN_KEYS) {
|
|
2683
|
-
var _sxSeen_get;
|
|
2684
|
-
const key = JSON.stringify(Object.keys(sx).sort().map((name)=>[
|
|
2685
|
-
name,
|
|
2686
|
-
sx[name]
|
|
2687
|
-
]));
|
|
2688
|
-
sxSeen.set(key, [
|
|
2689
|
-
...(_sxSeen_get = sxSeen.get(key)) != null ? _sxSeen_get : [],
|
|
2690
|
-
id
|
|
2691
|
-
]);
|
|
2692
|
-
}
|
|
2693
2859
|
}
|
|
2694
|
-
const duplicatedSx =
|
|
2695
|
-
...sxSeen.values()
|
|
2696
|
-
].filter((ids)=>ids.length >= DUPLICATE_SX_MIN_NODES).flat();
|
|
2860
|
+
const duplicatedSx = duplicatedSxGroups(tree).flat();
|
|
2697
2861
|
const violations = [];
|
|
2698
2862
|
const add = (code, ids, message)=>{
|
|
2699
2863
|
if (ids.length) violations.push({
|
|
@@ -3416,6 +3580,52 @@ const BUDGET_METRICS = [
|
|
|
3416
3580
|
}) : input;
|
|
3417
3581
|
}
|
|
3418
3582
|
// ── Mechanical rules, settled in code (AGL-3660) ─────────────────────────
|
|
3583
|
+
/** The inherited styles a repeated style may be carried once in, by the frame that holds every element repeating it (AGL-4076). */ const HOISTED_SX_KEYS = [
|
|
3584
|
+
'textAlign',
|
|
3585
|
+
'color'
|
|
3586
|
+
];
|
|
3587
|
+
/**
|
|
3588
|
+
* The elements that take each of those styles from their parent when they set
|
|
3589
|
+
* none themselves, which are also the frames that may carry one for the
|
|
3590
|
+
* elements they hold: none of them draws anything of its own in it.
|
|
3591
|
+
*/ const INHERITS_SX = {
|
|
3592
|
+
// A Typography aligns as `inherit` unless its `align` says otherwise; a Card and a Paper set no alignment.
|
|
3593
|
+
textAlign: new Set([
|
|
3594
|
+
'muiTypography',
|
|
3595
|
+
'muiBox',
|
|
3596
|
+
'muiStack',
|
|
3597
|
+
'muiGrid',
|
|
3598
|
+
'muiContainer',
|
|
3599
|
+
'section',
|
|
3600
|
+
'div',
|
|
3601
|
+
'muiCardContent',
|
|
3602
|
+
'muiCard',
|
|
3603
|
+
'muiPaper'
|
|
3604
|
+
]),
|
|
3605
|
+
// A Card and a Paper paint their own text color, and a Button, a link and a list row theirs.
|
|
3606
|
+
color: new Set([
|
|
3607
|
+
'muiTypography',
|
|
3608
|
+
'muiBox',
|
|
3609
|
+
'muiStack',
|
|
3610
|
+
'muiGrid',
|
|
3611
|
+
'muiContainer',
|
|
3612
|
+
'section',
|
|
3613
|
+
'div',
|
|
3614
|
+
'muiCardContent'
|
|
3615
|
+
])
|
|
3616
|
+
};
|
|
3617
|
+
/** A Typography's `align` values, which draw exactly what the same `sx` text alignment does. */ const TYPOGRAPHY_ALIGNS = new Set([
|
|
3618
|
+
'inherit',
|
|
3619
|
+
'left',
|
|
3620
|
+
'center',
|
|
3621
|
+
'right',
|
|
3622
|
+
'justify'
|
|
3623
|
+
]);
|
|
3624
|
+
/** An `sx` without one key. */ function sxWithout(sx, key) {
|
|
3625
|
+
const rest = _extends({}, sx != null ? sx : {});
|
|
3626
|
+
delete rest[key];
|
|
3627
|
+
return rest;
|
|
3628
|
+
}
|
|
3419
3629
|
/**
|
|
3420
3630
|
* THE RULES A CHECK WOULD REFUSE FOR AN ATTRIBUTE, SETTLED IN CODE (AGL-3660).
|
|
3421
3631
|
*
|
|
@@ -3451,11 +3661,20 @@ const BUDGET_METRICS = [
|
|
|
3451
3661
|
* - `page-text-on-band` (5): words on a band in a family's main or dark shade
|
|
3452
3662
|
* whose `sx` color is a page text color (`text.secondary`) are given the
|
|
3453
3663
|
* family's contrast text, and a List Item Text's secondary words there take
|
|
3454
|
-
* the row's own color (`secondaryColor: inherit`)
|
|
3664
|
+
* the row's own color (`secondaryColor: inherit`);
|
|
3665
|
+
* - `duplicated-inline-style` (16), where the style can be carried once
|
|
3666
|
+
* without changing what is drawn (AGL-4076): when the elements repeating it
|
|
3667
|
+
* are every element of one frame, its text alignment and its text color
|
|
3668
|
+
* move to that frame, which they inherit from, and a Typography's text
|
|
3669
|
+
* alignment becomes its `align`. Only where that leaves fewer repeats; a
|
|
3670
|
+
* repeated padding, size or border has no single place to go and is still
|
|
3671
|
+
* the re-ask's;
|
|
3672
|
+
* - `link-without-destination` and `link-fragment` (10), given `links`, the
|
|
3673
|
+
* context the check reads links in (`aiSettleLinksWithoutDestination`).
|
|
3455
3674
|
*
|
|
3456
3675
|
* A tree with nothing to settle comes back as the same object, so settling is
|
|
3457
3676
|
* idempotent; a node that is changed is copied, never edited in place.
|
|
3458
|
-
*/ export function aiSettleMechanicalRules(tree, outputKind) {
|
|
3677
|
+
*/ export function aiSettleMechanicalRules(tree, outputKind, links) {
|
|
3459
3678
|
if (!tree.nodes[tree.rootId]) return tree;
|
|
3460
3679
|
const nodes = _extends({}, tree.nodes);
|
|
3461
3680
|
const copied = new Set();
|
|
@@ -3590,25 +3809,122 @@ const BUDGET_METRICS = [
|
|
|
3590
3809
|
});
|
|
3591
3810
|
}
|
|
3592
3811
|
}
|
|
3593
|
-
|
|
3812
|
+
// Rule 16: a style a group of elements repeats, carried once where it draws the same (AGL-4076).
|
|
3813
|
+
if (framed) {
|
|
3814
|
+
const parentOf = new Map();
|
|
3815
|
+
for (const visit of walkTree({
|
|
3816
|
+
rootId: tree.rootId,
|
|
3817
|
+
nodes
|
|
3818
|
+
})){
|
|
3819
|
+
const parent = visit.ancestors[visit.ancestors.length - 1];
|
|
3820
|
+
if (parent !== undefined) parentOf.set(visit.id, parent);
|
|
3821
|
+
}
|
|
3822
|
+
const heavy = ()=>detectHeavyDocument({
|
|
3823
|
+
rootId: tree.rootId,
|
|
3824
|
+
nodes
|
|
3825
|
+
}, outputKind).reduce((sum, violation)=>{
|
|
3826
|
+
var _ref;
|
|
3827
|
+
var _violation_nodeIds;
|
|
3828
|
+
return sum + ((_ref = (_violation_nodeIds = violation.nodeIds) == null ? void 0 : _violation_nodeIds.length) != null ? _ref : 0);
|
|
3829
|
+
}, 0);
|
|
3830
|
+
for (const group of duplicatedSxGroups({
|
|
3831
|
+
rootId: tree.rootId,
|
|
3832
|
+
nodes
|
|
3833
|
+
})){
|
|
3834
|
+
var _ref, _ref1;
|
|
3835
|
+
var _nodes_group_, _nodes_parent;
|
|
3836
|
+
const style = (_ref = (_nodes_group_ = nodes[group[0]]) == null ? void 0 : _nodes_group_.sx) != null ? _ref : {};
|
|
3837
|
+
const same = JSON.stringify(Object.keys(style).sort().map((name)=>[
|
|
3838
|
+
name,
|
|
3839
|
+
style[name]
|
|
3840
|
+
]));
|
|
3841
|
+
const alike = group.every((id)=>{
|
|
3842
|
+
var _ref;
|
|
3843
|
+
var _nodes_id;
|
|
3844
|
+
const sx = (_ref = (_nodes_id = nodes[id]) == null ? void 0 : _nodes_id.sx) != null ? _ref : {};
|
|
3845
|
+
return JSON.stringify(Object.keys(sx).sort().map((name)=>[
|
|
3846
|
+
name,
|
|
3847
|
+
sx[name]
|
|
3848
|
+
])) === same;
|
|
3849
|
+
});
|
|
3850
|
+
if (!alike) continue;
|
|
3851
|
+
const before = heavy();
|
|
3852
|
+
const replaced = new Map();
|
|
3853
|
+
const wasCopied = new Set(copied);
|
|
3854
|
+
const put = (id, node)=>{
|
|
3855
|
+
if (!replaced.has(id)) replaced.set(id, nodes[id]);
|
|
3856
|
+
nodes[id] = node;
|
|
3857
|
+
copied.add(id);
|
|
3858
|
+
};
|
|
3859
|
+
const parent = parentOf.get(group[0]);
|
|
3860
|
+
const held = parent !== undefined ? ((_ref1 = (_nodes_parent = nodes[parent]) == null ? void 0 : _nodes_parent.nodes) != null ? _ref1 : []).filter((child)=>nodes[child]) : [];
|
|
3861
|
+
const whole = parent !== undefined && parent !== tree.rootId && held.length === group.length && group.every((id)=>parentOf.get(id) === parent);
|
|
3862
|
+
for (const key of whole ? HOISTED_SX_KEYS : []){
|
|
3863
|
+
var _holder_sx, _holder_sx1;
|
|
3864
|
+
var _holder_sx2;
|
|
3865
|
+
const holder = nodes[parent];
|
|
3866
|
+
if (style[key] === undefined || !INHERITS_SX[key].has(holder.componentId)) continue;
|
|
3867
|
+
if (!group.every((id)=>INHERITS_SX[key].has(nodes[id].componentId))) continue;
|
|
3868
|
+
const own = (_holder_sx2 = holder.sx) == null ? void 0 : _holder_sx2[key];
|
|
3869
|
+
if (own !== undefined && JSON.stringify(own) !== JSON.stringify(style[key])) continue;
|
|
3870
|
+
// A border drawn in the frame's own text color would take the new one.
|
|
3871
|
+
if (key === 'color' && Object.keys((_holder_sx = holder.sx) != null ? _holder_sx : {}).some((name)=>/^(?:border|outline)/.test(name))) continue;
|
|
3872
|
+
put(parent, _extends({}, holder, {
|
|
3873
|
+
sx: _extends({}, (_holder_sx1 = holder.sx) != null ? _holder_sx1 : {}, {
|
|
3874
|
+
[key]: style[key]
|
|
3875
|
+
})
|
|
3876
|
+
}));
|
|
3877
|
+
for (const id of group)put(id, _extends({}, nodes[id], {
|
|
3878
|
+
sx: sxWithout(nodes[id].sx, key)
|
|
3879
|
+
}));
|
|
3880
|
+
}
|
|
3881
|
+
for (const id of group){
|
|
3882
|
+
var _node_props7;
|
|
3883
|
+
var _node_sx4, _node_props8;
|
|
3884
|
+
const node = nodes[id];
|
|
3885
|
+
const align = (_node_sx4 = node.sx) == null ? void 0 : _node_sx4['textAlign'];
|
|
3886
|
+
if (node.componentId !== 'muiTypography' || typeof align !== 'string' || !TYPOGRAPHY_ALIGNS.has(align)) continue;
|
|
3887
|
+
if (((_node_props8 = node.props) == null ? void 0 : _node_props8['align']) !== undefined && node.props['align'] !== 'inherit') continue;
|
|
3888
|
+
put(id, _extends({}, node, {
|
|
3889
|
+
props: _extends({}, (_node_props7 = node.props) != null ? _node_props7 : {}, {
|
|
3890
|
+
align
|
|
3891
|
+
}),
|
|
3892
|
+
sx: sxWithout(node.sx, 'textAlign')
|
|
3893
|
+
}));
|
|
3894
|
+
}
|
|
3895
|
+
// Kept only where it leaves the page lighter by the rule's own measure.
|
|
3896
|
+
if (replaced.size && heavy() >= before) {
|
|
3897
|
+
for (const [id, node] of replaced)nodes[id] = node;
|
|
3898
|
+
copied.clear();
|
|
3899
|
+
for (const id of wasCopied)copied.add(id);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const settled = copied.size ? _extends({}, tree, {
|
|
3594
3904
|
nodes
|
|
3595
3905
|
}) : tree;
|
|
3906
|
+
return links ? aiSettleLinksWithoutDestination(settled, outputKind, links) : settled;
|
|
3596
3907
|
}
|
|
3597
3908
|
/**
|
|
3598
3909
|
* `aiSettleMechanicalRules` on a tree as the model wrote it, before the
|
|
3599
3910
|
* palette validator reads it (AGL-3660), as `aiSettleWrittenGridItems` settles
|
|
3600
3911
|
* its items. Anything that is no flat node map comes back as it was, and a
|
|
3601
3912
|
* node whose shape is not a palette node's is read as nothing and left as written.
|
|
3602
|
-
|
|
3913
|
+
* Given `links`, a link that goes nowhere is settled too (AGL-4076), and a
|
|
3914
|
+
* node that takes out is gone from the answer.
|
|
3915
|
+
*/ export function aiSettleWrittenMechanicalRules(input, outputKind, links) {
|
|
3603
3916
|
if (!isRecord(input) || typeof input['rootId'] !== 'string' || !isRecord(input['nodes'])) return input;
|
|
3604
3917
|
const readable = Object.entries(input['nodes']).filter(([, node])=>isRecord(node) && typeof node['componentId'] === 'string' && (node['props'] === undefined || isRecord(node['props'])) && (node['sx'] === undefined || isRecord(node['sx'])) && (node['nodes'] === undefined || Array.isArray(node['nodes']) && node['nodes'].every((child)=>typeof child === 'string')));
|
|
3605
3918
|
const tree = {
|
|
3606
3919
|
rootId: input['rootId'],
|
|
3607
3920
|
nodes: Object.fromEntries(readable)
|
|
3608
3921
|
};
|
|
3609
|
-
const settled = aiSettleMechanicalRules(tree, outputKind);
|
|
3610
|
-
|
|
3611
|
-
|
|
3922
|
+
const settled = aiSettleMechanicalRules(tree, outputKind, links);
|
|
3923
|
+
if (settled === tree) return input;
|
|
3924
|
+
const nodes = _extends({}, input['nodes'], settled.nodes);
|
|
3925
|
+
for (const id of Object.keys(tree.nodes))if (!settled.nodes[id]) delete nodes[id];
|
|
3926
|
+
return _extends({}, input, {
|
|
3927
|
+
nodes
|
|
3612
3928
|
});
|
|
3613
3929
|
}
|
|
3614
3930
|
/**
|
|
@@ -5197,7 +5513,8 @@ function slugKey(slug) {
|
|
|
5197
5513
|
*/ export const AI_FREE_PAGE_WORST_CASE_CREDITS = {
|
|
5198
5514
|
plan: 83,
|
|
5199
5515
|
layout: 64,
|
|
5200
|
-
|
|
5516
|
+
// 44 until AGL-4092, whose rule 14 every section pass's cached prefix carries.
|
|
5517
|
+
firstSection: 45,
|
|
5201
5518
|
laterSection: 20,
|
|
5202
5519
|
// A page's listing, on the fast tier at the platform's markup since
|
|
5203
5520
|
// AGL-3781 (3 credits at Claude Haiku 4.5's cost before).
|