@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232
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 +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +14 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +29 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
- package/src/lib/jobs/ai-job-layout-language.js +200 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +107 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
- package/src/lib/jobs/ai-job-page-language.js +418 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +115 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
- package/src/lib/jobs/ai-job-plan-step.js +62 -22
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
- package/src/lib/jobs/ai-job-site-step.js +183 -27
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +458 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
- package/src/lib/layout-language/ai-layout-language.js +529 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-store.js +75 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +52 -7
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +23 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +22 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +9 -1
- package/src/lib/model/ai-site-build-progress.js +54 -14
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +18 -2
- package/src/lib/model/ai-site-job.js +21 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +15 -3
- package/src/lib/model/ai-site-start.js +21 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +99 -48
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +38 -8
- package/src/lib/providers/catalog.js +106 -14
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +16 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -18,6 +18,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { formatBytes, measureNodeMap, nodeMapBytes } from "@aglyn/aglyn/app-utils/measure-node-map";
|
|
19
19
|
import { MEDIA_CDN_VARIANT_WIDTHS, parseMediaRef } from "@aglyn/aglyn/app-utils/media-ref";
|
|
20
20
|
import { ESTIMATED_PAGE_TRANSFER_BYTES } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
21
|
+
import { createIdUrlSafe } from "@aglyn/aglyn/foundation/constants/app";
|
|
21
22
|
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
22
23
|
import { REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY } from "@aglyn/aglyn/app-utils/reusable-component-keys";
|
|
23
24
|
import { parseBreakpointSpan } from "@aglyn/shared-data-enums/breakpoint-span";
|
|
@@ -27,7 +28,7 @@ import { aiCreationNoun, aiPlanCanPlaceForm, aiPlanUncreatable, aiPlanUncreatabl
|
|
|
27
28
|
import { aiFreeSiteSectionsWithin } from "../model/ai-site-job.js";
|
|
28
29
|
import { AI_INSTANCE_REF_PROP, validateAiNodeTree } from "./ai-node-tree.js";
|
|
29
30
|
import { AI_PAGE_SCROLL_TO_PROP, aiPageLinkTarget, aiStoredScrollTargets } from "./ai-page-links.js";
|
|
30
|
-
import { AI_EMAIL_CLIP_BYTES, AI_OUTPUT_BUDGETS, AI_OUTPUT_SURFACE, isHeadlineVariant } from "./ai-palette.js";
|
|
31
|
+
import { AI_EMAIL_CLIP_BYTES, AI_OUTPUT_BUDGETS, AI_OUTPUT_SURFACE, AI_TEXT_LIMITS, isHeadlineVariant } from "./ai-palette.js";
|
|
31
32
|
import { AI_PALETTE, AI_SX_TOKENS } from "./ai-palette.generated.js";
|
|
32
33
|
/** Each rule's title, in the words the proposal and the docs page use. */ export const AI_DOCTRINE_RULES = {
|
|
33
34
|
1: 'Repeats become one reusable component',
|
|
@@ -170,13 +171,13 @@ function listNames(ids, tree) {
|
|
|
170
171
|
* the outermost repeat: the header inside each of twelve cards repeats
|
|
171
172
|
* because the card does, and the fix is the card. `groupOf` counts each
|
|
172
173
|
* group's repeats apart; absent, the whole tree is one group.
|
|
173
|
-
*/ function repeatedShapes(tree, index, minNodes, minCount, groupOf = ()=>tree.rootId) {
|
|
174
|
+
*/ function repeatedShapes(tree, index, minNodes, minCount, groupOf = ()=>tree.rootId, skip = ()=>false) {
|
|
174
175
|
const byShape = new Map();
|
|
175
176
|
for (const visit of index.visits){
|
|
176
177
|
var _index_sizeOf_get, _byShape_get;
|
|
177
178
|
if (visit.id === tree.rootId) continue;
|
|
178
179
|
if (((_index_sizeOf_get = index.sizeOf.get(visit.id)) != null ? _index_sizeOf_get : 0) < minNodes) continue;
|
|
179
|
-
if (repeatedByData(tree, visit)) continue;
|
|
180
|
+
if (repeatedByData(tree, visit) || skip(visit)) continue;
|
|
180
181
|
const shape = JSON.stringify([
|
|
181
182
|
groupOf(visit),
|
|
182
183
|
index.shapeOf.get(visit.id)
|
|
@@ -202,6 +203,71 @@ function listNames(ids, tree) {
|
|
|
202
203
|
ids
|
|
203
204
|
}));
|
|
204
205
|
}
|
|
206
|
+
/** Elements that frame and space what they hold, as rule 1 reads a frame. */ const REPEAT_FRAMES = new Set([
|
|
207
|
+
'muiBox',
|
|
208
|
+
'muiStack',
|
|
209
|
+
'muiContainer',
|
|
210
|
+
'muiGrid',
|
|
211
|
+
'section',
|
|
212
|
+
'muiList'
|
|
213
|
+
]);
|
|
214
|
+
/**
|
|
215
|
+
* What rule 1 does not call a block (AGL-3660), however like another it is:
|
|
216
|
+
*
|
|
217
|
+
* - a part of the page's OUTLINE — anything holding a heading at h1 or h2.
|
|
218
|
+
* Three sections that each open with an h2 over a row of items share a
|
|
219
|
+
* shape because a page has sections, and a component cannot carry the
|
|
220
|
+
* page's outline: a heading inside one is no heading of the page.
|
|
221
|
+
* - a FRAME OF ITEMS — a Grid container, a List, or a Stack of like
|
|
222
|
+
* elements — and any frame that holds only such frames. What repeats in
|
|
223
|
+
* it is its items, which are judged on their own: two rows of three
|
|
224
|
+
* instances of two different components are not one block.
|
|
225
|
+
*
|
|
226
|
+
* So rule 1 still refuses three like cards, and the same card on two pages;
|
|
227
|
+
* it no longer refuses a page for having more than two sections, or more than
|
|
228
|
+
* two groups.
|
|
229
|
+
*/ function aiRepeatFrameOrSection(tree) {
|
|
230
|
+
var _ref;
|
|
231
|
+
const memo = new Map();
|
|
232
|
+
const childrenOf = (id)=>{
|
|
233
|
+
var _ref;
|
|
234
|
+
var _tree_nodes_id;
|
|
235
|
+
return ((_ref = (_tree_nodes_id = tree.nodes[id]) == null ? void 0 : _tree_nodes_id.nodes) != null ? _ref : []).filter((child)=>tree.nodes[child]);
|
|
236
|
+
};
|
|
237
|
+
const holdsOutline = (id)=>{
|
|
238
|
+
const node = tree.nodes[id];
|
|
239
|
+
const level = node ? aiHeadingLevel(node) : null;
|
|
240
|
+
return level !== null && level <= 2 || childrenOf(id).some(holdsOutline);
|
|
241
|
+
};
|
|
242
|
+
const itemFrame = (id)=>{
|
|
243
|
+
var _node_props;
|
|
244
|
+
const node = tree.nodes[id];
|
|
245
|
+
if (!node) return false;
|
|
246
|
+
if (node.componentId === 'muiGrid' && ((_node_props = node.props) == null ? void 0 : _node_props['container']) === true) return true;
|
|
247
|
+
if (node.componentId === 'muiList') return true;
|
|
248
|
+
const children = childrenOf(id);
|
|
249
|
+
// A Stack of like blocks — cards, rows, panels — and never of lines of text.
|
|
250
|
+
const kinds = new Set(children.map((child)=>tree.nodes[child].componentId));
|
|
251
|
+
return node.componentId === 'muiStack' && children.length >= 2 && kinds.size === 1 && children.every((child)=>{
|
|
252
|
+
var _tree_nodes_child_nodes;
|
|
253
|
+
return ((_tree_nodes_child_nodes = tree.nodes[child].nodes) != null ? _tree_nodes_child_nodes : []).length > 0;
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
const parentOf = new Map();
|
|
257
|
+
for (const [id, node] of Object.entries(tree.nodes))for (const child of (_ref = node == null ? void 0 : node.nodes) != null ? _ref : [])parentOf.set(child, id);
|
|
258
|
+
const exempt = (id)=>{
|
|
259
|
+
const known = memo.get(id);
|
|
260
|
+
if (known !== undefined) return known;
|
|
261
|
+
const node = tree.nodes[id];
|
|
262
|
+
const children = childrenOf(id);
|
|
263
|
+
const parent = parentOf.get(id);
|
|
264
|
+
const result = // An item of a frame is an item, whatever heading it carries.
|
|
265
|
+
holdsOutline(id) && !(parent !== undefined && itemFrame(parent)) || itemFrame(id) || !!node && REPEAT_FRAMES.has(node.componentId) && children.length > 0 && children.every(exempt);
|
|
266
|
+
memo.set(id, result);
|
|
267
|
+
return result;
|
|
268
|
+
};
|
|
269
|
+
return (visit)=>exempt(visit.id);
|
|
270
|
+
}
|
|
205
271
|
/**
|
|
206
272
|
* Rule 1. A block repeated on one page, or a block this page shares with
|
|
207
273
|
* another page generated beside it, is one component placed as instances.
|
|
@@ -211,7 +277,18 @@ function listNames(ids, tree) {
|
|
|
211
277
|
*/ export function detectRepeatedSubtrees(tree, otherPages = [], context = {}) {
|
|
212
278
|
if (context.reusableComponents === false) return [];
|
|
213
279
|
const index = indexShapes(tree);
|
|
214
|
-
const violations = repeatedShapes(tree, index, AI_REPEAT_MIN_NODES, AI_REPEAT_MIN_COUNT)
|
|
280
|
+
const violations = repeatedShapes(tree, index, AI_REPEAT_MIN_NODES, AI_REPEAT_MIN_COUNT, // Counted within the band of the page it sits in (AGL-3660), as rule 8
|
|
281
|
+
// counts a typed list: a heading over its words in the About band and in
|
|
282
|
+
// the Services band are two parts of two stories, not one block placed twice.
|
|
283
|
+
(visit)=>{
|
|
284
|
+
var _reverse_find;
|
|
285
|
+
return (_reverse_find = [
|
|
286
|
+
...visit.ancestors
|
|
287
|
+
].reverse().find((id)=>{
|
|
288
|
+
var _tree_nodes_id;
|
|
289
|
+
return ((_tree_nodes_id = tree.nodes[id]) == null ? void 0 : _tree_nodes_id.componentId) === 'section';
|
|
290
|
+
})) != null ? _reverse_find : tree.rootId;
|
|
291
|
+
}, aiRepeatFrameOrSection(tree)).map(({ ids })=>{
|
|
215
292
|
const name = displayName(tree.nodes[ids[0]].componentId);
|
|
216
293
|
return {
|
|
217
294
|
rule: 1,
|
|
@@ -1258,6 +1335,192 @@ const GRID = 'muiGrid';
|
|
|
1258
1335
|
}
|
|
1259
1336
|
return violations;
|
|
1260
1337
|
}
|
|
1338
|
+
/** The breakpoints a size names, in the order the renderer's string writes them. */ const GRID_SIZE_BREAKPOINTS = [
|
|
1339
|
+
'xs',
|
|
1340
|
+
'sm',
|
|
1341
|
+
'md',
|
|
1342
|
+
'lg',
|
|
1343
|
+
'xl'
|
|
1344
|
+
];
|
|
1345
|
+
/**
|
|
1346
|
+
* A size the model wrote as an object of breakpoints (`{ "xs": 12, "md": 4 }`),
|
|
1347
|
+
* which the palette validator drops because a size is stored as one string,
|
|
1348
|
+
* written as that string (`xs:12 md:4`). Anything else is not read.
|
|
1349
|
+
*/ function writtenGridSizeText(size) {
|
|
1350
|
+
if (!isRecord(size)) return null;
|
|
1351
|
+
const pairs = [];
|
|
1352
|
+
for (const [breakpoint, value] of Object.entries(size)){
|
|
1353
|
+
const span = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
1354
|
+
if (!GRID_SIZE_BREAKPOINTS.includes(breakpoint) || typeof span !== 'number' || !Number.isInteger(span)) return null;
|
|
1355
|
+
pairs.push(`${breakpoint}:${span}`);
|
|
1356
|
+
}
|
|
1357
|
+
pairs.sort((a, b)=>GRID_SIZE_BREAKPOINTS.indexOf(a.split(':')[0]) - GRID_SIZE_BREAKPOINTS.indexOf(b.split(':')[0]));
|
|
1358
|
+
return pairs.length ? pairs.join(' ') : null;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Rule 12's `grid-item-size`, settled before the check reads it where it has
|
|
1362
|
+
* one answer (AGL-3596), as `aiSettlePlanRefs` settles a plan. The re-ask for
|
|
1363
|
+
* it states the very size to write, and a production Home page was refused and
|
|
1364
|
+
* refunded after its re-ask still left a container's items unsized: the model
|
|
1365
|
+
* breaks a different rule each run, and a rule whose answer the validator
|
|
1366
|
+
* already knows is applied rather than asked for.
|
|
1367
|
+
*
|
|
1368
|
+
* For every Grid container, its items are read exactly as
|
|
1369
|
+
* `detectUnresponsiveGrids` counts them, a Box or a Stack holding nothing but
|
|
1370
|
+
* sized Grid items lending its items to the container:
|
|
1371
|
+
*
|
|
1372
|
+
* - an item whose size is missing, or is not full width on a phone, takes the
|
|
1373
|
+
* size the model wrote as an object of breakpoints where that one is full
|
|
1374
|
+
* width on a phone, and otherwise the size the re-ask would ask for
|
|
1375
|
+
* (`gridItemSizeFor`);
|
|
1376
|
+
* - an element that is no Grid, directly in the container, is wrapped in a new
|
|
1377
|
+
* Grid item of that size, minted the way the palette validator mints ids;
|
|
1378
|
+
* - a container of several items none of which steps down to columns at a
|
|
1379
|
+
* larger width takes that size on every item, which is what the re-ask asks;
|
|
1380
|
+
* - a `container` written as the text "true" in any case or spacing
|
|
1381
|
+
* (`grid-container-text`) is true, since it can mean nothing else.
|
|
1382
|
+
*
|
|
1383
|
+
* A size that is already full width on a phone is kept. Nothing else rule 12
|
|
1384
|
+
* names is touched: a Grid that is not a container may be meant as a row or as
|
|
1385
|
+
* a stack (`grid-not-container`, `grid-as-stack`), and a sized item inside a
|
|
1386
|
+
* Box (`grid-item-outside-container`) is moved only by changing the Box.
|
|
1387
|
+
*
|
|
1388
|
+
* `written` is the node as the model wrote it, by an id of `tree`, as
|
|
1389
|
+
* `detectUnresponsiveGrids` reads it, for a tree the palette validator has
|
|
1390
|
+
* read; a tree as the model wrote it is read as it stands, which is how the
|
|
1391
|
+
* tree check settles a layout, a template or a component (`aiSettleWrittenGridItems`).
|
|
1392
|
+
* A tree with nothing to settle comes back
|
|
1393
|
+
* as the same object, so settling is idempotent. A node that is changed is
|
|
1394
|
+
* copied, never edited in place.
|
|
1395
|
+
*/ export function aiSettleGridItems(tree, options = {}) {
|
|
1396
|
+
var _options_written, _options_mintId;
|
|
1397
|
+
const written = (_options_written = options.written) != null ? _options_written : ()=>undefined;
|
|
1398
|
+
const mintId = (_options_mintId = options.mintId) != null ? _options_mintId : createIdUrlSafe;
|
|
1399
|
+
const nodes = _extends({}, tree.nodes);
|
|
1400
|
+
const copied = new Set();
|
|
1401
|
+
const edit = (id)=>{
|
|
1402
|
+
if (!copied.has(id)) {
|
|
1403
|
+
const node = nodes[id];
|
|
1404
|
+
nodes[id] = _extends({}, node, {
|
|
1405
|
+
props: _extends({}, node.props)
|
|
1406
|
+
}, node.nodes ? {
|
|
1407
|
+
nodes: [
|
|
1408
|
+
...node.nodes
|
|
1409
|
+
]
|
|
1410
|
+
} : {});
|
|
1411
|
+
copied.add(id);
|
|
1412
|
+
}
|
|
1413
|
+
return nodes[id];
|
|
1414
|
+
};
|
|
1415
|
+
const setSize = (id, size)=>{
|
|
1416
|
+
var _nodes_id_props;
|
|
1417
|
+
if (((_nodes_id_props = nodes[id].props) == null ? void 0 : _nodes_id_props['size']) === size) return;
|
|
1418
|
+
edit(id).props['size'] = size;
|
|
1419
|
+
};
|
|
1420
|
+
const writtenProp = (id, name)=>{
|
|
1421
|
+
const node = written(id);
|
|
1422
|
+
return isRecord(node) && isRecord(node['props']) ? node['props'][name] : undefined;
|
|
1423
|
+
};
|
|
1424
|
+
const childrenOf = (id)=>{
|
|
1425
|
+
var _ref;
|
|
1426
|
+
var _nodes_id;
|
|
1427
|
+
return ((_ref = (_nodes_id = nodes[id]) == null ? void 0 : _nodes_id.nodes) != null ? _ref : []).filter((child)=>nodes[child]);
|
|
1428
|
+
};
|
|
1429
|
+
const isSized = (id)=>{
|
|
1430
|
+
var _nodes_id, _nodes_id_props;
|
|
1431
|
+
return ((_nodes_id = nodes[id]) == null ? void 0 : _nodes_id.componentId) === GRID && ((_nodes_id_props = nodes[id].props) == null ? void 0 : _nodes_id_props['size']) !== undefined;
|
|
1432
|
+
};
|
|
1433
|
+
const wrapsItems = (id)=>{
|
|
1434
|
+
const children = childrenOf(id);
|
|
1435
|
+
return GRID_ITEM_WRAPPERS.has(nodes[id].componentId) && children.length > 0 && children.every(isSized);
|
|
1436
|
+
};
|
|
1437
|
+
for (const { id } of walkTree(tree)){
|
|
1438
|
+
var _nodes_id_props;
|
|
1439
|
+
if (nodes[id].componentId !== GRID) continue;
|
|
1440
|
+
if (((_nodes_id_props = nodes[id].props) == null ? void 0 : _nodes_id_props['container']) !== true) {
|
|
1441
|
+
var _nodes_id_props1;
|
|
1442
|
+
// Text the palette validator dropped, or, in a tree as the model wrote it, the prop itself.
|
|
1443
|
+
const own = (_nodes_id_props1 = nodes[id].props) == null ? void 0 : _nodes_id_props1['container'];
|
|
1444
|
+
const text = own === undefined ? writtenProp(id, 'container') : own;
|
|
1445
|
+
if (typeof text !== 'string' || text.trim().toLowerCase() !== 'true') continue;
|
|
1446
|
+
edit(id).props['container'] = true;
|
|
1447
|
+
}
|
|
1448
|
+
const container = nodes[id];
|
|
1449
|
+
const columns = gridColumns(container);
|
|
1450
|
+
const children = childrenOf(id);
|
|
1451
|
+
const items = children.flatMap((child)=>wrapsItems(child) ? childrenOf(child) : [
|
|
1452
|
+
child
|
|
1453
|
+
]);
|
|
1454
|
+
if (!items.length) continue;
|
|
1455
|
+
const size = gridItemSizeFor(items.length, columns);
|
|
1456
|
+
const settledItems = [];
|
|
1457
|
+
for (const item of items){
|
|
1458
|
+
var _nodes_item_props;
|
|
1459
|
+
if (nodes[item].componentId !== GRID) {
|
|
1460
|
+
var _host_nodes;
|
|
1461
|
+
// Only a direct child can be no Grid: a wrapper lends only sized Grid items.
|
|
1462
|
+
const wrapperId = mintId();
|
|
1463
|
+
const host = edit(id);
|
|
1464
|
+
host.nodes = ((_host_nodes = host.nodes) != null ? _host_nodes : []).map((child)=>child === item ? wrapperId : child);
|
|
1465
|
+
const shape = container;
|
|
1466
|
+
nodes[wrapperId] = _extends({}, '$id' in shape ? {
|
|
1467
|
+
$id: wrapperId
|
|
1468
|
+
} : {}, 'type' in shape ? {
|
|
1469
|
+
type: shape['type']
|
|
1470
|
+
} : {}, {
|
|
1471
|
+
componentId: GRID
|
|
1472
|
+
}, 'pluginId' in shape ? {
|
|
1473
|
+
pluginId: shape['pluginId']
|
|
1474
|
+
} : {}, 'parentId' in shape ? {
|
|
1475
|
+
parentId: id
|
|
1476
|
+
} : {}, {
|
|
1477
|
+
nodes: [
|
|
1478
|
+
item
|
|
1479
|
+
],
|
|
1480
|
+
props: {
|
|
1481
|
+
size
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
copied.add(wrapperId);
|
|
1485
|
+
if ('parentId' in nodes[item]) edit(item)['parentId'] = wrapperId;
|
|
1486
|
+
settledItems.push(wrapperId);
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
if (!gridItemSpan((_nodes_item_props = nodes[item].props) == null ? void 0 : _nodes_item_props['size'], columns).phoneFull) {
|
|
1490
|
+
var _writtenProp;
|
|
1491
|
+
var _nodes_item_props1;
|
|
1492
|
+
const own = writtenGridSizeText((_writtenProp = writtenProp(item, 'size')) != null ? _writtenProp : (_nodes_item_props1 = nodes[item].props) == null ? void 0 : _nodes_item_props1['size']);
|
|
1493
|
+
setSize(item, own !== null && gridItemSpan(own, columns).phoneFull ? own : size);
|
|
1494
|
+
}
|
|
1495
|
+
settledItems.push(item);
|
|
1496
|
+
}
|
|
1497
|
+
const steps = settledItems.some((item)=>{
|
|
1498
|
+
var _nodes_item_props;
|
|
1499
|
+
return gridItemSpan((_nodes_item_props = nodes[item].props) == null ? void 0 : _nodes_item_props['size'], columns).steps;
|
|
1500
|
+
});
|
|
1501
|
+
if (settledItems.length >= 2 && !steps) for (const item of settledItems)setSize(item, size);
|
|
1502
|
+
}
|
|
1503
|
+
return copied.size ? _extends({}, tree, {
|
|
1504
|
+
nodes
|
|
1505
|
+
}) : tree;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* `aiSettleGridItems` on a tree as the model wrote it, before the palette
|
|
1509
|
+
* validator reads it (AGL-3596): the tree check every kind but a page section
|
|
1510
|
+
* goes through stores what the validator makes of its answer, so the answer
|
|
1511
|
+
* is settled first. Anything that is no flat node map comes back as it was.
|
|
1512
|
+
*/ export function aiSettleWrittenGridItems(input) {
|
|
1513
|
+
if (!isRecord(input) || typeof input['rootId'] !== 'string' || !isRecord(input['nodes'])) return input;
|
|
1514
|
+
const nodes = Object.fromEntries(Object.entries(input['nodes']).filter(([, node])=>isRecord(node)));
|
|
1515
|
+
const tree = {
|
|
1516
|
+
rootId: input['rootId'],
|
|
1517
|
+
nodes: nodes
|
|
1518
|
+
};
|
|
1519
|
+
const settled = aiSettleGridItems(tree);
|
|
1520
|
+
return settled === tree ? input : _extends({}, input, {
|
|
1521
|
+
nodes: _extends({}, input['nodes'], settled.nodes)
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1261
1524
|
/** The palette colors a link or a button can take, and a band can be painted in. */ const LINK_COLOR_FAMILIES = new Set([
|
|
1262
1525
|
'primary',
|
|
1263
1526
|
'secondary',
|
|
@@ -2305,6 +2568,175 @@ const BUDGET_METRICS = [
|
|
|
2305
2568
|
}
|
|
2306
2569
|
return violations;
|
|
2307
2570
|
}
|
|
2571
|
+
/** The key a repeated item is written under (`AI_REPEAT_KEY` in `ai-repeated-items.ts`, which imports this file). */ const REPEAT_KEY = 'repeat';
|
|
2572
|
+
/**
|
|
2573
|
+
* A node map that does not agree with itself, settled where it has one
|
|
2574
|
+
* reading (AGL-3596), before `detectDisagreeingNodes` reads it. A guided
|
|
2575
|
+
* start's Home page stopped on each of these after its re-ask, and each is
|
|
2576
|
+
* one line the model left out, not a choice it made.
|
|
2577
|
+
*
|
|
2578
|
+
* - A name listed under `nodes` that the map never holds is taken out of the
|
|
2579
|
+
* list: there is nothing written to place there.
|
|
2580
|
+
* - An element written and listed by nobody goes where the model said it
|
|
2581
|
+
* goes, when its own `parentId` names an element the tree reaches that can
|
|
2582
|
+
* hold it. Failing that, a single such element goes in the single element
|
|
2583
|
+
* the tree reaches that can hold elements and holds none, which is the shape
|
|
2584
|
+
* of a row whose item was written beside it. Failing both, it is dropped,
|
|
2585
|
+
* with everything under it: nothing on the page holds it, and none of it
|
|
2586
|
+
* would ever be shown.
|
|
2587
|
+
* - A repeated item (`repeat`) is the exception: dropping it would lose every
|
|
2588
|
+
* card it draws, so one that cannot be placed is left for the re-ask.
|
|
2589
|
+
*
|
|
2590
|
+
* Reads and returns the tree as the model wrote it; an input with nothing to
|
|
2591
|
+
* settle comes back as the same object.
|
|
2592
|
+
*/ export function aiSettleDisagreeingNodes(input) {
|
|
2593
|
+
if (!isRecord(input) || typeof input['rootId'] !== 'string' || !isRecord(input['nodes'])) return input;
|
|
2594
|
+
const rootId = input['rootId'];
|
|
2595
|
+
const source = input['nodes'];
|
|
2596
|
+
if (!isRecord(source[rootId])) return input;
|
|
2597
|
+
const nodes = _extends({}, source);
|
|
2598
|
+
let changed = false;
|
|
2599
|
+
const listed = (id)=>{
|
|
2600
|
+
const node = nodes[id];
|
|
2601
|
+
return isRecord(node) && Array.isArray(node['nodes']) ? node['nodes'] : [];
|
|
2602
|
+
};
|
|
2603
|
+
const setListed = (id, children)=>{
|
|
2604
|
+
nodes[id] = _extends({}, nodes[id], {
|
|
2605
|
+
nodes: children
|
|
2606
|
+
});
|
|
2607
|
+
changed = true;
|
|
2608
|
+
};
|
|
2609
|
+
const holds = (id)=>{
|
|
2610
|
+
var _AI_PALETTE_node_componentId;
|
|
2611
|
+
const node = nodes[id];
|
|
2612
|
+
return isRecord(node) && typeof node['componentId'] === 'string' && ((_AI_PALETTE_node_componentId = AI_PALETTE[node['componentId']]) == null ? void 0 : _AI_PALETTE_node_componentId.acceptsChildren) === true;
|
|
2613
|
+
};
|
|
2614
|
+
const reach = ()=>{
|
|
2615
|
+
const reached = new Set([
|
|
2616
|
+
rootId
|
|
2617
|
+
]);
|
|
2618
|
+
const queue = [
|
|
2619
|
+
rootId
|
|
2620
|
+
];
|
|
2621
|
+
while(queue.length){
|
|
2622
|
+
for (const child of listed(queue.shift())){
|
|
2623
|
+
if (typeof child !== 'string' || reached.has(child) || !isRecord(nodes[child])) continue;
|
|
2624
|
+
reached.add(child);
|
|
2625
|
+
queue.push(child);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
return reached;
|
|
2629
|
+
};
|
|
2630
|
+
// Names that were never written come out of the lists that name them.
|
|
2631
|
+
for (const id of Object.keys(nodes)){
|
|
2632
|
+
const children = listed(id);
|
|
2633
|
+
const written = children.filter((child)=>typeof child !== 'string' || isRecord(nodes[child]));
|
|
2634
|
+
if (written.length !== children.length) setListed(id, written);
|
|
2635
|
+
}
|
|
2636
|
+
let reached = reach();
|
|
2637
|
+
const orphans = Object.keys(nodes).filter((id)=>!reached.has(id) && isRecord(nodes[id]));
|
|
2638
|
+
if (orphans.length) {
|
|
2639
|
+
const inOrphan = new Set(orphans.flatMap((id)=>listed(id)));
|
|
2640
|
+
const roots = orphans.filter((id)=>!inOrphan.has(id));
|
|
2641
|
+
const empty = [
|
|
2642
|
+
...reached
|
|
2643
|
+
].filter((id)=>id !== rootId && holds(id) && listed(id).length === 0);
|
|
2644
|
+
for (const id of roots){
|
|
2645
|
+
const named = nodes[id]['parentId'];
|
|
2646
|
+
const parent = typeof named === 'string' && reached.has(named) && holds(named) ? named : roots.length === 1 && empty.length === 1 ? empty[0] : null;
|
|
2647
|
+
if (parent) setListed(parent, [
|
|
2648
|
+
...listed(parent),
|
|
2649
|
+
id
|
|
2650
|
+
]);
|
|
2651
|
+
}
|
|
2652
|
+
reached = reach();
|
|
2653
|
+
const unplaced = Object.keys(nodes).filter((id)=>!reached.has(id) && isRecord(nodes[id]));
|
|
2654
|
+
// A repeated item, and what it holds, is kept for the re-ask to place.
|
|
2655
|
+
const kept = new Set();
|
|
2656
|
+
const queue = unplaced.filter((id)=>nodes[id][REPEAT_KEY] !== undefined);
|
|
2657
|
+
while(queue.length){
|
|
2658
|
+
const id = queue.shift();
|
|
2659
|
+
if (kept.has(id) || !isRecord(nodes[id])) continue;
|
|
2660
|
+
kept.add(id);
|
|
2661
|
+
for (const child of listed(id))if (typeof child === 'string') queue.push(child);
|
|
2662
|
+
}
|
|
2663
|
+
for (const id of unplaced){
|
|
2664
|
+
if (kept.has(id)) continue;
|
|
2665
|
+
delete nodes[id];
|
|
2666
|
+
changed = true;
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
return changed ? _extends({}, input, {
|
|
2670
|
+
nodes
|
|
2671
|
+
}) : input;
|
|
2672
|
+
}
|
|
2673
|
+
/** The fewest words a heading cut at a boundary keeps, so a cut never leaves a stub. */ const HEADING_CUT_MIN_WORDS = 3;
|
|
2674
|
+
/** The least share of the ceiling a heading cut at a boundary keeps. */ const HEADING_CUT_MIN_SHARE = 0.3;
|
|
2675
|
+
/** Where a sentence ends: its stop is kept. */ const SENTENCE_END = /[.!?](?=\s)/g;
|
|
2676
|
+
/** Where a clause ends: the mark is dropped. */ const CLAUSE_END = /\s*(?:[,;:]|\s[—–-])\s/g;
|
|
2677
|
+
/**
|
|
2678
|
+
* The longest whole part of a heading that fits `limit`: up to the last
|
|
2679
|
+
* sentence that ends within it, else the last clause, or `null` where neither
|
|
2680
|
+
* does, or where what is left is a stub.
|
|
2681
|
+
*/ function headingWithin(line, limit) {
|
|
2682
|
+
const keeps = (head)=>head.split(/\s+/).length >= HEADING_CUT_MIN_WORDS && head.length >= limit * HEADING_CUT_MIN_SHARE;
|
|
2683
|
+
for (const [pattern, keepMark] of [
|
|
2684
|
+
[
|
|
2685
|
+
SENTENCE_END,
|
|
2686
|
+
true
|
|
2687
|
+
],
|
|
2688
|
+
[
|
|
2689
|
+
CLAUSE_END,
|
|
2690
|
+
false
|
|
2691
|
+
]
|
|
2692
|
+
]){
|
|
2693
|
+
let best = null;
|
|
2694
|
+
for (const match of line.matchAll(pattern)){
|
|
2695
|
+
var _match_index;
|
|
2696
|
+
const head = line.slice(0, ((_match_index = match.index) != null ? _match_index : 0) + (keepMark ? match[0].length : 0)).trim();
|
|
2697
|
+
if (head.length > limit) break;
|
|
2698
|
+
best = head;
|
|
2699
|
+
}
|
|
2700
|
+
if (best !== null && keeps(best)) return best;
|
|
2701
|
+
}
|
|
2702
|
+
return null;
|
|
2703
|
+
}
|
|
2704
|
+
/**
|
|
2705
|
+
* A heading written past the headline ceiling, settled where it has one
|
|
2706
|
+
* reading (AGL-3596). The palette validator cuts such a line where the
|
|
2707
|
+
* ceiling falls, mid-word, and the check refuses the cut (rule 14,
|
|
2708
|
+
* `copy-cut-at-ceiling`); live guided starts' heroes stopped on it after
|
|
2709
|
+
* their re-ask. A heading whose first sentence or clause ends within the
|
|
2710
|
+
* ceiling is a whole line already, so it is kept to that, its later words
|
|
2711
|
+
* dropped: "Free inspections after a storm, with photos of every shingle we
|
|
2712
|
+
* replace and a written…" becomes "Free inspections after a storm". A heading
|
|
2713
|
+
* with no such boundary, or whose part within is a stub, is left for the
|
|
2714
|
+
* re-ask. Reads and returns the tree as the model wrote it.
|
|
2715
|
+
*/ export function aiSettleCutHeadings(input) {
|
|
2716
|
+
var _ref;
|
|
2717
|
+
var _AI_PALETTE_muiTypography_textLimits, _AI_PALETTE_muiTypography;
|
|
2718
|
+
if (!isRecord(input) || !isRecord(input['nodes'])) return input;
|
|
2719
|
+
const limit = Math.min((_ref = (_AI_PALETTE_muiTypography = AI_PALETTE['muiTypography']) == null ? void 0 : (_AI_PALETTE_muiTypography_textLimits = _AI_PALETTE_muiTypography.textLimits) == null ? void 0 : _AI_PALETTE_muiTypography_textLimits['children']) != null ? _ref : Infinity, AI_TEXT_LIMITS.headline);
|
|
2720
|
+
let nodes = null;
|
|
2721
|
+
for (const [id, node] of Object.entries(input['nodes'])){
|
|
2722
|
+
if (!isRecord(node) || node['componentId'] !== 'muiTypography' || !isRecord(node['props'])) continue;
|
|
2723
|
+
const line = node['props']['children'];
|
|
2724
|
+
if (!isHeadlineVariant(node['props']['variant']) || typeof line !== 'string') continue;
|
|
2725
|
+
const trimmed = line.trim();
|
|
2726
|
+
if (trimmed.length <= limit || trimmed.includes('{{')) continue;
|
|
2727
|
+
const head = headingWithin(trimmed, limit);
|
|
2728
|
+
if (head === null) continue;
|
|
2729
|
+
nodes != null ? nodes : nodes = _extends({}, input['nodes']);
|
|
2730
|
+
nodes[id] = _extends({}, node, {
|
|
2731
|
+
props: _extends({}, node['props'], {
|
|
2732
|
+
children: head
|
|
2733
|
+
})
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
return nodes ? _extends({}, input, {
|
|
2737
|
+
nodes
|
|
2738
|
+
}) : input;
|
|
2739
|
+
}
|
|
2308
2740
|
/**
|
|
2309
2741
|
* The whole check on one emitted tree: the palette validator first, then
|
|
2310
2742
|
* every tree rule, then the budget. `otherPages` are the trees generated
|