@aglyn/plugins-ai 1.0.0-beta.244 → 1.0.0-beta.247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-links.js +21 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +3 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-hub-card.component.js +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.js +17 -9
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +98 -23
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.js +1 -1
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/declarations.server.js +12 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +216 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +3 -1
- package/src/lib/jobs/ai-job-build-step.js +124 -18
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-duplicate-step.d.ts +123 -0
- package/src/lib/jobs/ai-job-duplicate-step.js +269 -0
- package/src/lib/jobs/ai-job-duplicate-step.js.map +1 -0
- package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
- package/src/lib/jobs/ai-job-form-dataset.js +28 -10
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
- package/src/lib/jobs/ai-job-form-step.js +98 -28
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
- package/src/lib/jobs/ai-job-layout-language.js +29 -5
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +4 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
- package/src/lib/jobs/ai-job-media-step.js +610 -0
- package/src/lib/jobs/ai-job-media-step.js.map +1 -0
- package/src/lib/jobs/ai-job-navigation-step.d.ts +157 -0
- package/src/lib/jobs/ai-job-navigation-step.js +439 -0
- package/src/lib/jobs/ai-job-navigation-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +4 -1
- package/src/lib/jobs/ai-job-page-language.js +30 -5
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +5 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +3 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-plan-step.js +87 -20
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-products-step.js +2 -2
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +268 -0
- package/src/lib/jobs/ai-job-settings-step.js +690 -0
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -0
- package/src/lib/jobs/ai-job-site-content.d.ts +22 -8
- package/src/lib/jobs/ai-job-site-content.js +33 -15
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +3 -1
- package/src/lib/jobs/ai-job-site-datasets.js +13 -5
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
- package/src/lib/jobs/ai-job-site-events.js +313 -0
- package/src/lib/jobs/ai-job-site-events.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-step.js +132 -26
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
- package/src/lib/jobs/ai-job-tracking-step.js +501 -0
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +21 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +41 -4
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +62 -4
- package/src/lib/jobs/ai-site-publish.js +202 -56
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
- package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
- package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
- package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -7
- package/src/lib/layout-language/ai-layout-compiler.js +62 -42
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-frame-hollows.js +330 -0
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +24 -5
- package/src/lib/layout-language/ai-layout-frame.js +93 -20
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +3 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
- package/src/lib/layout-language/ai-layout-labels.js +162 -0
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.js +5 -2
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +49 -1
- package/src/lib/layout-language/ai-layout-listings.js +66 -5
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-tree.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-tree.js +17 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -1
- package/src/lib/model/ai-build-event-page.d.ts +70 -0
- package/src/lib/model/ai-build-event-page.js +145 -0
- package/src/lib/model/ai-build-event-page.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +44 -0
- package/src/lib/model/ai-build-job.js +141 -13
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +39 -3
- package/src/lib/model/ai-build-plan.js +59 -5
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +52 -0
- package/src/lib/model/ai-credit-estimate.js +51 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +6 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +18 -2
- package/src/lib/model/ai-jobs.types.js +6 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-layout-menus.d.ts +104 -0
- package/src/lib/model/ai-layout-menus.js +518 -0
- package/src/lib/model/ai-layout-menus.js.map +1 -0
- package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-products.d.ts +31 -1
- package/src/lib/model/ai-products.js +34 -1
- package/src/lib/model/ai-products.js.map +1 -1
- package/src/lib/model/ai-site-inventory.d.ts +17 -0
- package/src/lib/model/ai-site-inventory.js +5 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +12 -2
- package/src/lib/model/ai-site-job.js +39 -12
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-store-pages.js +1 -1
- package/src/lib/model/ai-site-store-pages.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +78 -3
- package/src/lib/runtime/ai-doctrine-validators.js +219 -31
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -1
- package/src/lib/runtime/ai-doctrine.js +16 -7
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +3 -1
- package/src/lib/runtime/ai-palette.generated.js +499 -11
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +5 -3
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-products-generation.d.ts +4 -2
- package/src/lib/runtime/ai-products-generation.js +12 -6
- package/src/lib/runtime/ai-products-generation.js.map +1 -1
- package/src/lib/runtime/ai-template-examples.js +8 -2
- package/src/lib/runtime/ai-template-examples.js.map +1 -1
- package/src/lib/runtime/site-inventory.d.ts +12 -0
- package/src/lib/runtime/site-inventory.js +29 -1
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server.js +20 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-dataset-index.js +37 -3
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
- package/src/lib/tools/ai-products-tool.d.ts +3 -1
- package/src/lib/tools/ai-products-tool.js +15 -5
- package/src/lib/tools/ai-products-tool.js.map +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
|
|
18
|
+
import type { AiBuildOps } from './ai-build-job';
|
|
19
|
+
import { type AiBuildItem, type AiBuildPlan } from './ai-build-plan';
|
|
20
|
+
/**
|
|
21
|
+
* THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).
|
|
22
|
+
*
|
|
23
|
+
* A build's `event` items are written as events, and an event is seen on the
|
|
24
|
+
* site only through an Event List a page section places — the section whose
|
|
25
|
+
* uses name the event. The live eval of 2026-10-13 asked "add our next three
|
|
26
|
+
* Friday night jazz shows at 8pm to an events page" of a site with only a
|
|
27
|
+
* Home page, and the plan made the three events and no page: they landed
|
|
28
|
+
* nowhere a visitor could see. So, settled in code where there is one right
|
|
29
|
+
* answer ({@link aiSettleBuildEventPage}), and re-asked where there is not
|
|
30
|
+
* ({@link aiBuildUnlistedEventsCheck}):
|
|
31
|
+
*
|
|
32
|
+
* 1. Events no section names, and a page the plan makes about them (its
|
|
33
|
+
* address or title says events): the events are named in that page's
|
|
34
|
+
* section about them, else its second section, else its first.
|
|
35
|
+
* 2. None such, the site has no page at `/events`, and the plan has room
|
|
36
|
+
* for one more page: the plan gains an Events page at `/events`, linked
|
|
37
|
+
* in the navigation and in the layout the other pages render in, whose
|
|
38
|
+
* one section lists them.
|
|
39
|
+
* 3. Anything else — a site that already has an events page, a plan at its
|
|
40
|
+
* page cap — is left to the plan's one re-ask, which names the events.
|
|
41
|
+
*
|
|
42
|
+
* Only the `event` operation: other placed operations keep their own rules.
|
|
43
|
+
*/
|
|
44
|
+
/** The operation whose items this settles; the events-calendar plugin's, named, never imported. */
|
|
45
|
+
export declare const AI_BUILD_EVENT_OP = "event";
|
|
46
|
+
/**
|
|
47
|
+
* The page a build adds for its events. Its address is the SITE's page,
|
|
48
|
+
* spelled from its title so it is never read as the console's Events page.
|
|
49
|
+
*/
|
|
50
|
+
export declare const AI_BUILD_EVENT_PAGE: {
|
|
51
|
+
readonly title: "Events";
|
|
52
|
+
readonly slug: `/${string}`;
|
|
53
|
+
readonly section: "Upcoming events";
|
|
54
|
+
};
|
|
55
|
+
/** The plan's event items that no page section names. */
|
|
56
|
+
export declare function aiBuildUnlistedEvents(plan: Pick<AiBuildPlan, 'items' | 'screens'>): AiBuildItem[];
|
|
57
|
+
export interface AiBuildEventPageContext {
|
|
58
|
+
/** The operations this site has; the settling runs only where it has `event`. */
|
|
59
|
+
ops: AiBuildOps | null;
|
|
60
|
+
/** The site's pages, by address. */
|
|
61
|
+
siteSlugs: readonly string[];
|
|
62
|
+
/** How many pages the build may plan. */
|
|
63
|
+
pageLimit: number;
|
|
64
|
+
/** The layout the site's pages render in (its home page's, else its one layout), or null. */
|
|
65
|
+
siteLayout: string | null;
|
|
66
|
+
}
|
|
67
|
+
/** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */
|
|
68
|
+
export declare function aiSettleBuildEventPage<T extends AiBuildPlan>(plan: T, context: AiBuildEventPageContext): T;
|
|
69
|
+
/** The re-ask for events the settling could not place: each named, and where to put it. */
|
|
70
|
+
export declare function aiBuildUnlistedEventsCheck(ops: AiBuildOps | null): (plan: AiBuildPlan) => AiDoctrineViolation[];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { AI_BUILD_PLAN_LIMITS, aiPlanSlugKey } from "./ai-build-plan.js";
|
|
18
|
+
/**
|
|
19
|
+
* THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).
|
|
20
|
+
*
|
|
21
|
+
* A build's `event` items are written as events, and an event is seen on the
|
|
22
|
+
* site only through an Event List a page section places — the section whose
|
|
23
|
+
* uses name the event. The live eval of 2026-10-13 asked "add our next three
|
|
24
|
+
* Friday night jazz shows at 8pm to an events page" of a site with only a
|
|
25
|
+
* Home page, and the plan made the three events and no page: they landed
|
|
26
|
+
* nowhere a visitor could see. So, settled in code where there is one right
|
|
27
|
+
* answer ({@link aiSettleBuildEventPage}), and re-asked where there is not
|
|
28
|
+
* ({@link aiBuildUnlistedEventsCheck}):
|
|
29
|
+
*
|
|
30
|
+
* 1. Events no section names, and a page the plan makes about them (its
|
|
31
|
+
* address or title says events): the events are named in that page's
|
|
32
|
+
* section about them, else its second section, else its first.
|
|
33
|
+
* 2. None such, the site has no page at `/events`, and the plan has room
|
|
34
|
+
* for one more page: the plan gains an Events page at `/events`, linked
|
|
35
|
+
* in the navigation and in the layout the other pages render in, whose
|
|
36
|
+
* one section lists them.
|
|
37
|
+
* 3. Anything else — a site that already has an events page, a plan at its
|
|
38
|
+
* page cap — is left to the plan's one re-ask, which names the events.
|
|
39
|
+
*
|
|
40
|
+
* Only the `event` operation: other placed operations keep their own rules.
|
|
41
|
+
*/ /** The operation whose items this settles; the events-calendar plugin's, named, never imported. */ export const AI_BUILD_EVENT_OP = 'event';
|
|
42
|
+
const EVENT_PAGE_TITLE = 'Events';
|
|
43
|
+
/**
|
|
44
|
+
* The page a build adds for its events. Its address is the SITE's page,
|
|
45
|
+
* spelled from its title so it is never read as the console's Events page.
|
|
46
|
+
*/ export const AI_BUILD_EVENT_PAGE = {
|
|
47
|
+
title: EVENT_PAGE_TITLE,
|
|
48
|
+
slug: `/${EVENT_PAGE_TITLE.toLowerCase()}`,
|
|
49
|
+
section: 'Upcoming events'
|
|
50
|
+
};
|
|
51
|
+
/** A page or section that is about events. */ const ABOUT_EVENTS = /\b(events?|calendar|upcoming|what'?s on|shows?|gigs?|schedule|classes|workshops?)\b/i;
|
|
52
|
+
const refOf = (item)=>`new:${item.name}`;
|
|
53
|
+
/** The plan's event items that no page section names. */ export function aiBuildUnlistedEvents(plan) {
|
|
54
|
+
var _plan_items;
|
|
55
|
+
const uses = new Set(plan.screens.flatMap((screen)=>screen.sections.flatMap((section)=>section.uses.map((ref)=>ref.trim().toLowerCase()))));
|
|
56
|
+
return ((_plan_items = plan.items) != null ? _plan_items : []).filter((item)=>item.op === AI_BUILD_EVENT_OP && !uses.has(refOf(item).trim().toLowerCase()));
|
|
57
|
+
}
|
|
58
|
+
/** Whether a planned page is about events: its address or its title says so. */ function aboutEvents(screen) {
|
|
59
|
+
var _screen_slug_trim_replace_split_;
|
|
60
|
+
const first = (_screen_slug_trim_replace_split_ = screen.slug.trim().replace(/^\/+/, '').split('/')[0]) != null ? _screen_slug_trim_replace_split_ : '';
|
|
61
|
+
return first !== '' && ABOUT_EVENTS.test(`${first.replace(/-/g, ' ')} ${screen.title}`);
|
|
62
|
+
}
|
|
63
|
+
/** The section of an events page that lists them: the one named for them, else its second, else its first. */ function listingSection(screen) {
|
|
64
|
+
const named = screen.sections.findIndex((section)=>ABOUT_EVENTS.test(section.name));
|
|
65
|
+
if (named !== -1) return named;
|
|
66
|
+
return screen.sections.length > 1 ? 1 : 0;
|
|
67
|
+
}
|
|
68
|
+
/** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */ export function aiSettleBuildEventPage(plan, context) {
|
|
69
|
+
var _ref;
|
|
70
|
+
var _context_ops, _plan_screens_find;
|
|
71
|
+
if (!((_context_ops = context.ops) == null ? void 0 : _context_ops.has(AI_BUILD_EVENT_OP))) return plan;
|
|
72
|
+
const unlisted = aiBuildUnlistedEvents(plan);
|
|
73
|
+
if (!unlisted.length) return plan;
|
|
74
|
+
const refs = unlisted.map(refOf);
|
|
75
|
+
// 1. A page the plan makes about them lists them.
|
|
76
|
+
const page = plan.screens.findIndex((screen)=>!screen.record && screen.sections.length && aboutEvents(screen));
|
|
77
|
+
if (page !== -1) {
|
|
78
|
+
const screen = plan.screens[page];
|
|
79
|
+
const at = listingSection(screen);
|
|
80
|
+
const section = screen.sections[at];
|
|
81
|
+
if (!section || section.uses.length + refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan;
|
|
82
|
+
const sections = screen.sections.map((one, index)=>index === at ? _extends({}, one, {
|
|
83
|
+
uses: [
|
|
84
|
+
...one.uses,
|
|
85
|
+
...refs
|
|
86
|
+
]
|
|
87
|
+
}) : one);
|
|
88
|
+
return _extends({}, plan, {
|
|
89
|
+
screens: plan.screens.map((one, index)=>index === page ? _extends({}, screen, {
|
|
90
|
+
sections
|
|
91
|
+
}) : one)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// 2. An Events page of its own, where the address is free and the plan has room.
|
|
95
|
+
const taken = new Set([
|
|
96
|
+
...context.siteSlugs,
|
|
97
|
+
...plan.screens.map((screen)=>screen.slug)
|
|
98
|
+
].map(aiPlanSlugKey));
|
|
99
|
+
if (taken.has(aiPlanSlugKey(AI_BUILD_EVENT_PAGE.slug)) || plan.screens.length >= context.pageLimit) return plan;
|
|
100
|
+
if (refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan;
|
|
101
|
+
const names = unlisted.map((item)=>item.name);
|
|
102
|
+
const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`;
|
|
103
|
+
const events = {
|
|
104
|
+
title: AI_BUILD_EVENT_PAGE.title,
|
|
105
|
+
slug: AI_BUILD_EVENT_PAGE.slug,
|
|
106
|
+
// The layout the plan's other pages render in, else the site's.
|
|
107
|
+
layout: (_ref = (_plan_screens_find = plan.screens.find((screen)=>screen.layout)) == null ? void 0 : _plan_screens_find.layout) != null ? _ref : context.siteLayout,
|
|
108
|
+
template: null,
|
|
109
|
+
duplicateOf: null,
|
|
110
|
+
nav: true,
|
|
111
|
+
seoTitle: AI_BUILD_EVENT_PAGE.title,
|
|
112
|
+
seoDescription: `Upcoming events: ${listed}.`.slice(0, AI_BUILD_PLAN_LIMITS.seoDescription),
|
|
113
|
+
sections: [
|
|
114
|
+
{
|
|
115
|
+
name: AI_BUILD_EVENT_PAGE.section,
|
|
116
|
+
uses: refs,
|
|
117
|
+
items: 0
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
record: null
|
|
121
|
+
};
|
|
122
|
+
return _extends({}, plan, {
|
|
123
|
+
screens: [
|
|
124
|
+
...plan.screens,
|
|
125
|
+
events
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** The re-ask for events the settling could not place: each named, and where to put it. */ export function aiBuildUnlistedEventsCheck(ops) {
|
|
130
|
+
return (plan)=>{
|
|
131
|
+
if (!(ops == null ? void 0 : ops.has(AI_BUILD_EVENT_OP))) return [];
|
|
132
|
+
const unlisted = aiBuildUnlistedEvents(plan);
|
|
133
|
+
if (!unlisted.length) return [];
|
|
134
|
+
const named = unlisted.map((item)=>`new:${item.name}`).join(', ');
|
|
135
|
+
return [
|
|
136
|
+
{
|
|
137
|
+
rule: null,
|
|
138
|
+
code: 'plan-job-shape',
|
|
139
|
+
message: `The events ${named} are listed on no page, so no visitor would see them. Put each in the uses of the section that lists the events, on the page the request names for them or a page about events this plan makes.`
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=ai-build-event-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-event-page.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport type { AiBuildOps } from './ai-build-job'\nimport {\n AI_BUILD_PLAN_LIMITS,\n aiPlanSlugKey,\n type AiBuildItem,\n type AiBuildPlan,\n type AiBuildPlanScreen,\n} from './ai-build-plan'\n\n/**\n * THE PAGE A BUILD'S EVENTS ARE LISTED ON (AGL-3755).\n *\n * A build's `event` items are written as events, and an event is seen on the\n * site only through an Event List a page section places — the section whose\n * uses name the event. The live eval of 2026-10-13 asked \"add our next three\n * Friday night jazz shows at 8pm to an events page\" of a site with only a\n * Home page, and the plan made the three events and no page: they landed\n * nowhere a visitor could see. So, settled in code where there is one right\n * answer ({@link aiSettleBuildEventPage}), and re-asked where there is not\n * ({@link aiBuildUnlistedEventsCheck}):\n *\n * 1. Events no section names, and a page the plan makes about them (its\n * address or title says events): the events are named in that page's\n * section about them, else its second section, else its first.\n * 2. None such, the site has no page at `/events`, and the plan has room\n * for one more page: the plan gains an Events page at `/events`, linked\n * in the navigation and in the layout the other pages render in, whose\n * one section lists them.\n * 3. Anything else — a site that already has an events page, a plan at its\n * page cap — is left to the plan's one re-ask, which names the events.\n *\n * Only the `event` operation: other placed operations keep their own rules.\n */\n\n/** The operation whose items this settles; the events-calendar plugin's, named, never imported. */\nexport const AI_BUILD_EVENT_OP = 'event'\n\nconst EVENT_PAGE_TITLE = 'Events'\n\n/**\n * The page a build adds for its events. Its address is the SITE's page,\n * spelled from its title so it is never read as the console's Events page.\n */\nexport const AI_BUILD_EVENT_PAGE = {\n title: EVENT_PAGE_TITLE,\n slug: `/${EVENT_PAGE_TITLE.toLowerCase()}`,\n section: 'Upcoming events',\n} as const\n\n/** A page or section that is about events. */\nconst ABOUT_EVENTS = /\\b(events?|calendar|upcoming|what'?s on|shows?|gigs?|schedule|classes|workshops?)\\b/i\n\nconst refOf = (item: Pick<AiBuildItem, 'name'>) => `new:${item.name}`\n\n/** The plan's event items that no page section names. */\nexport function aiBuildUnlistedEvents(plan: Pick<AiBuildPlan, 'items' | 'screens'>): AiBuildItem[] {\n const uses = new Set(\n plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses.map((ref) => ref.trim().toLowerCase()))),\n )\n return (plan.items ?? []).filter((item) => item.op === AI_BUILD_EVENT_OP && !uses.has(refOf(item).trim().toLowerCase()))\n}\n\n/** Whether a planned page is about events: its address or its title says so. */\nfunction aboutEvents(screen: Pick<AiBuildPlanScreen, 'slug' | 'title'>): boolean {\n const first = screen.slug.trim().replace(/^\\/+/, '').split('/')[0] ?? ''\n return first !== '' && ABOUT_EVENTS.test(`${first.replace(/-/g, ' ')} ${screen.title}`)\n}\n\n/** The section of an events page that lists them: the one named for them, else its second, else its first. */\nfunction listingSection(screen: AiBuildPlanScreen): number {\n const named = screen.sections.findIndex((section) => ABOUT_EVENTS.test(section.name))\n if (named !== -1) return named\n return screen.sections.length > 1 ? 1 : 0\n}\n\nexport interface AiBuildEventPageContext {\n /** The operations this site has; the settling runs only where it has `event`. */\n ops: AiBuildOps | null\n /** The site's pages, by address. */\n siteSlugs: readonly string[]\n /** How many pages the build may plan. */\n pageLimit: number\n /** The layout the site's pages render in (its home page's, else its one layout), or null. */\n siteLayout: string | null\n}\n\n/** A build plan with its unlisted events placed where there is one right answer; otherwise as it came. */\nexport function aiSettleBuildEventPage<T extends AiBuildPlan>(plan: T, context: AiBuildEventPageContext): T {\n if (!context.ops?.has(AI_BUILD_EVENT_OP)) return plan\n const unlisted = aiBuildUnlistedEvents(plan)\n if (!unlisted.length) return plan\n const refs = unlisted.map(refOf)\n // 1. A page the plan makes about them lists them.\n const page = plan.screens.findIndex((screen) => !screen.record && screen.sections.length && aboutEvents(screen))\n if (page !== -1) {\n const screen = plan.screens[page] as AiBuildPlanScreen\n const at = listingSection(screen)\n const section = screen.sections[at]\n if (!section || section.uses.length + refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan\n const sections = screen.sections.map((one, index) => (index === at ? { ...one, uses: [...one.uses, ...refs] } : one))\n return { ...plan, screens: plan.screens.map((one, index) => (index === page ? { ...screen, sections } : one)) }\n }\n // 2. An Events page of its own, where the address is free and the plan has room.\n const taken = new Set([...context.siteSlugs, ...plan.screens.map((screen) => screen.slug)].map(aiPlanSlugKey))\n if (taken.has(aiPlanSlugKey(AI_BUILD_EVENT_PAGE.slug)) || plan.screens.length >= context.pageLimit) return plan\n if (refs.length > AI_BUILD_PLAN_LIMITS.uses) return plan\n const names = unlisted.map((item) => item.name)\n const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n const events: AiBuildPlanScreen = {\n title: AI_BUILD_EVENT_PAGE.title,\n slug: AI_BUILD_EVENT_PAGE.slug,\n // The layout the plan's other pages render in, else the site's.\n layout: plan.screens.find((screen) => screen.layout)?.layout ?? context.siteLayout,\n template: null,\n duplicateOf: null,\n nav: true,\n seoTitle: AI_BUILD_EVENT_PAGE.title,\n seoDescription: `Upcoming events: ${listed}.`.slice(0, AI_BUILD_PLAN_LIMITS.seoDescription),\n sections: [{ name: AI_BUILD_EVENT_PAGE.section, uses: refs, items: 0 }],\n record: null,\n }\n return { ...plan, screens: [...plan.screens, events] }\n}\n\n/** The re-ask for events the settling could not place: each named, and where to put it. */\nexport function aiBuildUnlistedEventsCheck(ops: AiBuildOps | null): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n if (!ops?.has(AI_BUILD_EVENT_OP)) return []\n const unlisted = aiBuildUnlistedEvents(plan)\n if (!unlisted.length) return []\n const named = unlisted.map((item) => `new:${item.name}`).join(', ')\n return [\n {\n rule: null,\n code: 'plan-job-shape',\n message: `The events ${named} are listed on no page, so no visitor would see them. Put each in the uses of the section that lists the events, on the page the request names for them or a page about events this plan makes.`,\n },\n ]\n }\n}\n"],"names":["AI_BUILD_PLAN_LIMITS","aiPlanSlugKey","AI_BUILD_EVENT_OP","EVENT_PAGE_TITLE","AI_BUILD_EVENT_PAGE","title","slug","toLowerCase","section","ABOUT_EVENTS","refOf","item","name","aiBuildUnlistedEvents","plan","uses","Set","screens","flatMap","screen","sections","map","ref","trim","items","filter","op","has","aboutEvents","first","replace","split","test","listingSection","named","findIndex","length","aiSettleBuildEventPage","context","ops","unlisted","refs","page","record","at","one","index","taken","siteSlugs","pageLimit","names","listed","slice","join","events","layout","find","siteLayout","template","duplicateOf","nav","seoTitle","seoDescription","aiBuildUnlistedEventsCheck","rule","code","message"],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import { type PluginAiCapability } from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities';
|
|
18
|
+
import type { PluginDraftRecord } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts';
|
|
18
19
|
import { type AiBuildItem, type AiBuildPlan, type AiBuildPlanCreate, type AiBuildPlanCreateKind, type AiBuildPlanScreen } from './ai-build-plan';
|
|
19
20
|
import type { AiJobItemLedger } from './ai-jobs.types';
|
|
20
21
|
import { type AiCreditRange, type AiCreditsSmaller } from './ai-credit-estimate';
|
|
@@ -59,6 +60,22 @@ export declare const AI_BUILD_CREATE_KINDS: readonly AiBuildPlanCreateKind[];
|
|
|
59
60
|
* `items`: an item naming one is a plan that put it in the wrong list.
|
|
60
61
|
*/
|
|
61
62
|
export declare const AI_BUILD_STRUCTURAL_OPS: readonly string[];
|
|
63
|
+
/**
|
|
64
|
+
* What a build hands an item it made before it (AGL-3728): its pages, by the
|
|
65
|
+
* title the plan gave each and the id it was written under, and its layouts,
|
|
66
|
+
* by name. An item's `new:<title>` destinations are read against it.
|
|
67
|
+
*/
|
|
68
|
+
export declare const AI_BUILD_MADE_INPUT = "buildMade";
|
|
69
|
+
export interface AiBuildMade {
|
|
70
|
+
pages: Array<{
|
|
71
|
+
title: string;
|
|
72
|
+
id: string;
|
|
73
|
+
}>;
|
|
74
|
+
layouts: Array<{
|
|
75
|
+
name: string;
|
|
76
|
+
id: string;
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
62
79
|
/** The job input a build's Assist proposal sets when the brief asked to publish. */
|
|
63
80
|
export declare const AI_BUILD_PUBLISH_INPUT = "publish";
|
|
64
81
|
/** A unit of a build: a creation, a page, or an item. */
|
|
@@ -75,6 +92,13 @@ export interface AiBuildUnit {
|
|
|
75
92
|
}
|
|
76
93
|
/** The name a `new:<name>` reference gives, lowercased. */
|
|
77
94
|
export declare function aiBuildRefName(ref: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* The `new:<name>` references an item's arguments make (AGL-3728): an
|
|
97
|
+
* argument that is one, or the destination after `=>` in an entry such as a
|
|
98
|
+
* menu item's `About => new:About`. Read from the arguments, never from the
|
|
99
|
+
* model's `dependsOn`, so what an item links is always built before it.
|
|
100
|
+
*/
|
|
101
|
+
export declare function aiBuildItemArgRefs(args: Readonly<Record<string, unknown>> | null | undefined): string[];
|
|
78
102
|
/** Every reference a page makes: its layout, template, starting page and what its sections place. */
|
|
79
103
|
export declare function aiBuildScreenRefs(screen: AiBuildPlanScreen): string[];
|
|
80
104
|
/**
|
|
@@ -112,6 +136,16 @@ export declare function aiBuildPlanShapeRefusal(plan: AiBuildPlan, options?: {
|
|
|
112
136
|
freeTaste?: boolean;
|
|
113
137
|
ops?: AiBuildOps;
|
|
114
138
|
}): string | null;
|
|
139
|
+
/**
|
|
140
|
+
* What the person must still say before this plan can be built, as the
|
|
141
|
+
* questions its operations ask (`missingInput`), or `null` when nothing is
|
|
142
|
+
* missing: an item the planner kept with nothing it may build from — a
|
|
143
|
+
* tracking change naming no id, AGL-3751 — is asked about, never built
|
|
144
|
+
* empty or from a value the planner made up. Not a plan rule: a re-ask
|
|
145
|
+
* cannot supply what only the person knows, so the plan is not kept and
|
|
146
|
+
* the build answers with the question.
|
|
147
|
+
*/
|
|
148
|
+
export declare function aiBuildPlanMissingInput(plan: AiBuildPlan, ops: AiBuildOps | null | undefined): string | null;
|
|
115
149
|
/** "a booking service", "an email design". */
|
|
116
150
|
export declare function aiArticle(noun: string): string;
|
|
117
151
|
/**
|
|
@@ -217,6 +251,8 @@ export declare function aiBuildPlacedItemLines(unit: AiBuildUnit, context: {
|
|
|
217
251
|
units: readonly AiBuildUnit[];
|
|
218
252
|
ledger: readonly AiJobItemLedger[];
|
|
219
253
|
ops?: AiBuildOps;
|
|
254
|
+
/** Operations whose items the platform places itself (a listing), so the page is told nothing of them. */
|
|
255
|
+
placedByCode?: readonly string[];
|
|
220
256
|
}): string[];
|
|
221
257
|
/** What a build came to: whether anything was delivered, and whether anything is left. */
|
|
222
258
|
export declare function aiBuildSettlement(ledger: readonly AiJobItemLedger[]): {
|
|
@@ -242,3 +278,11 @@ export declare function aiBuildRetryLedger(ledger: readonly AiJobItemLedger[], u
|
|
|
242
278
|
* page built after the layouts and forms the ledger holds.
|
|
243
279
|
*/
|
|
244
280
|
export declare function aiLedgerUnits(ledger: readonly AiJobItemLedger[]): AiBuildUnit[];
|
|
281
|
+
/**
|
|
282
|
+
* What the job drawer says under an item another plugin's writer made: a
|
|
283
|
+
* draft, or, for setup its writer saves live (`live`, a CRM pipeline or
|
|
284
|
+
* field, AGL-3746), that it was saved. A writer that went live only when the
|
|
285
|
+
* member may (an event the publishing role publishes, AGL-3755) reports
|
|
286
|
+
* `live: false` for one it kept as a draft, with its `notice` saying why.
|
|
287
|
+
*/
|
|
288
|
+
export declare function aiBuildItemOutputNote(capability: Pick<PluginAiCapability, 'where' | 'live'>, written?: Pick<PluginDraftRecord, 'live' | 'notice'>): string;
|
|
@@ -65,6 +65,11 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
65
65
|
'page',
|
|
66
66
|
...AI_BUILD_CREATE_KINDS
|
|
67
67
|
];
|
|
68
|
+
/**
|
|
69
|
+
* What a build hands an item it made before it (AGL-3728): its pages, by the
|
|
70
|
+
* title the plan gave each and the id it was written under, and its layouts,
|
|
71
|
+
* by name. An item's `new:<title>` destinations are read against it.
|
|
72
|
+
*/ export const AI_BUILD_MADE_INPUT = 'buildMade';
|
|
68
73
|
/** The job input a build's Assist proposal sets when the brief asked to publish. */ export const AI_BUILD_PUBLISH_INPUT = 'publish';
|
|
69
74
|
/** The operation a creation is built as. */ function creationOp(creation) {
|
|
70
75
|
return creation.kind;
|
|
@@ -81,6 +86,30 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
81
86
|
/** The name a `new:<name>` reference gives, lowercased. */ export function aiBuildRefName(ref) {
|
|
82
87
|
return ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase();
|
|
83
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* The `new:<name>` references an item's arguments make (AGL-3728): an
|
|
91
|
+
* argument that is one, or the destination after `=>` in an entry such as a
|
|
92
|
+
* menu item's `About => new:About`. Read from the arguments, never from the
|
|
93
|
+
* model's `dependsOn`, so what an item links is always built before it.
|
|
94
|
+
*/ export function aiBuildItemArgRefs(args) {
|
|
95
|
+
const refs = [];
|
|
96
|
+
const take = (value)=>{
|
|
97
|
+
var _value_split_slice_;
|
|
98
|
+
if (typeof value !== 'string') return;
|
|
99
|
+
for (const part of [
|
|
100
|
+
value,
|
|
101
|
+
(_value_split_slice_ = value.split('=>').slice(-1)[0]) != null ? _value_split_slice_ : ''
|
|
102
|
+
]){
|
|
103
|
+
const ref = part.trim();
|
|
104
|
+
if (isAiPlanNewRef(ref) && !refs.includes(ref)) refs.push(ref);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
for (const value of Object.values(args != null ? args : {})){
|
|
108
|
+
if (Array.isArray(value)) value.forEach(take);
|
|
109
|
+
else take(value);
|
|
110
|
+
}
|
|
111
|
+
return refs;
|
|
112
|
+
}
|
|
84
113
|
/** Every reference a page makes: its layout, template, starting page and what its sections place. */ export function aiBuildScreenRefs(screen) {
|
|
85
114
|
return [
|
|
86
115
|
screen.layout,
|
|
@@ -97,11 +126,19 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
97
126
|
*/ export function aiBuildUnits(plan) {
|
|
98
127
|
var _plan_items;
|
|
99
128
|
const slots = namedSlots(plan);
|
|
100
|
-
|
|
129
|
+
// A page, by the title the plan gave it: what an item's arguments may link
|
|
130
|
+
// as `new:<title>` (a menu item, AGL-3728), built before the item.
|
|
131
|
+
const pageSlots = new Map();
|
|
132
|
+
plan.screens.forEach((screen, index)=>{
|
|
133
|
+
const title = screen.title.trim().toLowerCase();
|
|
134
|
+
if (title && !pageSlots.has(title)) pageSlots.set(title, `p${index}`);
|
|
135
|
+
});
|
|
136
|
+
const depsOf = (refs, self, pages = false)=>{
|
|
101
137
|
const deps = [];
|
|
102
138
|
for (const ref of refs){
|
|
139
|
+
var _slots_get;
|
|
103
140
|
if (!isAiPlanNewRef(ref)) continue;
|
|
104
|
-
const slot = slots.get(aiBuildRefName(ref));
|
|
141
|
+
const slot = (_slots_get = slots.get(aiBuildRefName(ref))) != null ? _slots_get : pages ? pageSlots.get(aiBuildRefName(ref)) : undefined;
|
|
105
142
|
if (slot && slot !== self && !deps.includes(slot)) deps.push(slot);
|
|
106
143
|
}
|
|
107
144
|
return deps;
|
|
@@ -129,7 +166,11 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
129
166
|
op: item.op,
|
|
130
167
|
label: item.name,
|
|
131
168
|
item,
|
|
132
|
-
|
|
169
|
+
// What it depends on, and what its arguments link as `new:` (AGL-3728).
|
|
170
|
+
deps: depsOf([
|
|
171
|
+
...item.dependsOn,
|
|
172
|
+
...aiBuildItemArgRefs(item.args)
|
|
173
|
+
], item.slot, true)
|
|
133
174
|
});
|
|
134
175
|
}
|
|
135
176
|
plan.screens.forEach((screen, index)=>{
|
|
@@ -259,6 +300,29 @@ import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, aiCreditRangeAdd, aiCre
|
|
|
259
300
|
}
|
|
260
301
|
return null;
|
|
261
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* What the person must still say before this plan can be built, as the
|
|
305
|
+
* questions its operations ask (`missingInput`), or `null` when nothing is
|
|
306
|
+
* missing: an item the planner kept with nothing it may build from — a
|
|
307
|
+
* tracking change naming no id, AGL-3751 — is asked about, never built
|
|
308
|
+
* empty or from a value the planner made up. Not a plan rule: a re-ask
|
|
309
|
+
* cannot supply what only the person knows, so the plan is not kept and
|
|
310
|
+
* the build answers with the question.
|
|
311
|
+
*/ export function aiBuildPlanMissingInput(plan, ops) {
|
|
312
|
+
var _plan_items;
|
|
313
|
+
if (!ops) return null;
|
|
314
|
+
const questions = [];
|
|
315
|
+
for (const item of (_plan_items = plan.items) != null ? _plan_items : []){
|
|
316
|
+
var _ops_get_missingInput, _ops_get;
|
|
317
|
+
const question = (_ops_get = ops.get(item.op)) == null ? void 0 : (_ops_get_missingInput = _ops_get.missingInput) == null ? void 0 : _ops_get_missingInput.call(_ops_get, {
|
|
318
|
+
name: item.name,
|
|
319
|
+
why: item.why,
|
|
320
|
+
args: item.args
|
|
321
|
+
});
|
|
322
|
+
if (question && !questions.includes(question)) questions.push(question);
|
|
323
|
+
}
|
|
324
|
+
return questions.length ? questions.join(' ') : null;
|
|
325
|
+
}
|
|
262
326
|
/** "a booking service", "an email design". */ export function aiArticle(noun) {
|
|
263
327
|
return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`;
|
|
264
328
|
}
|
|
@@ -477,9 +541,21 @@ const OPEN = [
|
|
|
477
541
|
continue;
|
|
478
542
|
}
|
|
479
543
|
if (unit.screen) {
|
|
480
|
-
var _capability_pageBlocks;
|
|
544
|
+
var _capability_pageBlocks, _capability_pageBlocks1;
|
|
481
545
|
const section = unit.screen.sections.find((one)=>one.uses.some((ref)=>isAiPlanNewRef(ref) && aiBuildRefName(ref) === dep.label.toLowerCase()));
|
|
482
|
-
|
|
546
|
+
// Another item this section places with the same block was built (one
|
|
547
|
+
// of several events, AGL-3743): the block stays, and only this item is
|
|
548
|
+
// missing from what it shows.
|
|
549
|
+
const blockStays = Boolean(section && (capability == null ? void 0 : (_capability_pageBlocks = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks.length) && aiBuildSectionDeliveredBlocks(section, _extends({}, context, {
|
|
550
|
+
rows,
|
|
551
|
+
except: slot
|
|
552
|
+
})).has(aiBuildBlocksKey(capability.pageBlocks)));
|
|
553
|
+
if (blockStays) {
|
|
554
|
+
result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`);
|
|
555
|
+
result.briefLines.push(`Leave the ${noun} “${dep.label}” out of the “${section == null ? void 0 : section.name}” section: it could not be created.`);
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
const blocks = (capability == null ? void 0 : (_capability_pageBlocks1 = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks1.length) ? ` the ${capability.pageBlocks.join(' or ')} block` : '';
|
|
483
559
|
result.notes.push(`Built without${blocks || ` the ${noun}`}: the ${noun} “${dep.label}” could not be created.`);
|
|
484
560
|
result.briefLines.push(`${section ? `Build the “${section.name}” section` : 'Build the page'} without the ${noun} “${dep.label}”${blocks ? ` and without${blocks}` : ''}: it could not be created.`);
|
|
485
561
|
} else {
|
|
@@ -507,22 +583,60 @@ const OPEN = [
|
|
|
507
583
|
]));
|
|
508
584
|
const lines = [];
|
|
509
585
|
for (const section of unit.screen.sections){
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
586
|
+
// One line per block a section places, naming every built item it shows:
|
|
587
|
+
// a section listing four events places the Event List once (AGL-3743).
|
|
588
|
+
const placed = new Map();
|
|
589
|
+
for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)){
|
|
590
|
+
var _ref, _placed_get, _ref1;
|
|
591
|
+
var _context_placedByCode;
|
|
516
592
|
const row = rows.get(dep.slot);
|
|
517
593
|
if (!row || !aiBuildItemDelivered(row)) continue;
|
|
518
|
-
|
|
594
|
+
if ((_context_placedByCode = context.placedByCode) == null ? void 0 : _context_placedByCode.includes(dep.op)) continue;
|
|
519
595
|
const blocks = (_ref = capability == null ? void 0 : capability.pageBlocks) != null ? _ref : [];
|
|
520
596
|
if (!blocks.length) continue;
|
|
521
|
-
|
|
597
|
+
const key = aiBuildBlocksKey(blocks);
|
|
598
|
+
const entry = (_placed_get = placed.get(key)) != null ? _placed_get : {
|
|
599
|
+
blocks,
|
|
600
|
+
noun: (_ref1 = capability == null ? void 0 : capability.noun) != null ? _ref1 : dep.op,
|
|
601
|
+
labels: []
|
|
602
|
+
};
|
|
603
|
+
entry.labels.push(`“${dep.label}”`);
|
|
604
|
+
placed.set(key, entry);
|
|
605
|
+
}
|
|
606
|
+
for (const { blocks, noun, labels } of placed.values()){
|
|
607
|
+
const named = labels.length === 1 ? `the ${noun} ${labels[0]}` : `the ${noun}s ${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`;
|
|
608
|
+
lines.push(`In the “${section.name}” section, place the ${blocks.join(' or ')} block for ${named}.`);
|
|
522
609
|
}
|
|
523
610
|
}
|
|
524
611
|
return lines;
|
|
525
612
|
}
|
|
613
|
+
/** The items a page section places, each with its capability, in the order its uses name them. */ function aiBuildSectionItems(section, units, ops) {
|
|
614
|
+
const found = [];
|
|
615
|
+
for (const ref of section.uses){
|
|
616
|
+
if (!isAiPlanNewRef(ref)) continue;
|
|
617
|
+
const dep = units.find((one)=>one.item && one.item.name.toLowerCase() === aiBuildRefName(ref));
|
|
618
|
+
if (dep == null ? void 0 : dep.item) found.push({
|
|
619
|
+
dep,
|
|
620
|
+
capability: ops == null ? void 0 : ops.get(dep.op)
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return found;
|
|
624
|
+
}
|
|
625
|
+
/** The blocks a section's built items place, each set as one key; `except` leaves one item out. */ function aiBuildSectionDeliveredBlocks(section, context) {
|
|
626
|
+
const keys = new Set();
|
|
627
|
+
for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)){
|
|
628
|
+
var _capability_pageBlocks;
|
|
629
|
+
if (dep.slot === context.except || !(capability == null ? void 0 : (_capability_pageBlocks = capability.pageBlocks) == null ? void 0 : _capability_pageBlocks.length)) continue;
|
|
630
|
+
const row = context.rows.get(dep.slot);
|
|
631
|
+
if (row && aiBuildItemDelivered(row)) keys.add(aiBuildBlocksKey(capability.pageBlocks));
|
|
632
|
+
}
|
|
633
|
+
return keys;
|
|
634
|
+
}
|
|
635
|
+
function aiBuildBlocksKey(blocks) {
|
|
636
|
+
return [
|
|
637
|
+
...blocks
|
|
638
|
+
].sort().join('|');
|
|
639
|
+
}
|
|
526
640
|
/** What a build came to: whether anything was delivered, and whether anything is left. */ export function aiBuildSettlement(ledger) {
|
|
527
641
|
return {
|
|
528
642
|
delivered: ledger.some(aiBuildItemDelivered),
|
|
@@ -602,5 +716,19 @@ const OPEN = [
|
|
|
602
716
|
deps: row.op === 'page' ? creations : []
|
|
603
717
|
}));
|
|
604
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* What the job drawer says under an item another plugin's writer made: a
|
|
721
|
+
* draft, or, for setup its writer saves live (`live`, a CRM pipeline or
|
|
722
|
+
* field, AGL-3746), that it was saved. A writer that went live only when the
|
|
723
|
+
* member may (an event the publishing role publishes, AGL-3755) reports
|
|
724
|
+
* `live: false` for one it kept as a draft, with its `notice` saying why.
|
|
725
|
+
*/ export function aiBuildItemOutputNote(capability, written) {
|
|
726
|
+
var _ref;
|
|
727
|
+
var _written_notice;
|
|
728
|
+
const live = (_ref = written == null ? void 0 : written.live) != null ? _ref : capability.live;
|
|
729
|
+
const said = live ? `Live now, in ${capability.where}.` : `A draft, in ${capability.where}.`;
|
|
730
|
+
const notice = written == null ? void 0 : (_written_notice = written.notice) == null ? void 0 : _written_notice.trim();
|
|
731
|
+
return notice ? `${said} ${notice}` : said;
|
|
732
|
+
}
|
|
605
733
|
|
|
606
734
|
//# sourceMappingURL=ai-build-job.js.map
|