@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
|
@@ -0,0 +1,1235 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Aglyn LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/ import { REUSABLE_INSTANCE_COMPONENT_ID } from "@aglyn/aglyn/app-utils/reusable-component-keys";
|
|
19
|
+
import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
|
|
20
|
+
import { AI_ICON_COMPONENT_ID, aiIconOfWord, AI_ICON_LIBRARY } from "../runtime/ai-icon-library.js";
|
|
21
|
+
import { AI_INSTANCE_REF_PROP } from "../runtime/ai-node-tree.js";
|
|
22
|
+
import { aiPageScrollInteraction } from "../runtime/ai-page-links.js";
|
|
23
|
+
import { aiLayoutFitText, aiLayoutQuoteGap, aiLayoutWords } from "./ai-layout-copy.js";
|
|
24
|
+
import { AI_LAYOUT_GROUP_KINDS, AI_LAYOUT_MAX_ITEMS } from "./ai-layout-language.js";
|
|
25
|
+
import { aiLayoutResolveLink } from "./ai-layout-links.js";
|
|
26
|
+
import { AI_LAYOUT_FRAMES, AiLayoutTreeBuilder } from "./ai-layout-tree.js";
|
|
27
|
+
/** A section root's id where the caller gives none. */ function sectionIdOf(index) {
|
|
28
|
+
return `section-${index + 1}`;
|
|
29
|
+
}
|
|
30
|
+
/** The most pictures a page carries; each beyond it is one more empty slot to fill. */ export const AI_LAYOUT_MAX_IMAGES = 3;
|
|
31
|
+
/** The most `brand` or `dark` bands a page shows; more and none stands out. */ export const AI_LAYOUT_MAX_STRONG_BANDS = 2;
|
|
32
|
+
const SECTION_PADDING = {
|
|
33
|
+
xs: 8,
|
|
34
|
+
md: 12
|
|
35
|
+
};
|
|
36
|
+
const HERO_PADDING = {
|
|
37
|
+
xs: 10,
|
|
38
|
+
md: 14
|
|
39
|
+
};
|
|
40
|
+
const formIdsOf = (targets)=>new Set(targets.forms.map((form)=>form.id));
|
|
41
|
+
const componentOf = (targets, id)=>{
|
|
42
|
+
var _targets_components_find;
|
|
43
|
+
return id ? (_targets_components_find = targets.components.find((component)=>component.id === id)) != null ? _targets_components_find : null : null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The section of this page that places a form: one whose plan places one, or
|
|
47
|
+
* whose design places one by its id.
|
|
48
|
+
*/ function formSectionOf(sections, plan, targets) {
|
|
49
|
+
const forms = formIdsOf(targets);
|
|
50
|
+
const planned = plan.sections.findIndex((section)=>section.uses.some((ref)=>forms.has(ref)));
|
|
51
|
+
if (planned !== -1) return planned;
|
|
52
|
+
const designed = sections.findIndex((section)=>section.blocks.some((block)=>block.kind === 'form' && (!block.to || forms.has(block.to))));
|
|
53
|
+
return designed === -1 ? null : designed;
|
|
54
|
+
}
|
|
55
|
+
/** Compiles a page designed in the layout language into the tree it stores. */ export function aiCompileLayoutPage(sections, plan, targets, options) {
|
|
56
|
+
const tree = new AiLayoutTreeBuilder();
|
|
57
|
+
const page = {
|
|
58
|
+
tree,
|
|
59
|
+
plan,
|
|
60
|
+
targets,
|
|
61
|
+
options,
|
|
62
|
+
settled: [],
|
|
63
|
+
scrollTo: {},
|
|
64
|
+
images: 0,
|
|
65
|
+
strong: {
|
|
66
|
+
brand: 0,
|
|
67
|
+
dark: 0
|
|
68
|
+
},
|
|
69
|
+
formSection: formSectionOf(sections, plan, targets),
|
|
70
|
+
pageIcon: pageIconOf(sections),
|
|
71
|
+
formsPlaced: new Set(),
|
|
72
|
+
itemIds: plan.sections.map(()=>[])
|
|
73
|
+
};
|
|
74
|
+
const roots = plan.sections.map((_, index)=>{
|
|
75
|
+
var _sections_index;
|
|
76
|
+
const section = (_sections_index = sections[index]) != null ? _sections_index : {
|
|
77
|
+
blocks: []
|
|
78
|
+
};
|
|
79
|
+
return compileSection(page, section, index);
|
|
80
|
+
});
|
|
81
|
+
const rootId = tree.add('div', null, null, roots, 'page', CANVAS_ROOT_ELEMENT_ID);
|
|
82
|
+
return {
|
|
83
|
+
tree: {
|
|
84
|
+
rootId,
|
|
85
|
+
nodes: tree.nodes
|
|
86
|
+
},
|
|
87
|
+
sectionRoots: roots,
|
|
88
|
+
scrollTo: page.scrollTo,
|
|
89
|
+
settled: page.settled,
|
|
90
|
+
itemIds: page.itemIds
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** The first icon the design names anywhere, else a sparkle: what an empty picture shows. */ function pageIconOf(sections) {
|
|
94
|
+
for (const section of sections){
|
|
95
|
+
for (const block of section.blocks){
|
|
96
|
+
var _block_items;
|
|
97
|
+
const own = aiIconOfWord(block.icon);
|
|
98
|
+
if (own) return own;
|
|
99
|
+
for (const item of (_block_items = block.items) != null ? _block_items : []){
|
|
100
|
+
const icon = aiIconOfWord(item.icon);
|
|
101
|
+
if (icon) return icon;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return AI_ICON_LIBRARY.sparkle;
|
|
106
|
+
}
|
|
107
|
+
// ── A section ─────────────────────────────────────────────────────────────
|
|
108
|
+
function compileSection(page, raw, index) {
|
|
109
|
+
var _raw_band, _ref;
|
|
110
|
+
var _placedHere_, _page_plan_sections_index_name, _page_plan_sections_index, _page_options_sectionIds;
|
|
111
|
+
const at = `sections[${index}]`;
|
|
112
|
+
const band = settleBand(page, (_raw_band = raw.band) != null ? _raw_band : 'plain', at);
|
|
113
|
+
const scope = {
|
|
114
|
+
page,
|
|
115
|
+
index,
|
|
116
|
+
at,
|
|
117
|
+
band,
|
|
118
|
+
centered: raw.align === 'center',
|
|
119
|
+
level: index === 0 ? 1 : 2,
|
|
120
|
+
headed: false,
|
|
121
|
+
itemsLeft: AI_LAYOUT_MAX_ITEMS,
|
|
122
|
+
link: {
|
|
123
|
+
sections: page.plan.sections.map((section)=>section.name),
|
|
124
|
+
from: index,
|
|
125
|
+
formSection: page.formSection
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// A quote is a gap the owner fills, and a published page shows no gap (AGL-3660).
|
|
129
|
+
const blocks = placePlanned(scope, raw).filter((block)=>{
|
|
130
|
+
if (block.kind !== 'quotes') return true;
|
|
131
|
+
page.settled.push({
|
|
132
|
+
at,
|
|
133
|
+
what: 'a quotes group left out: a published page shows no customer words the brief did not give'
|
|
134
|
+
});
|
|
135
|
+
return false;
|
|
136
|
+
});
|
|
137
|
+
const cols = raw.cols && raw.cols.length >= 2 ? raw.cols : null;
|
|
138
|
+
// Above the row, the row's columns, and below it.
|
|
139
|
+
const head = [];
|
|
140
|
+
const tail = [];
|
|
141
|
+
const columns = cols ? cols.map(()=>[]) : [];
|
|
142
|
+
let placed = false;
|
|
143
|
+
for (const block of blocks){
|
|
144
|
+
if (cols && block.col !== undefined) {
|
|
145
|
+
columns[Math.min(block.col, cols.length - 1)].push(block);
|
|
146
|
+
placed = true;
|
|
147
|
+
} else if (placed) {
|
|
148
|
+
tail.push(block);
|
|
149
|
+
} else {
|
|
150
|
+
head.push(block);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
let filled = columns.map((column, position)=>{
|
|
154
|
+
var _ref;
|
|
155
|
+
return {
|
|
156
|
+
column,
|
|
157
|
+
weight: (_ref = cols == null ? void 0 : cols[position]) != null ? _ref : 1
|
|
158
|
+
};
|
|
159
|
+
}).filter((entry)=>entry.column.length);
|
|
160
|
+
// A row whose columns each place the same component, one instance a
|
|
161
|
+
// column or more, is that component's group of cards: the compiler lays
|
|
162
|
+
// its instances out once, rather than one like Grid a column (rule 1).
|
|
163
|
+
// Row by row, as the columns showed them: the first of each column, then the second.
|
|
164
|
+
const deepest = Math.max(0, ...filled.map((entry)=>entry.column.length));
|
|
165
|
+
const placedHere = Array.from({
|
|
166
|
+
length: deepest
|
|
167
|
+
}, (_, row)=>filled.flatMap((entry)=>entry.column[row] ? [
|
|
168
|
+
entry.column[row]
|
|
169
|
+
] : [])).flat();
|
|
170
|
+
const repeated = ((_placedHere_ = placedHere[0]) == null ? void 0 : _placedHere_.kind) === 'component' ? componentOf(page.targets, placedHere[0].to) : null;
|
|
171
|
+
if (filled.length >= 2 && repeated && placedHere.every((block)=>block.kind === 'component' && block.to === repeated.id)) {
|
|
172
|
+
head.push({
|
|
173
|
+
kind: 'cards',
|
|
174
|
+
to: repeated.id,
|
|
175
|
+
items: placedHere.map((block)=>componentItem(block, repeated.props))
|
|
176
|
+
});
|
|
177
|
+
page.settled.push({
|
|
178
|
+
at,
|
|
179
|
+
what: `${placedHere.length} ${repeated.name} instances in ${filled.length} columns drawn as one group`
|
|
180
|
+
});
|
|
181
|
+
filled = [];
|
|
182
|
+
}
|
|
183
|
+
// A row whose columns each hold only one kind of group — a card a column —
|
|
184
|
+
// is that group, laid out by the compiler rather than column by column.
|
|
185
|
+
const groups = filled.flatMap((entry)=>entry.column);
|
|
186
|
+
if (filled.length >= 2 && groups.every((block)=>isItemGroup(block) && block.kind === groups[0].kind && block.to === groups[0].to)) {
|
|
187
|
+
head.push(_extends({}, groups[0], {
|
|
188
|
+
col: undefined,
|
|
189
|
+
items: groups.flatMap((block)=>{
|
|
190
|
+
var _block_items;
|
|
191
|
+
return (_block_items = block.items) != null ? _block_items : [];
|
|
192
|
+
})
|
|
193
|
+
}));
|
|
194
|
+
page.settled.push({
|
|
195
|
+
at,
|
|
196
|
+
what: `a ${groups[0].kind} group split over ${filled.length} columns drawn as one`
|
|
197
|
+
});
|
|
198
|
+
filled = [];
|
|
199
|
+
}
|
|
200
|
+
// A row whose columns all say the same kind of thing, three or more across,
|
|
201
|
+
// is a group of cards: one design, the words the columns gave.
|
|
202
|
+
if (filled.length >= 3 && sameShape(filled.map((entry)=>entry.column))) {
|
|
203
|
+
head.push(columnsAsCards(filled.map((entry)=>entry.column)));
|
|
204
|
+
page.settled.push({
|
|
205
|
+
at,
|
|
206
|
+
what: `${filled.length} like columns drawn as cards`
|
|
207
|
+
});
|
|
208
|
+
filled = [];
|
|
209
|
+
}
|
|
210
|
+
if (filled.length === 1) {
|
|
211
|
+
// One column with anything in it is no row.
|
|
212
|
+
head.push(...filled[0].column);
|
|
213
|
+
filled = [];
|
|
214
|
+
}
|
|
215
|
+
// The section's heading leads its outline: a heading with no words is no
|
|
216
|
+
// heading, the first section always carries the page's one h1, and a
|
|
217
|
+
// heading written after a group whose titles sit under it moves above it.
|
|
218
|
+
const inOrder = ()=>head.concat(filled.flatMap((entry)=>entry.column), tail);
|
|
219
|
+
const speaks = (block)=>block.kind === 'heading' && !!aiLayoutWords(block.text, 'heading', page.targets.facts);
|
|
220
|
+
for (const list of [
|
|
221
|
+
head,
|
|
222
|
+
tail,
|
|
223
|
+
...filled.map((entry)=>entry.column)
|
|
224
|
+
]){
|
|
225
|
+
for(let at = list.length - 1; at >= 0; at -= 1)if (list[at].kind === 'heading' && !speaks(list[at])) list.splice(at, 1);
|
|
226
|
+
}
|
|
227
|
+
if (index === 0 && !inOrder().some(speaks)) {
|
|
228
|
+
head.unshift({
|
|
229
|
+
kind: 'heading',
|
|
230
|
+
text: page.plan.title
|
|
231
|
+
});
|
|
232
|
+
page.settled.push({
|
|
233
|
+
at,
|
|
234
|
+
what: "no heading; the page's title written as its h1"
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const order = inOrder();
|
|
238
|
+
const first = order.findIndex(speaks);
|
|
239
|
+
if (first > 0 && order.slice(0, first).some((block)=>block.kind === 'cards' || block.kind === 'steps' || block.kind === 'component')) {
|
|
240
|
+
const lead = order[first];
|
|
241
|
+
for (const list of [
|
|
242
|
+
head,
|
|
243
|
+
tail,
|
|
244
|
+
...filled.map((entry)=>entry.column)
|
|
245
|
+
]){
|
|
246
|
+
const at = list.indexOf(lead);
|
|
247
|
+
if (at !== -1) list.splice(at, 1);
|
|
248
|
+
}
|
|
249
|
+
head.unshift(_extends({}, lead, {
|
|
250
|
+
col: undefined
|
|
251
|
+
}));
|
|
252
|
+
page.settled.push({
|
|
253
|
+
at,
|
|
254
|
+
what: 'the heading moved above the group it heads'
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
filled = filled.filter((entry)=>entry.column.length);
|
|
258
|
+
if (filled.length === 1) {
|
|
259
|
+
head.push(...filled[0].column);
|
|
260
|
+
filled = [];
|
|
261
|
+
}
|
|
262
|
+
const wide = filled.length > 0 || [
|
|
263
|
+
...head,
|
|
264
|
+
...tail
|
|
265
|
+
].some((block)=>isWideGroup(block));
|
|
266
|
+
const parts = [];
|
|
267
|
+
const headId = compileFlow(scope, head, 'full');
|
|
268
|
+
if (headId) parts.push(centered(scope, headId, wide));
|
|
269
|
+
if (filled.length) parts.push(compileRow(scope, filled));
|
|
270
|
+
const tailId = compileFlow(scope, tail, 'full');
|
|
271
|
+
if (tailId) parts.push(centered(scope, tailId, wide));
|
|
272
|
+
// A frame whose content came to nothing is taken out, leaves first.
|
|
273
|
+
for(let at = parts.length - 1; at >= 0; at -= 1){
|
|
274
|
+
var _part_nodes;
|
|
275
|
+
page.tree.prune(parts[at]);
|
|
276
|
+
const part = page.tree.nodes[parts[at]];
|
|
277
|
+
if (AI_LAYOUT_FRAMES.has(part.componentId) && !((_part_nodes = part.nodes) == null ? void 0 : _part_nodes.length)) {
|
|
278
|
+
delete page.tree.nodes[parts[at]];
|
|
279
|
+
parts.splice(at, 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!parts.length) {
|
|
283
|
+
var _page_plan_sections_index1;
|
|
284
|
+
// Nothing the design gave this section could be shown: it keeps its
|
|
285
|
+
// place on the page under the name its plan gives it.
|
|
286
|
+
const fallback = compileFlow(scope, [
|
|
287
|
+
{
|
|
288
|
+
kind: 'heading',
|
|
289
|
+
text: ((_page_plan_sections_index1 = page.plan.sections[index]) == null ? void 0 : _page_plan_sections_index1.name) || page.plan.title
|
|
290
|
+
}
|
|
291
|
+
], 'full');
|
|
292
|
+
if (fallback) parts.push(fallback);
|
|
293
|
+
page.settled.push({
|
|
294
|
+
at,
|
|
295
|
+
what: 'nothing to show; the section named by its plan'
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const tree = page.tree;
|
|
299
|
+
const content = parts.length === 1 ? parts[0] : // margins, which un-centers a reading-width head (AGL-3660).
|
|
300
|
+
tree.add('muiStack', {
|
|
301
|
+
spacing: '6',
|
|
302
|
+
useFlexGap: true
|
|
303
|
+
}, null, parts, 'content');
|
|
304
|
+
const container = tree.add('muiContainer', {
|
|
305
|
+
maxWidth: wide ? 'lg' : 'md'
|
|
306
|
+
}, {
|
|
307
|
+
py: index === 0 ? _extends({}, HERO_PADDING) : _extends({}, SECTION_PADDING)
|
|
308
|
+
}, [
|
|
309
|
+
content
|
|
310
|
+
], 'container');
|
|
311
|
+
const name = ((_page_plan_sections_index = page.plan.sections[index]) == null ? void 0 : (_page_plan_sections_index_name = _page_plan_sections_index.name) == null ? void 0 : _page_plan_sections_index_name.trim()) || `Section ${index + 1}`;
|
|
312
|
+
return tree.add('section', _extends({
|
|
313
|
+
element: 'section',
|
|
314
|
+
ariaLabel: aiLayoutFitText(name, 'note') || `Section ${index + 1}`
|
|
315
|
+
}, band === 'dark' ? {
|
|
316
|
+
colorScheme: 'dark'
|
|
317
|
+
} : {}), bandSx(band), [
|
|
318
|
+
container
|
|
319
|
+
], 'section', (_ref = (_page_options_sectionIds = page.options.sectionIds) == null ? void 0 : _page_options_sectionIds[index]) != null ? _ref : sectionIdOf(index));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* A component block's prop values as a card item: its title-like prop as the
|
|
323
|
+
* title and its text-like prop as the text, which `instanceValues` writes
|
|
324
|
+
* back into the same props.
|
|
325
|
+
*/ function componentItem(block, props) {
|
|
326
|
+
var _block_items, _block_items1;
|
|
327
|
+
// An item that names no prop is the card itself, written as a cards item.
|
|
328
|
+
const own = ((_block_items = block.items) != null ? _block_items : []).find((item)=>!(item.title in props) && (item.title.trim() || item.text.trim()));
|
|
329
|
+
if (own && !((_block_items1 = block.items) != null ? _block_items1 : []).some((item)=>item.title in props)) return {
|
|
330
|
+
title: own.title,
|
|
331
|
+
text: own.text
|
|
332
|
+
};
|
|
333
|
+
const value = (pattern)=>{
|
|
334
|
+
var _ref, _block_items;
|
|
335
|
+
var _find;
|
|
336
|
+
return (_ref = (_find = ((_block_items = block.items) != null ? _block_items : []).find((item)=>item.title in props && pattern.test(item.title) && item.text.trim())) == null ? void 0 : _find.text) != null ? _ref : '';
|
|
337
|
+
};
|
|
338
|
+
const title = value(/title|name|heading|label|question|figure|value/i) || block.text || '';
|
|
339
|
+
const text = value(/text|description|body|summary|copy|answer|detail|caption/i);
|
|
340
|
+
return {
|
|
341
|
+
title,
|
|
342
|
+
text
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/** A band the page has room for: past two brand or two dark bands, a soft one instead. */ function settleBand(page, band, at) {
|
|
346
|
+
if (band !== 'brand' && band !== 'dark') return band;
|
|
347
|
+
if (page.strong[band] >= AI_LAYOUT_MAX_STRONG_BANDS) {
|
|
348
|
+
page.settled.push({
|
|
349
|
+
at: `${at}.band`,
|
|
350
|
+
what: `a third ${band} band drawn soft`
|
|
351
|
+
});
|
|
352
|
+
return 'soft';
|
|
353
|
+
}
|
|
354
|
+
page.strong[band] += 1;
|
|
355
|
+
return band;
|
|
356
|
+
}
|
|
357
|
+
/** A band's own colors, as palette roles: the dark band pins the scheme instead. */ export function bandSx(band) {
|
|
358
|
+
if (band === 'soft') return {
|
|
359
|
+
bgcolor: 'background.paper'
|
|
360
|
+
};
|
|
361
|
+
if (band === 'brand') return {
|
|
362
|
+
bgcolor: 'primary.main',
|
|
363
|
+
color: 'primary.contrastText'
|
|
364
|
+
};
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
/** A group whose items sit side by side. */ function isItemGroup(block) {
|
|
368
|
+
return block.kind === 'cards' || block.kind === 'steps' || block.kind === 'stats' || block.kind === 'quotes';
|
|
369
|
+
}
|
|
370
|
+
/** Whether a block spans more than a reading column: a row of several items. */ function isWideGroup(block) {
|
|
371
|
+
var _ref;
|
|
372
|
+
var _block_items;
|
|
373
|
+
return (block.kind === 'cards' || block.kind === 'steps' || block.kind === 'stats' || block.kind === 'quotes') && ((_ref = (_block_items = block.items) == null ? void 0 : _block_items.length) != null ? _ref : 0) >= 2;
|
|
374
|
+
}
|
|
375
|
+
/** A full-width flow centered on a wide section is held to a reading width. */ function centered(scope, flowId, wide) {
|
|
376
|
+
var _flow_nodes;
|
|
377
|
+
const flow = scope.page.tree.nodes[flowId];
|
|
378
|
+
const holdsGroup = ((_flow_nodes = flow.nodes) != null ? _flow_nodes : []).some((id)=>{
|
|
379
|
+
var _scope_page_tree_nodes_id;
|
|
380
|
+
return ((_scope_page_tree_nodes_id = scope.page.tree.nodes[id]) == null ? void 0 : _scope_page_tree_nodes_id.componentId) === 'muiGrid';
|
|
381
|
+
});
|
|
382
|
+
if (!wide || !scope.centered || holdsGroup) return flowId;
|
|
383
|
+
return scope.page.tree.add('muiContainer', {
|
|
384
|
+
maxWidth: 'md',
|
|
385
|
+
disableGutters: true
|
|
386
|
+
}, null, [
|
|
387
|
+
flowId
|
|
388
|
+
], 'measure');
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The section's blocks with what its plan places in it: a form or a
|
|
392
|
+
* component the plan line names and the design left out is added where the
|
|
393
|
+
* section ends, and a `form` or `component` block naming nothing takes the
|
|
394
|
+
* one the plan line names.
|
|
395
|
+
*/ function placePlanned(scope, raw) {
|
|
396
|
+
var _ref;
|
|
397
|
+
var _page_plan_sections_index;
|
|
398
|
+
const { page, index, at } = scope;
|
|
399
|
+
const forms = formIdsOf(page.targets);
|
|
400
|
+
const uses = (_ref = (_page_plan_sections_index = page.plan.sections[index]) == null ? void 0 : _page_plan_sections_index.uses) != null ? _ref : [];
|
|
401
|
+
const plannedForms = uses.filter((ref)=>forms.has(ref));
|
|
402
|
+
const plannedComponents = uses.filter((ref)=>componentOf(page.targets, ref));
|
|
403
|
+
const blocks = raw.blocks.map((block)=>_extends({}, block, block.items ? {
|
|
404
|
+
items: [
|
|
405
|
+
...block.items
|
|
406
|
+
]
|
|
407
|
+
} : {}));
|
|
408
|
+
const lastCol = raw.cols && raw.cols.length >= 2 ? raw.cols.length - 1 : undefined;
|
|
409
|
+
for (const block of blocks){
|
|
410
|
+
var _block_items;
|
|
411
|
+
if (block.kind === 'form' && !(block.to && forms.has(block.to))) {
|
|
412
|
+
var _plannedForms_;
|
|
413
|
+
const only = page.targets.forms.length === 1 ? page.targets.forms[0].id : undefined;
|
|
414
|
+
block.to = (_plannedForms_ = plannedForms[0]) != null ? _plannedForms_ : only;
|
|
415
|
+
}
|
|
416
|
+
if ((block.kind === 'component' || block.kind === 'cards') && block.to && !componentOf(page.targets, block.to)) {
|
|
417
|
+
if (block.kind === 'component') block.to = plannedComponents[0];
|
|
418
|
+
else delete block.to;
|
|
419
|
+
}
|
|
420
|
+
if (block.kind === 'component' && !block.to) block.to = plannedComponents[0];
|
|
421
|
+
// A group whose every item names the same component is that component's
|
|
422
|
+
// placements (AGL-3660): the design put the id on its items, not the block.
|
|
423
|
+
if (AI_LAYOUT_GROUP_KINDS.has(block.kind) && !block.to && ((_block_items = block.items) == null ? void 0 : _block_items.length)) {
|
|
424
|
+
var _block_items_;
|
|
425
|
+
const named = (_block_items_ = block.items[0]) == null ? void 0 : _block_items_.to;
|
|
426
|
+
if (named && componentOf(page.targets, named) && block.items.every((item)=>item.to === named)) {
|
|
427
|
+
block.to = named;
|
|
428
|
+
block.items = block.items.map((_0)=>{
|
|
429
|
+
let { to: _to } = _0, item = _object_without_properties_loose(_0, [
|
|
430
|
+
"to"
|
|
431
|
+
]);
|
|
432
|
+
return item;
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
for (const form of plannedForms){
|
|
438
|
+
if (!blocks.some((block)=>block.kind === 'form' && block.to === form)) {
|
|
439
|
+
blocks.push(_extends({
|
|
440
|
+
kind: 'form',
|
|
441
|
+
to: form
|
|
442
|
+
}, lastCol !== undefined ? {
|
|
443
|
+
col: lastCol
|
|
444
|
+
} : {}));
|
|
445
|
+
page.settled.push({
|
|
446
|
+
at,
|
|
447
|
+
what: `the planned form ${form} placed`
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const component of plannedComponents){
|
|
452
|
+
if (blocks.some((block)=>(block.kind === 'component' || block.kind === 'cards') && block.to === component)) continue;
|
|
453
|
+
// A group the design drew where the plan places a component is that component's placements.
|
|
454
|
+
const group = blocks.find((block)=>AI_LAYOUT_GROUP_KINDS.has(block.kind) && block.kind !== 'faq' && block.kind !== 'list' && !block.to);
|
|
455
|
+
if (group) {
|
|
456
|
+
// Every group of that kind the design split over the row's columns, so
|
|
457
|
+
// each item of the row is drawn alike (AGL-3660), never the first alone.
|
|
458
|
+
for (const like of blocks){
|
|
459
|
+
if (like.kind === group.kind && !like.to) like.to = component;
|
|
460
|
+
}
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
blocks.push(_extends({
|
|
464
|
+
kind: 'component',
|
|
465
|
+
to: component
|
|
466
|
+
}, lastCol !== undefined ? {
|
|
467
|
+
col: lastCol
|
|
468
|
+
} : {}));
|
|
469
|
+
page.settled.push({
|
|
470
|
+
at,
|
|
471
|
+
what: `the planned component ${component} placed`
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return blocks.filter((block)=>{
|
|
475
|
+
if ((block.kind === 'form' || block.kind === 'component') && !block.to) {
|
|
476
|
+
page.settled.push({
|
|
477
|
+
at,
|
|
478
|
+
what: `a ${block.kind} with nothing to place; left out`
|
|
479
|
+
});
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
// A form is placed once a page: a second copy of the same form asks a
|
|
483
|
+
// visitor the same thing twice.
|
|
484
|
+
if (block.kind === 'form' && block.to) {
|
|
485
|
+
if (page.formsPlaced.has(block.to)) {
|
|
486
|
+
page.settled.push({
|
|
487
|
+
at,
|
|
488
|
+
what: `the form ${block.to} again; left out`
|
|
489
|
+
});
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
page.formsPlaced.add(block.to);
|
|
493
|
+
}
|
|
494
|
+
return true;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
/** The block kinds a column holds, as a column's shape is compared. */ function shapeOf(column) {
|
|
498
|
+
return column.map((block)=>block.kind).join(',');
|
|
499
|
+
}
|
|
500
|
+
function sameShape(columns) {
|
|
501
|
+
const first = shapeOf(columns[0]);
|
|
502
|
+
return columns.every((column)=>shapeOf(column) === first) && columns[0].some((block)=>block.kind === 'heading') && columns[0].every((block)=>[
|
|
503
|
+
'heading',
|
|
504
|
+
'text',
|
|
505
|
+
'lede',
|
|
506
|
+
'eyebrow',
|
|
507
|
+
'image',
|
|
508
|
+
'note'
|
|
509
|
+
].includes(block.kind));
|
|
510
|
+
}
|
|
511
|
+
/** Like columns as one group of cards: each column's heading its title, its words its text. */ function columnsAsCards(columns) {
|
|
512
|
+
return {
|
|
513
|
+
kind: 'cards',
|
|
514
|
+
items: columns.map((column)=>{
|
|
515
|
+
var _ref;
|
|
516
|
+
var _column_find, _column_find1;
|
|
517
|
+
const title = (_ref = (_column_find = column.find((block)=>block.kind === 'heading')) == null ? void 0 : _column_find.text) != null ? _ref : '';
|
|
518
|
+
const words = column.filter((block)=>block.kind === 'text' || block.kind === 'lede' || block.kind === 'note').map((block)=>{
|
|
519
|
+
var _block_text;
|
|
520
|
+
return (_block_text = block.text) != null ? _block_text : '';
|
|
521
|
+
}).join(' ');
|
|
522
|
+
const icon = (_column_find1 = column.find((block)=>block.icon)) == null ? void 0 : _column_find1.icon;
|
|
523
|
+
return _extends({
|
|
524
|
+
title,
|
|
525
|
+
text: words
|
|
526
|
+
}, icon ? {
|
|
527
|
+
icon
|
|
528
|
+
} : {});
|
|
529
|
+
})
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
// ── A row of columns ──────────────────────────────────────────────────────
|
|
533
|
+
/** Spans out of twelve for relative widths: each at least three, summing to twelve. */ export function aiLayoutSpans(weights) {
|
|
534
|
+
const total = weights.reduce((sum, weight)=>sum + weight, 0);
|
|
535
|
+
const spans = weights.map((weight)=>Math.max(3, Math.round(weight / total * 12)));
|
|
536
|
+
let over = spans.reduce((sum, span)=>sum + span, 0) - 12;
|
|
537
|
+
while(over !== 0){
|
|
538
|
+
// Take from (or give to) the widest column that can spare it.
|
|
539
|
+
const order = spans.map((span, position)=>({
|
|
540
|
+
span,
|
|
541
|
+
position
|
|
542
|
+
})).sort((a, b)=>over > 0 ? b.span - a.span : a.span - b.span);
|
|
543
|
+
const pick = order.find((entry)=>over > 0 ? entry.span > 3 : true);
|
|
544
|
+
if (!pick) break;
|
|
545
|
+
spans[pick.position] += over > 0 ? -1 : 1;
|
|
546
|
+
over += over > 0 ? -1 : 1;
|
|
547
|
+
}
|
|
548
|
+
return spans;
|
|
549
|
+
}
|
|
550
|
+
export function compileRow(scope, columns) {
|
|
551
|
+
const tree = scope.page.tree;
|
|
552
|
+
const spans = aiLayoutSpans(columns.map((entry)=>entry.weight));
|
|
553
|
+
const items = columns.map((entry, position)=>{
|
|
554
|
+
const span = spans[position];
|
|
555
|
+
const flow = compileFlow(scope, entry.column, span >= 8 ? 'full' : span >= 5 ? 'half' : 'narrow');
|
|
556
|
+
const size = columns.length >= 4 ? `xs:12 sm:6 md:${span}` : `xs:12 md:${span}`;
|
|
557
|
+
return tree.add('muiGrid', {
|
|
558
|
+
size
|
|
559
|
+
}, null, [
|
|
560
|
+
flow
|
|
561
|
+
], 'column');
|
|
562
|
+
});
|
|
563
|
+
return tree.add('muiGrid', {
|
|
564
|
+
container: true,
|
|
565
|
+
spacing: columns.length >= 3 ? '4' : '6'
|
|
566
|
+
}, {
|
|
567
|
+
alignItems: 'center'
|
|
568
|
+
}, items, 'row');
|
|
569
|
+
}
|
|
570
|
+
/** A column of blocks, top to bottom, with buttons side by side; `null` when nothing in it compiles. */ export function compileFlow(scope, blocks, room) {
|
|
571
|
+
const tree = scope.page.tree;
|
|
572
|
+
const children = [];
|
|
573
|
+
let buttons = [];
|
|
574
|
+
const flushButtons = ()=>{
|
|
575
|
+
if (!buttons.length) return;
|
|
576
|
+
children.push(buttons.length === 1 ? buttons[0] : tree.add('muiStack', _extends({
|
|
577
|
+
direction: 'row',
|
|
578
|
+
spacing: '2',
|
|
579
|
+
useFlexGap: true,
|
|
580
|
+
flexWrap: 'wrap'
|
|
581
|
+
}, scope.centered ? {
|
|
582
|
+
justifyContent: 'center'
|
|
583
|
+
} : {}), null, buttons, 'actions'));
|
|
584
|
+
buttons = [];
|
|
585
|
+
};
|
|
586
|
+
for (const block of blocks){
|
|
587
|
+
if (block.kind === 'button') {
|
|
588
|
+
const id = compileButton(scope, block);
|
|
589
|
+
if (id) buttons.push(id);
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
flushButtons();
|
|
593
|
+
const id = compileBlock(scope, block, room);
|
|
594
|
+
if (id) children.push(id);
|
|
595
|
+
}
|
|
596
|
+
flushButtons();
|
|
597
|
+
if (!children.length) return null;
|
|
598
|
+
// One block needs no flow around it.
|
|
599
|
+
if (children.length === 1) return children[0];
|
|
600
|
+
const align = scope.centered && room === 'full';
|
|
601
|
+
// A flow whose words open with a label, a heading and a lede reads as one
|
|
602
|
+
// block at a tighter spacing than the groups under it.
|
|
603
|
+
return tree.add('muiStack', _extends({
|
|
604
|
+
spacing: hasGroup(tree, children) ? '4' : '2.5'
|
|
605
|
+
}, align ? {
|
|
606
|
+
alignItems: 'center'
|
|
607
|
+
} : {}), null, children, 'flow');
|
|
608
|
+
}
|
|
609
|
+
function hasGroup(tree, ids) {
|
|
610
|
+
return ids.some((id)=>{
|
|
611
|
+
var _ref;
|
|
612
|
+
var _tree_nodes_id;
|
|
613
|
+
return [
|
|
614
|
+
'muiGrid',
|
|
615
|
+
'muiList'
|
|
616
|
+
].includes((_ref = (_tree_nodes_id = tree.nodes[id]) == null ? void 0 : _tree_nodes_id.componentId) != null ? _ref : '');
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function compileBlock(scope, block, room) {
|
|
620
|
+
switch(block.kind){
|
|
621
|
+
case 'eyebrow':
|
|
622
|
+
return words(scope, block, 'eyebrow', 'overline', {
|
|
623
|
+
component: 'p'
|
|
624
|
+
}, accent(scope));
|
|
625
|
+
case 'heading':
|
|
626
|
+
return heading(scope, block);
|
|
627
|
+
case 'lede':
|
|
628
|
+
return words(scope, block, 'lede', 'lede', {}, muted(scope));
|
|
629
|
+
case 'text':
|
|
630
|
+
return words(scope, block, 'text', 'body1', {}, muted(scope));
|
|
631
|
+
case 'note':
|
|
632
|
+
return words(scope, block, 'note', 'body2', {}, muted(scope));
|
|
633
|
+
case 'image':
|
|
634
|
+
return image(scope, block, room);
|
|
635
|
+
case 'form':
|
|
636
|
+
return form(scope, block);
|
|
637
|
+
case 'component':
|
|
638
|
+
return component(scope, block);
|
|
639
|
+
case 'list':
|
|
640
|
+
return list(scope, block);
|
|
641
|
+
case 'faq':
|
|
642
|
+
return faq(scope, block);
|
|
643
|
+
case 'cards':
|
|
644
|
+
case 'steps':
|
|
645
|
+
case 'stats':
|
|
646
|
+
case 'quotes':
|
|
647
|
+
return group(scope, block, room);
|
|
648
|
+
default:
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/** Secondary words' color: muted text, or the band's own on a brand band. */ function muted(scope) {
|
|
653
|
+
return scope.band === 'brand' ? null : {
|
|
654
|
+
color: 'text.secondary'
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
/** An accent's color: the brand's, or the band's own on a brand band. */ function accent(scope) {
|
|
658
|
+
return scope.band === 'brand' ? null : {
|
|
659
|
+
color: 'primary.main'
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function align(scope) {
|
|
663
|
+
return scope.centered ? {
|
|
664
|
+
align: 'center'
|
|
665
|
+
} : {};
|
|
666
|
+
}
|
|
667
|
+
function words(scope, block, place, variant, props, sx) {
|
|
668
|
+
const text = aiLayoutWords(block.text, place, scope.page.targets.facts);
|
|
669
|
+
if (!text) return null;
|
|
670
|
+
return scope.page.tree.add('muiTypography', _extends({
|
|
671
|
+
children: text,
|
|
672
|
+
variant
|
|
673
|
+
}, props, align(scope)), sx, null, place);
|
|
674
|
+
}
|
|
675
|
+
/** A heading at its place in the outline: the section's own, or one under it. */ function heading(scope, block) {
|
|
676
|
+
if (scope.frame) {
|
|
677
|
+
const label = aiLayoutWords(block.text, 'itemTitle', scope.page.targets.facts);
|
|
678
|
+
return label ? scope.page.tree.add('muiTypography', _extends({
|
|
679
|
+
children: label,
|
|
680
|
+
variant: 'subtitle1',
|
|
681
|
+
component: 'p'
|
|
682
|
+
}, align(scope)), null, null, 'label') : null;
|
|
683
|
+
}
|
|
684
|
+
const own = !scope.headed;
|
|
685
|
+
const level = own ? scope.level : scope.level + 1;
|
|
686
|
+
const place = level === 1 ? 'title' : own ? 'heading' : 'itemTitle';
|
|
687
|
+
const text = aiLayoutWords(block.text, place, scope.page.targets.facts);
|
|
688
|
+
if (!text) return null;
|
|
689
|
+
scope.headed = scope.headed || own;
|
|
690
|
+
const variant = level === 1 ? block.style === 'large' ? 'displayXl' : 'h1' : own ? 'h2' : 'h4';
|
|
691
|
+
return scope.page.tree.add('muiTypography', _extends({
|
|
692
|
+
children: text,
|
|
693
|
+
variant,
|
|
694
|
+
component: `h${level}`
|
|
695
|
+
}, align(scope)), null, null, 'heading');
|
|
696
|
+
}
|
|
697
|
+
/** The level an item's title takes: one under the section's heading, or h2 in a section with none yet. */ function itemLevel(scope) {
|
|
698
|
+
return scope.headed ? scope.level + 1 : Math.max(2, scope.level);
|
|
699
|
+
}
|
|
700
|
+
/** The element an item's title renders as: a heading on a page, a label in the frame. */ function itemElement(scope) {
|
|
701
|
+
return scope.frame ? 'p' : `h${itemLevel(scope)}`;
|
|
702
|
+
}
|
|
703
|
+
function compileButton(scope, block) {
|
|
704
|
+
var _block_style;
|
|
705
|
+
const label = aiLayoutFitText(block.text, 'label');
|
|
706
|
+
if (!label) return null;
|
|
707
|
+
const destination = aiLayoutResolveLink(block.to, label, scope.link, scope.page.targets);
|
|
708
|
+
if (!destination) {
|
|
709
|
+
scope.page.settled.push({
|
|
710
|
+
at: scope.at,
|
|
711
|
+
what: `the button "${label}" has nowhere to go; left out`
|
|
712
|
+
});
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
const style = (_block_style = block.style) != null ? _block_style : 'primary';
|
|
716
|
+
const onBrand = scope.band === 'brand';
|
|
717
|
+
const look = style === 'quiet' ? {
|
|
718
|
+
variant: 'text',
|
|
719
|
+
color: onBrand ? 'inherit' : 'primary'
|
|
720
|
+
} : style === 'secondary' ? {
|
|
721
|
+
variant: 'outlined',
|
|
722
|
+
color: onBrand ? 'inherit' : 'primary'
|
|
723
|
+
} : {
|
|
724
|
+
variant: 'contained',
|
|
725
|
+
color: onBrand ? 'secondary' : 'primary'
|
|
726
|
+
};
|
|
727
|
+
const id = scope.page.tree.add('muiButton', _extends({
|
|
728
|
+
children: label
|
|
729
|
+
}, look, {
|
|
730
|
+
size: scope.index === 0 || block.style === 'large' ? 'large' : 'medium'
|
|
731
|
+
}, destinationProps(destination)), // Sized to its words, never stretched across the column a Stack would
|
|
732
|
+
// stretch it over (AGL-3660), and aligned with its section.
|
|
733
|
+
{
|
|
734
|
+
alignSelf: scope.centered ? 'center' : 'flex-start'
|
|
735
|
+
}, null, 'button');
|
|
736
|
+
linkTo(scope, id, destination);
|
|
737
|
+
return id;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* A link to a section of this page carries the platform's Scroll to element
|
|
741
|
+
* interaction to that section's root (AGL-3097), by the id the page stores it
|
|
742
|
+
* under.
|
|
743
|
+
*/ function linkTo(scope, id, destination) {
|
|
744
|
+
var _ref, _ref1;
|
|
745
|
+
var _page_options_sectionIds, _page_plan_sections_destination_index;
|
|
746
|
+
if (destination.kind !== 'section') return;
|
|
747
|
+
const { page } = scope;
|
|
748
|
+
page.scrollTo[id] = destination.index;
|
|
749
|
+
const target = (_ref = (_page_options_sectionIds = page.options.sectionIds) == null ? void 0 : _page_options_sectionIds[destination.index]) != null ? _ref : sectionIdOf(destination.index);
|
|
750
|
+
const name = (_ref1 = (_page_plan_sections_destination_index = page.plan.sections[destination.index]) == null ? void 0 : _page_plan_sections_destination_index.name) != null ? _ref1 : `section ${destination.index + 1}`;
|
|
751
|
+
page.tree.nodes[id].interactions = [
|
|
752
|
+
aiPageScrollInteraction(target, name)
|
|
753
|
+
];
|
|
754
|
+
}
|
|
755
|
+
export function destinationProps(destination) {
|
|
756
|
+
if (destination.kind === 'page') return {
|
|
757
|
+
screenId: destination.screenId
|
|
758
|
+
};
|
|
759
|
+
if (destination.kind === 'href') return {
|
|
760
|
+
href: destination.href,
|
|
761
|
+
target: '_blank'
|
|
762
|
+
};
|
|
763
|
+
return {};
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* A picture slot: a soft frame at a stock shape showing an icon until the
|
|
767
|
+
* owner places the picture, which then fills it. Its description is its alt
|
|
768
|
+
* text (rule 9), and no source is set here: the compiler stays pure, and
|
|
769
|
+
* `ai-layout-pictures.ts` fills the slot with a photo the site serves itself
|
|
770
|
+
* once the page is checked.
|
|
771
|
+
*/ function image(scope, block, room) {
|
|
772
|
+
var _aiIconOfWord;
|
|
773
|
+
const { page } = scope;
|
|
774
|
+
const alt = aiLayoutFitText(block.text, 'alt');
|
|
775
|
+
if (!alt) return null;
|
|
776
|
+
if (page.images >= maxImages(page)) {
|
|
777
|
+
page.settled.push({
|
|
778
|
+
at: scope.at,
|
|
779
|
+
what: `a picture past the page's ${AI_LAYOUT_MAX_IMAGES}; left out`
|
|
780
|
+
});
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
page.images += 1;
|
|
784
|
+
const icon = (_aiIconOfWord = aiIconOfWord(block.icon)) != null ? _aiIconOfWord : page.pageIcon;
|
|
785
|
+
const tree = page.tree;
|
|
786
|
+
const mark = tree.add(AI_ICON_COMPONENT_ID, {
|
|
787
|
+
iconId: icon.id,
|
|
788
|
+
size: '64'
|
|
789
|
+
}, {
|
|
790
|
+
color: 'primary.main'
|
|
791
|
+
}, null, 'imageIcon');
|
|
792
|
+
const picture = tree.add('image', {
|
|
793
|
+
alt,
|
|
794
|
+
objectFit: 'cover'
|
|
795
|
+
}, {
|
|
796
|
+
position: 'absolute',
|
|
797
|
+
top: 0,
|
|
798
|
+
left: 0,
|
|
799
|
+
width: '100%',
|
|
800
|
+
height: '100%'
|
|
801
|
+
}, null, 'image');
|
|
802
|
+
return tree.add('muiBox', null, {
|
|
803
|
+
position: 'relative',
|
|
804
|
+
display: 'flex',
|
|
805
|
+
alignItems: 'center',
|
|
806
|
+
justifyContent: 'center',
|
|
807
|
+
overflow: 'hidden',
|
|
808
|
+
// In multiples of the theme's corner radius, so a sharp site's frames are sharp.
|
|
809
|
+
borderRadius: 2,
|
|
810
|
+
bgcolor: 'action.hover',
|
|
811
|
+
// The frame's shape follows its room, so no two slots of a page share one style.
|
|
812
|
+
aspectRatio: room === 'full' ? page.images === 1 ? '16 / 9' : '21 / 9' : room === 'half' ? '4 / 3' : page.images === 1 ? '1 / 1' : '3 / 4'
|
|
813
|
+
}, [
|
|
814
|
+
mark,
|
|
815
|
+
picture
|
|
816
|
+
], 'frame');
|
|
817
|
+
}
|
|
818
|
+
/** A saved form, framed as a card so it reads as the section's one task. */ function form(scope, block) {
|
|
819
|
+
const tree = scope.page.tree;
|
|
820
|
+
const placed = tree.add('form', {
|
|
821
|
+
formId: block.to
|
|
822
|
+
}, null, null, 'form');
|
|
823
|
+
const content = tree.add('muiCardContent', null, {
|
|
824
|
+
p: {
|
|
825
|
+
xs: 3,
|
|
826
|
+
md: 4
|
|
827
|
+
}
|
|
828
|
+
}, [
|
|
829
|
+
placed
|
|
830
|
+
], 'formContent');
|
|
831
|
+
// The card style is the site theme's (AGL-3660), as a card dropped from the
|
|
832
|
+
// drawer takes it; it takes the flow's width, so a centered section never
|
|
833
|
+
// shrinks a form to its button.
|
|
834
|
+
return tree.add('muiCard', null, {
|
|
835
|
+
alignSelf: 'stretch'
|
|
836
|
+
}, [
|
|
837
|
+
content
|
|
838
|
+
], 'formCard');
|
|
839
|
+
}
|
|
840
|
+
/** A reusable component placed by its id, its props filled from the block's items. */ function component(scope, block) {
|
|
841
|
+
var _block_items;
|
|
842
|
+
const declared = componentOf(scope.page.targets, block.to);
|
|
843
|
+
if (!declared) return null;
|
|
844
|
+
const values = {};
|
|
845
|
+
for (const item of (_block_items = block.items) != null ? _block_items : []){
|
|
846
|
+
const name = item.title.trim();
|
|
847
|
+
if (name in declared.props && item.text.trim()) values[name] = aiLayoutWords(item.text, 'text', scope.page.targets.facts);
|
|
848
|
+
}
|
|
849
|
+
// An instance fills at least one prop (rule 1): the block's words, else the
|
|
850
|
+
// name its section has in the plan, rather than the component's placeholder.
|
|
851
|
+
if (!Object.values(values).some(Boolean)) {
|
|
852
|
+
var _scope_page_plan_sections_scope_index;
|
|
853
|
+
const first = textProps(declared.props)[0];
|
|
854
|
+
const words = aiLayoutWords(block.text, 'text', scope.page.targets.facts) || aiLayoutWords((_scope_page_plan_sections_scope_index = scope.page.plan.sections[scope.index]) == null ? void 0 : _scope_page_plan_sections_scope_index.name, 'itemTitle', scope.page.targets.facts);
|
|
855
|
+
if (first && words) values[first] = words;
|
|
856
|
+
}
|
|
857
|
+
for (const [name, value] of Object.entries(values))if (!value) delete values[name];
|
|
858
|
+
return scope.page.tree.add(REUSABLE_INSTANCE_COMPONENT_ID, _extends({
|
|
859
|
+
[AI_INSTANCE_REF_PROP]: declared.id
|
|
860
|
+
}, Object.keys(values).length ? {
|
|
861
|
+
propValues: values
|
|
862
|
+
} : {}), null, null, 'instance');
|
|
863
|
+
}
|
|
864
|
+
function textProps(props) {
|
|
865
|
+
return Object.entries(props).filter(([, type])=>type === 'text' || type === 'richText').map(([name])=>name);
|
|
866
|
+
}
|
|
867
|
+
/** An item's words as the props of the component that draws it. */ function instanceValues(props, item, facts) {
|
|
868
|
+
var _texts_find, _texts_find1;
|
|
869
|
+
const texts = textProps(props);
|
|
870
|
+
const titleProp = (_texts_find = texts.find((name)=>/title|name|heading|label|question|figure|value/i.test(name))) != null ? _texts_find : texts[0];
|
|
871
|
+
const textProp = (_texts_find1 = texts.find((name)=>name !== titleProp && /text|description|body|summary|copy|answer|detail|caption/i.test(name))) != null ? _texts_find1 : texts.find((name)=>name !== titleProp);
|
|
872
|
+
const values = {};
|
|
873
|
+
if (titleProp && item.title.trim()) values[titleProp] = aiLayoutWords(item.title, 'itemTitle', facts);
|
|
874
|
+
if (textProp && item.text.trim()) values[textProp] = aiLayoutWords(item.text, 'itemText', facts);
|
|
875
|
+
const first = titleProp != null ? titleProp : textProp;
|
|
876
|
+
if (!Object.values(values).some(Boolean) && first) values[first] = aiLayoutWords(item.title || item.text, 'itemTitle', facts);
|
|
877
|
+
return values;
|
|
878
|
+
}
|
|
879
|
+
// ── Groups ────────────────────────────────────────────────────────────────
|
|
880
|
+
/** Records the elements that draw a section's repeated items, and hands them back. */ function noted(scope, ids) {
|
|
881
|
+
var _scope_page_itemIds_scope_index;
|
|
882
|
+
if (!scope.frame) (_scope_page_itemIds_scope_index = scope.page.itemIds[scope.index]) == null ? void 0 : _scope_page_itemIds_scope_index.push(...ids);
|
|
883
|
+
return ids;
|
|
884
|
+
}
|
|
885
|
+
/** How many items sit side by side in a room. */ function across(count, room, kind) {
|
|
886
|
+
if (room === 'narrow') return 1;
|
|
887
|
+
if (room === 'half') return count >= 2 && kind === 'stats' ? 2 : 1;
|
|
888
|
+
if (count <= 3) return count;
|
|
889
|
+
if (count === 4) return 4;
|
|
890
|
+
if (count === 7) return 4;
|
|
891
|
+
return 3;
|
|
892
|
+
}
|
|
893
|
+
function itemSize(perRow) {
|
|
894
|
+
if (perRow === 2) return 'xs:12 sm:6';
|
|
895
|
+
if (perRow === 3) return 'xs:12 md:4';
|
|
896
|
+
return 'xs:12 sm:6 md:3';
|
|
897
|
+
}
|
|
898
|
+
/** Items laid out side by side as a Grid of sized items, or stacked where they sit one to a row. */ function layOut(scope, items, perRow, spacing, role) {
|
|
899
|
+
const tree = scope.page.tree;
|
|
900
|
+
if (items.length === 1) return items[0];
|
|
901
|
+
if (perRow <= 1) {
|
|
902
|
+
return tree.add('muiStack', {
|
|
903
|
+
spacing
|
|
904
|
+
}, null, items, role);
|
|
905
|
+
}
|
|
906
|
+
const size = itemSize(perRow);
|
|
907
|
+
const cells = items.map((id)=>tree.add('muiGrid', {
|
|
908
|
+
size
|
|
909
|
+
}, null, [
|
|
910
|
+
id
|
|
911
|
+
], 'cell'));
|
|
912
|
+
return tree.add('muiGrid', {
|
|
913
|
+
container: true,
|
|
914
|
+
spacing
|
|
915
|
+
}, null, cells, role);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Whether repeats must be components here (rule 1): the workspace keeps
|
|
919
|
+
* them. Rule 1 counts like blocks across the whole page, so where it holds
|
|
920
|
+
* every group the plan places no component for is drawn compact, however
|
|
921
|
+
* few its items — two sections of two cards are four like cards.
|
|
922
|
+
*/ function rule1(scope) {
|
|
923
|
+
return scope.page.options.reusableComponents;
|
|
924
|
+
}
|
|
925
|
+
/** The most pictures this page carries: two where rule 1 counts three like frames as a repeat. */ function maxImages(page) {
|
|
926
|
+
return page.options.reusableComponents ? 2 : AI_LAYOUT_MAX_IMAGES;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* The items a group may still show in its section (rule 8): seven like items
|
|
930
|
+
* a section, across all its groups, since eight are a list typed by hand.
|
|
931
|
+
*/ /** Whether an item has words left once they are cleaned and fitted: an item with none is no item. */ function speaks(scope, item) {
|
|
932
|
+
const facts = scope.page.targets.facts;
|
|
933
|
+
return !!(aiLayoutWords(item.title, 'itemTitle', facts) || aiLayoutWords(item.text, 'itemText', facts));
|
|
934
|
+
}
|
|
935
|
+
/** A group's items with every part that has no words left once cleaned emptied, and wordless items left out. */ /** A title that only names the kind of thing it is: "Card", "Item 2", "Title". */ const AI_LAYOUT_PLACEHOLDER_TITLE = /^(?:card|item|title|heading|placeholder|tile|box|feature|service|post|article|entry)\s*\d*$/i;
|
|
936
|
+
function cleanItems(scope, items) {
|
|
937
|
+
const facts = scope.page.targets.facts;
|
|
938
|
+
// A title that is only the name or id of what places it — a "Card" item of
|
|
939
|
+
// the Card component (AGL-3660) — is no words a visitor reads.
|
|
940
|
+
const references = new Set(scope.page.targets.components.flatMap((component)=>[
|
|
941
|
+
component.name,
|
|
942
|
+
component.id
|
|
943
|
+
]).map((name)=>name.trim().toLowerCase()));
|
|
944
|
+
return items.map((item)=>_extends({}, item, {
|
|
945
|
+
title: aiLayoutWords(item.title, 'itemTitle', facts) && !references.has(item.title.trim().toLowerCase()) && !AI_LAYOUT_PLACEHOLDER_TITLE.test(item.title.trim()) ? item.title : '',
|
|
946
|
+
text: aiLayoutWords(item.text, 'itemText', facts) ? item.text : ''
|
|
947
|
+
})).filter((item)=>speaks(scope, item));
|
|
948
|
+
}
|
|
949
|
+
function takeItems(scope, items) {
|
|
950
|
+
const kept = items.slice(0, Math.max(0, scope.itemsLeft));
|
|
951
|
+
if (kept.length < items.length) {
|
|
952
|
+
scope.page.settled.push({
|
|
953
|
+
at: scope.at,
|
|
954
|
+
what: `${items.length - kept.length} items past the section's ${AI_LAYOUT_MAX_ITEMS}; left out`
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
scope.itemsLeft -= kept.length;
|
|
958
|
+
return kept;
|
|
959
|
+
}
|
|
960
|
+
/** `takeItems` on a list it shortens where it stands. */ function takeItemsInPlace(scope, items) {
|
|
961
|
+
const kept = takeItems(scope, items);
|
|
962
|
+
items.length = kept.length;
|
|
963
|
+
}
|
|
964
|
+
function group(scope, block, room) {
|
|
965
|
+
var _block_items;
|
|
966
|
+
const items = takeItems(scope, cleanItems(scope, (_block_items = block.items) != null ? _block_items : []));
|
|
967
|
+
if (!items.length) return null;
|
|
968
|
+
const perRow = across(items.length, room, block.kind);
|
|
969
|
+
const placed = componentOf(scope.page.targets, block.to);
|
|
970
|
+
if (placed) return instances(scope, placed, items, perRow);
|
|
971
|
+
if (rule1(scope)) return compactGroup(scope, block, items, perRow);
|
|
972
|
+
switch(block.kind){
|
|
973
|
+
case 'steps':
|
|
974
|
+
return layOut(scope, noted(scope, items.map((item, position)=>step(scope, item, position))), perRow, '4', 'steps');
|
|
975
|
+
case 'stats':
|
|
976
|
+
return layOut(scope, noted(scope, items.map((item)=>stat(scope, item))), perRow, '4', 'stats');
|
|
977
|
+
case 'quotes':
|
|
978
|
+
return layOut(scope, noted(scope, items.map((item)=>quote(scope, item))), perRow, '3', 'quotes');
|
|
979
|
+
default:
|
|
980
|
+
return layOut(scope, noted(scope, items.map((item)=>card(scope, block, item))), perRow, '3', 'cards');
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
function instances(scope, placed, items, perRow) {
|
|
984
|
+
const tree = scope.page.tree;
|
|
985
|
+
const ids = items.map((item)=>{
|
|
986
|
+
const values = instanceValues(placed.props, item, scope.page.targets.facts);
|
|
987
|
+
return tree.add(REUSABLE_INSTANCE_COMPONENT_ID, _extends({
|
|
988
|
+
[AI_INSTANCE_REF_PROP]: placed.id
|
|
989
|
+
}, Object.keys(values).length ? {
|
|
990
|
+
propValues: values
|
|
991
|
+
} : {}), null, null, 'instance');
|
|
992
|
+
});
|
|
993
|
+
return layOut(scope, noted(scope, ids), perRow, '3', 'instances');
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* A group in its compact form, a title over its text: two elements an item,
|
|
997
|
+
* under the three a block must have before rule 1 asks for a component.
|
|
998
|
+
*/ function compactGroup(scope, block, items, perRow) {
|
|
999
|
+
const tree = scope.page.tree;
|
|
1000
|
+
const facts = scope.page.targets.facts;
|
|
1001
|
+
const pairs = items.map((item, position)=>{
|
|
1002
|
+
const title = block.kind === 'quotes' ? aiLayoutQuoteGap(item.text || item.title) : block.kind === 'stats' ? aiLayoutWords(item.title, 'stat', facts) : `${block.kind === 'steps' ? `${position + 1}. ` : ''}${aiLayoutWords(item.title, 'itemTitle', facts)}`;
|
|
1003
|
+
const text = block.kind === 'quotes' ? '[Customer name]' : aiLayoutWords(item.text, block.kind === 'stats' ? 'statLabel' : 'itemText', facts);
|
|
1004
|
+
return tree.add('muiListItemText', _extends({
|
|
1005
|
+
primary: title
|
|
1006
|
+
}, text ? {
|
|
1007
|
+
secondary: text
|
|
1008
|
+
} : {}), null, null, 'pair');
|
|
1009
|
+
});
|
|
1010
|
+
return layOut(scope, noted(scope, pairs), perRow, '3', 'compact');
|
|
1011
|
+
}
|
|
1012
|
+
/** A card: an optional icon, its title one level under the section's heading, and its words. */ function card(scope, block, item) {
|
|
1013
|
+
var _aiIconOfWord;
|
|
1014
|
+
const tree = scope.page.tree;
|
|
1015
|
+
const facts = scope.page.targets.facts;
|
|
1016
|
+
const icon = (_aiIconOfWord = aiIconOfWord(item.icon)) != null ? _aiIconOfWord : aiIconOfWord(block.icon);
|
|
1017
|
+
const children = [
|
|
1018
|
+
icon ? tree.add(AI_ICON_COMPONENT_ID, {
|
|
1019
|
+
iconId: icon.id,
|
|
1020
|
+
size: '32'
|
|
1021
|
+
}, {
|
|
1022
|
+
color: 'primary.main'
|
|
1023
|
+
}, null, 'icon') : null,
|
|
1024
|
+
item.title.trim() ? tree.add('muiTypography', {
|
|
1025
|
+
children: aiLayoutWords(item.title, 'itemTitle', facts),
|
|
1026
|
+
variant: 'h5',
|
|
1027
|
+
component: itemElement(scope)
|
|
1028
|
+
}, null, null, 'title') : null,
|
|
1029
|
+
item.text.trim() ? tree.add('muiTypography', {
|
|
1030
|
+
children: aiLayoutWords(item.text, 'itemText', facts),
|
|
1031
|
+
variant: 'body1'
|
|
1032
|
+
}, {
|
|
1033
|
+
color: 'text.secondary'
|
|
1034
|
+
}, null, 'text') : null
|
|
1035
|
+
];
|
|
1036
|
+
const stack = tree.add('muiStack', {
|
|
1037
|
+
spacing: '1.5'
|
|
1038
|
+
}, null, children, 'cardStack');
|
|
1039
|
+
if (block.style === 'quiet') return stack;
|
|
1040
|
+
const content = tree.add('muiCardContent', null, {
|
|
1041
|
+
p: 3
|
|
1042
|
+
}, [
|
|
1043
|
+
stack
|
|
1044
|
+
], 'cardContent');
|
|
1045
|
+
// Flat, outlined, raised, tinted or ruled is the site theme's card style
|
|
1046
|
+
// (AGL-3660), never a choice stamped on this card.
|
|
1047
|
+
return tree.add('muiCard', null, {
|
|
1048
|
+
height: '100%'
|
|
1049
|
+
}, [
|
|
1050
|
+
content
|
|
1051
|
+
], 'card');
|
|
1052
|
+
}
|
|
1053
|
+
/** A numbered step: its number in the brand color, its title and its words. */ function step(scope, item, position) {
|
|
1054
|
+
const tree = scope.page.tree;
|
|
1055
|
+
const facts = scope.page.targets.facts;
|
|
1056
|
+
return tree.add('muiStack', {
|
|
1057
|
+
spacing: '1'
|
|
1058
|
+
}, null, [
|
|
1059
|
+
tree.add('muiTypography', {
|
|
1060
|
+
children: String(position + 1).padStart(2, '0'),
|
|
1061
|
+
variant: 'h3',
|
|
1062
|
+
component: 'p'
|
|
1063
|
+
}, accent(scope), null, 'number'),
|
|
1064
|
+
item.title.trim() ? tree.add('muiTypography', {
|
|
1065
|
+
children: aiLayoutWords(item.title, 'itemTitle', facts),
|
|
1066
|
+
variant: 'h5',
|
|
1067
|
+
component: itemElement(scope)
|
|
1068
|
+
}, null, null, 'title') : null,
|
|
1069
|
+
item.text.trim() ? tree.add('muiTypography', {
|
|
1070
|
+
children: aiLayoutWords(item.text, 'itemText', facts),
|
|
1071
|
+
variant: 'body1'
|
|
1072
|
+
}, muted(scope), null, 'text') : null
|
|
1073
|
+
], 'step');
|
|
1074
|
+
}
|
|
1075
|
+
/** A figure and its label. A figure the brief did not give is the model's bracketed gap. */ function stat(scope, item) {
|
|
1076
|
+
const tree = scope.page.tree;
|
|
1077
|
+
const facts = scope.page.targets.facts;
|
|
1078
|
+
return tree.add('muiStack', _extends({
|
|
1079
|
+
spacing: '0.5'
|
|
1080
|
+
}, scope.centered ? {
|
|
1081
|
+
alignItems: 'center'
|
|
1082
|
+
} : {}), null, [
|
|
1083
|
+
tree.add('muiTypography', _extends({
|
|
1084
|
+
children: aiLayoutWords(item.title, 'stat', facts) || '[figure]',
|
|
1085
|
+
variant: 'h2',
|
|
1086
|
+
component: 'p'
|
|
1087
|
+
}, align(scope)), accent(scope), null, 'figure'),
|
|
1088
|
+
item.text.trim() ? tree.add('muiTypography', _extends({
|
|
1089
|
+
children: aiLayoutWords(item.text, 'statLabel', facts),
|
|
1090
|
+
variant: 'body2'
|
|
1091
|
+
}, align(scope)), muted(scope), null, 'label') : null
|
|
1092
|
+
], 'stat');
|
|
1093
|
+
}
|
|
1094
|
+
/** A quote slot: what a customer should be quoted saying, as the gap the owner fills. */ function quote(scope, item) {
|
|
1095
|
+
const tree = scope.page.tree;
|
|
1096
|
+
const words = aiLayoutQuoteGap(item.text || item.title);
|
|
1097
|
+
const who = item.text && item.title ? aiLayoutFitText(item.title, 'statLabel') : '';
|
|
1098
|
+
const stack = tree.add('muiStack', {
|
|
1099
|
+
spacing: '2'
|
|
1100
|
+
}, null, [
|
|
1101
|
+
tree.add(AI_ICON_COMPONENT_ID, {
|
|
1102
|
+
iconId: AI_ICON_LIBRARY.quote.id,
|
|
1103
|
+
size: '32'
|
|
1104
|
+
}, {
|
|
1105
|
+
color: 'primary.main'
|
|
1106
|
+
}, null, 'quoteMark'),
|
|
1107
|
+
tree.add('muiTypography', {
|
|
1108
|
+
children: words,
|
|
1109
|
+
variant: 'body1'
|
|
1110
|
+
}, null, null, 'quote'),
|
|
1111
|
+
tree.add('muiTypography', {
|
|
1112
|
+
children: aiLayoutFitText(who ? `[Customer name], ${who.replace(/^\[|\]$/g, '')}` : '', 'note') || '[Customer name]',
|
|
1113
|
+
variant: 'subtitle2',
|
|
1114
|
+
component: 'p'
|
|
1115
|
+
}, null, null, 'attribution')
|
|
1116
|
+
], 'quoteStack');
|
|
1117
|
+
const content = tree.add('muiCardContent', null, {
|
|
1118
|
+
p: 3
|
|
1119
|
+
}, [
|
|
1120
|
+
stack
|
|
1121
|
+
], 'quoteContent');
|
|
1122
|
+
return tree.add('muiCard', null, {
|
|
1123
|
+
height: '100%'
|
|
1124
|
+
}, [
|
|
1125
|
+
content
|
|
1126
|
+
], 'quoteCard');
|
|
1127
|
+
}
|
|
1128
|
+
/** A list: check-marked lines, or plain lines where rule 1 holds and the list is long. */ function list(scope, block) {
|
|
1129
|
+
var _block_items, _block_items1, _aiIconOfWord;
|
|
1130
|
+
const tree = scope.page.tree;
|
|
1131
|
+
const facts = scope.page.targets.facts;
|
|
1132
|
+
// A list whose lines go somewhere is a list of links: each line that
|
|
1133
|
+
// resolves is a Page Link, and one that does not is left out (rule 10).
|
|
1134
|
+
if (((_block_items = block.items) != null ? _block_items : []).some((item)=>item.to)) {
|
|
1135
|
+
var _block_items2;
|
|
1136
|
+
const links = ((_block_items2 = block.items) != null ? _block_items2 : []).flatMap((item)=>{
|
|
1137
|
+
const label = aiLayoutFitText(item.title || item.text, 'label');
|
|
1138
|
+
const destination = label ? aiLayoutResolveLink(item.to, label, scope.link, scope.page.targets) : null;
|
|
1139
|
+
if (!destination) return [];
|
|
1140
|
+
const id = tree.add('muiScreenLink', _extends({
|
|
1141
|
+
children: label,
|
|
1142
|
+
renderAs: 'link',
|
|
1143
|
+
color: 'inherit'
|
|
1144
|
+
}, destinationProps(destination)), null, null, 'link');
|
|
1145
|
+
linkTo(scope, id, destination);
|
|
1146
|
+
return [
|
|
1147
|
+
id
|
|
1148
|
+
];
|
|
1149
|
+
});
|
|
1150
|
+
return links.length ? tree.add('muiStack', {
|
|
1151
|
+
spacing: '1'
|
|
1152
|
+
}, null, noted(scope, links), 'links') : null;
|
|
1153
|
+
}
|
|
1154
|
+
const lines = cleanItems(scope, (_block_items1 = block.items) != null ? _block_items1 : []).map((item)=>aiLayoutWords([
|
|
1155
|
+
item.title,
|
|
1156
|
+
item.text
|
|
1157
|
+
].filter((part)=>part.trim()).join(' — '), 'listItem', facts)).filter(Boolean);
|
|
1158
|
+
takeItemsInPlace(scope, lines);
|
|
1159
|
+
if (!lines.length) return null;
|
|
1160
|
+
if (rule1(scope)) {
|
|
1161
|
+
const rows = lines.map((line)=>tree.add('muiListItem', {
|
|
1162
|
+
disableGutters: true
|
|
1163
|
+
}, null, [
|
|
1164
|
+
tree.add('muiListItemText', {
|
|
1165
|
+
primary: line
|
|
1166
|
+
}, null, null, 'line')
|
|
1167
|
+
], 'row'));
|
|
1168
|
+
return tree.add('muiList', {
|
|
1169
|
+
disablePadding: true
|
|
1170
|
+
}, null, noted(scope, rows), 'list');
|
|
1171
|
+
}
|
|
1172
|
+
const check = (_aiIconOfWord = aiIconOfWord(block.icon)) != null ? _aiIconOfWord : AI_ICON_LIBRARY.check;
|
|
1173
|
+
const rows = lines.map((line)=>tree.add('muiStack', {
|
|
1174
|
+
direction: 'row',
|
|
1175
|
+
spacing: '1.5',
|
|
1176
|
+
alignItems: 'flex-start'
|
|
1177
|
+
}, null, [
|
|
1178
|
+
tree.add(AI_ICON_COMPONENT_ID, {
|
|
1179
|
+
iconId: check.id,
|
|
1180
|
+
size: '22'
|
|
1181
|
+
}, accent(scope), null, 'check'),
|
|
1182
|
+
tree.add('muiTypography', {
|
|
1183
|
+
children: line,
|
|
1184
|
+
variant: 'body1'
|
|
1185
|
+
}, null, null, 'line')
|
|
1186
|
+
], 'row'));
|
|
1187
|
+
return tree.add('muiStack', {
|
|
1188
|
+
spacing: '1.5'
|
|
1189
|
+
}, null, noted(scope, rows), 'list');
|
|
1190
|
+
}
|
|
1191
|
+
/** Questions and answers: an accordion each, or question-over-answer pairs where rule 1 holds. */ function faq(scope, block) {
|
|
1192
|
+
var _block_items;
|
|
1193
|
+
const tree = scope.page.tree;
|
|
1194
|
+
const facts = scope.page.targets.facts;
|
|
1195
|
+
const items = takeItems(scope, cleanItems(scope, (_block_items = block.items) != null ? _block_items : []).filter((item)=>!!aiLayoutWords(item.title, 'question', facts)));
|
|
1196
|
+
if (!items.length) return null;
|
|
1197
|
+
if (rule1(scope)) {
|
|
1198
|
+
const rows = items.map((item)=>tree.add('muiListItem', {
|
|
1199
|
+
disableGutters: true,
|
|
1200
|
+
divider: true
|
|
1201
|
+
}, null, [
|
|
1202
|
+
tree.add('muiListItemText', _extends({
|
|
1203
|
+
primary: aiLayoutWords(item.title, 'question', facts)
|
|
1204
|
+
}, item.text.trim() ? {
|
|
1205
|
+
secondary: aiLayoutWords(item.text, 'answer', facts)
|
|
1206
|
+
} : {}), null, null, 'qa')
|
|
1207
|
+
], 'row'));
|
|
1208
|
+
return tree.add('muiList', {
|
|
1209
|
+
disablePadding: true
|
|
1210
|
+
}, null, noted(scope, rows), 'faq');
|
|
1211
|
+
}
|
|
1212
|
+
const panels = items.map((item, position)=>tree.add('muiAccordion', _extends({
|
|
1213
|
+
disableGutters: true
|
|
1214
|
+
}, position === 0 ? {
|
|
1215
|
+
defaultExpanded: true
|
|
1216
|
+
} : {}), null, [
|
|
1217
|
+
tree.add('muiAccordionSummary', {
|
|
1218
|
+
children: aiLayoutWords(item.title, 'question', facts)
|
|
1219
|
+
}, null, null, 'question'),
|
|
1220
|
+
item.text.trim() ? tree.add('muiAccordionDetails', null, null, [
|
|
1221
|
+
tree.add('muiTypography', {
|
|
1222
|
+
children: aiLayoutWords(item.text, 'answer', facts),
|
|
1223
|
+
variant: 'body1'
|
|
1224
|
+
}, {
|
|
1225
|
+
color: 'text.secondary'
|
|
1226
|
+
}, null, 'answer')
|
|
1227
|
+
], 'details') : null
|
|
1228
|
+
], 'panel'));
|
|
1229
|
+
return tree.add('muiStack', {
|
|
1230
|
+
spacing: '1.5'
|
|
1231
|
+
}, null, noted(scope, panels), 'faq');
|
|
1232
|
+
}
|
|
1233
|
+
/** The id the compiled page's root is stored under. */ export const AI_LAYOUT_PAGE_ROOT_ID = CANVAS_ROOT_ELEMENT_ID;
|
|
1234
|
+
|
|
1235
|
+
//# sourceMappingURL=ai-layout-compiler.js.map
|