@aglyn/plugins-ai 1.0.0-beta.269 → 1.0.0-beta.271
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.d.ts +1 -1
- package/src/lib/activity/ai-activity-actions.js +1 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/components/ai-credits-card.component.d.ts +8 -6
- package/src/lib/components/ai-credits-card.component.js +39 -22
- package/src/lib/components/ai-credits-card.component.js.map +1 -1
- package/src/lib/components/ai-job-input-form.component.d.ts +36 -0
- package/src/lib/components/ai-job-input-form.component.js +100 -0
- package/src/lib/components/ai-job-input-form.component.js.map +1 -0
- package/src/lib/components/ai-job-links.d.ts +6 -0
- package/src/lib/components/ai-job-links.js +9 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +4 -0
- package/src/lib/components/ai-job-requests.js +4 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-products-hub-card.component.d.ts +6 -0
- package/src/lib/components/ai-products-hub-card.component.js +21 -6
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +3 -2
- package/src/lib/components/ai-site-build-page.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 +99 -31
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
- package/src/lib/components/ai-site-start-steps.component.js +651 -15
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
- package/src/lib/components/ai-site-start-tracking.js +14 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.js +26 -22
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/components/ai-store-finish-card.component.d.ts +14 -3
- package/src/lib/components/ai-store-finish-card.component.js +67 -9
- package/src/lib/components/ai-store-finish-card.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
- package/src/lib/components/ai-upsell-dialog.component.js +8 -6
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.js +14 -3
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +9 -1
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +8 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +4 -7
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/declarations.d.ts +2 -2
- package/src/lib/declarations.js +9 -5
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +3 -1
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +3 -0
- package/src/lib/jobs/ai-job-build-step.js +13 -5
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +24 -1
- package/src/lib/jobs/ai-job-form-step.js +93 -7
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +35 -11
- package/src/lib/jobs/ai-job-page-language.js +68 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +17 -0
- package/src/lib/jobs/ai-job-page-step.js +40 -17
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
- package/src/lib/jobs/ai-job-plan-step.js +103 -29
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.js +4 -3
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
- package/src/lib/jobs/ai-job-site-datasets.js +140 -14
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-look.js +18 -5
- package/src/lib/jobs/ai-job-site-look.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
- package/src/lib/jobs/ai-job-site-step.js +95 -22
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-tracking-step.js +28 -5
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +8 -1
- package/src/lib/jobs/ai-jobs.js +63 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-owner-photos.d.ts +57 -0
- package/src/lib/jobs/ai-layout-owner-photos.js +110 -0
- package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -0
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +62 -4
- package/src/lib/jobs/ai-layout-stock-photos.js +523 -72
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
- package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
- package/src/lib/jobs/ai-site-inspiration.js +346 -0
- package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +26 -1
- package/src/lib/jobs/ai-site-publish.js +162 -13
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +16 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js +18 -4
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.d.ts +29 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.js +291 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3942-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +438 -25
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
- package/src/lib/layout-language/ai-layout-contact.js +137 -0
- package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-copy.js +34 -2
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
- package/src/lib/layout-language/ai-layout-design.js +411 -97
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +23 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-labels.js +12 -6
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
- package/src/lib/layout-language/ai-layout-language.js +4 -3
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
- package/src/lib/layout-language/ai-layout-links.js +30 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
- package/src/lib/layout-language/ai-layout-pictures.js +145 -22
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +16 -1
- package/src/lib/model/ai-build-job.js +40 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-job-input.d.ts +33 -0
- package/src/lib/model/ai-job-input.js +59 -0
- package/src/lib/model/ai-job-input.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +4 -0
- package/src/lib/model/ai-job-notice.js +11 -1
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +40 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-details.d.ts +122 -0
- package/src/lib/model/ai-site-details.js +300 -0
- package/src/lib/model/ai-site-details.js.map +1 -0
- package/src/lib/model/ai-site-entity.d.ts +181 -0
- package/src/lib/model/ai-site-entity.js +390 -0
- package/src/lib/model/ai-site-entity.js.map +1 -0
- package/src/lib/model/ai-site-inspiration.d.ts +72 -0
- package/src/lib/model/ai-site-inspiration.js +109 -0
- package/src/lib/model/ai-site-inspiration.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +13 -1
- package/src/lib/model/ai-site-job.js +15 -2
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +109 -21
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +10 -0
- package/src/lib/model/ai-site-look.js +24 -0
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +19 -5
- package/src/lib/model/ai-site-start-seo.js +25 -7
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +9 -3
- package/src/lib/model/ai-site-start-steps.js +67 -5
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +68 -4
- package/src/lib/model/ai-site-start.js +166 -13
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-upsell-copy.js +4 -1
- package/src/lib/model/ai-upsell-copy.js.map +1 -1
- package/src/lib/native-nav.d.ts +26 -0
- package/src/lib/native-nav.js +58 -0
- package/src/lib/native-nav.js.map +1 -0
- package/src/lib/plan-entitlements.d.ts +4 -3
- package/src/lib/plan-entitlements.js +3 -8
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
- package/src/lib/runtime/ai-dataset-generation.js +12 -2
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
- package/src/lib/runtime/ai-doctrine-validators.js +54 -44
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +7 -0
- package/src/lib/runtime/ai-node-tree.js +9 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +1 -1
- package/src/lib/server/ai-create-route.js +3 -4
- package/src/lib/server/ai-create-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +27 -3
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +7 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +4 -4
- package/src/lib/usage/assist-credits.js +4 -4
- package/src/lib/usage/assist-credits.js.map +1 -1
- package/src/lib/usage/staff-org-ai-view.js +6 -3
- package/src/lib/usage/staff-org-ai-view.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +4 -2
- package/src/lib/usage/staff-org-ai.js +7 -7
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage/staff-org-usage-ai.js +2 -2
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
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 { AiSiteContact } from '../model/ai-site-details';
|
|
18
|
+
import type { AiLayoutBlock, AiLayoutItem, AiLayoutSection } from './ai-layout-language';
|
|
19
|
+
/**
|
|
20
|
+
* The business's phone and email on its pages (AGL-3979), by code.
|
|
21
|
+
*
|
|
22
|
+
* Told them, a model writes them into the copy where it sees fit — a header
|
|
23
|
+
* button that calls, a line in an About page. Two places are not left to it:
|
|
24
|
+
* the footer, which is on every page, and the page that places the site's
|
|
25
|
+
* form, which is where a visitor goes to get in touch. Each shows the phone
|
|
26
|
+
* and the email, as links that call and write, wherever the design did not
|
|
27
|
+
* already show them. Nothing is added for a detail the person did not give,
|
|
28
|
+
* so a start without them builds exactly the pages it built before.
|
|
29
|
+
*/
|
|
30
|
+
/** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */
|
|
31
|
+
export declare const AI_LAYOUT_CONTACT_LABEL_MAX = 40;
|
|
32
|
+
/** Which of the phone and the email some blocks already show, in their words or as links. */
|
|
33
|
+
export declare function aiLayoutContactShown(blocks: readonly AiLayoutBlock[], contact: AiSiteContact): {
|
|
34
|
+
phone: boolean;
|
|
35
|
+
email: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* A contact link's words, kept whole (AGL-3979): words that carry the phone
|
|
39
|
+
* number or the email are a label a cut would break — "Email
|
|
40
|
+
* hello@harborline.co" cut to a button's 28 characters is "Email" — so they
|
|
41
|
+
* keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number
|
|
42
|
+
* or address. `''` for words that carry neither.
|
|
43
|
+
*/
|
|
44
|
+
export declare function aiLayoutContactLabel(text: string | undefined, contact: AiSiteContact | null | undefined): string;
|
|
45
|
+
/** The lines that link what is not yet shown: the phone, then the email. */
|
|
46
|
+
export declare function aiLayoutContactItems(contact: AiSiteContact, shown?: {
|
|
47
|
+
phone: boolean;
|
|
48
|
+
email: boolean;
|
|
49
|
+
}): AiLayoutItem[];
|
|
50
|
+
/**
|
|
51
|
+
* A page's sections with the phone and email in the section that places the
|
|
52
|
+
* site's form (AGL-3979), where no section of the page shows them yet: a list
|
|
53
|
+
* of the two links, under the words beside the form — in their column, in a
|
|
54
|
+
* row — or, in a section of one column, just before the form. The sections
|
|
55
|
+
* as given where there is no such section or nothing to add.
|
|
56
|
+
*/
|
|
57
|
+
export declare function aiLayoutWithContactLines(sections: readonly AiLayoutSection[], formSection: number | null, contact: AiSiteContact | null | undefined): readonly AiLayoutSection[];
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* The business's phone and email on its pages (AGL-3979), by code.
|
|
19
|
+
*
|
|
20
|
+
* Told them, a model writes them into the copy where it sees fit — a header
|
|
21
|
+
* button that calls, a line in an About page. Two places are not left to it:
|
|
22
|
+
* the footer, which is on every page, and the page that places the site's
|
|
23
|
+
* form, which is where a visitor goes to get in touch. Each shows the phone
|
|
24
|
+
* and the email, as links that call and write, wherever the design did not
|
|
25
|
+
* already show them. Nothing is added for a detail the person did not give,
|
|
26
|
+
* so a start without them builds exactly the pages it built before.
|
|
27
|
+
*/ /** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */ export const AI_LAYOUT_CONTACT_LABEL_MAX = 40;
|
|
28
|
+
const digitsOf = (value)=>value.replace(/\D/g, '');
|
|
29
|
+
/** Whether words carry the phone number (by its digits) or the email address the job was given. */ function says(text, contact) {
|
|
30
|
+
const value = text != null ? text : '';
|
|
31
|
+
const digits = digitsOf(contact.phone);
|
|
32
|
+
return {
|
|
33
|
+
phone: Boolean(contact.phone) && digits.length >= 7 && digitsOf(value).includes(digits),
|
|
34
|
+
email: Boolean(contact.email) && value.toLowerCase().includes(contact.email.toLowerCase())
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Whether a link names the phone or the email (`phone`, `tel:…`, `email`, `mailto:…`). */ function links(to) {
|
|
38
|
+
const lower = (to != null ? to : '').trim().toLowerCase();
|
|
39
|
+
return {
|
|
40
|
+
phone: lower === 'phone' || lower === 'call' || lower.startsWith('tel:'),
|
|
41
|
+
email: lower === 'email' || lower === 'mail' || lower.startsWith('mailto:')
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Which of the phone and the email some blocks already show, in their words or as links. */ export function aiLayoutContactShown(blocks, contact) {
|
|
45
|
+
const shown = {
|
|
46
|
+
phone: false,
|
|
47
|
+
email: false
|
|
48
|
+
};
|
|
49
|
+
const note = (found)=>{
|
|
50
|
+
var _shown, _shown1;
|
|
51
|
+
(_shown = shown).phone || (_shown.phone = found.phone);
|
|
52
|
+
(_shown1 = shown).email || (_shown1.email = found.email);
|
|
53
|
+
};
|
|
54
|
+
for (const block of blocks){
|
|
55
|
+
var _block_items;
|
|
56
|
+
note(says(block.text, contact));
|
|
57
|
+
note(links(block.to));
|
|
58
|
+
for (const item of (_block_items = block.items) != null ? _block_items : []){
|
|
59
|
+
note(says(`${item.title} ${item.text}`, contact));
|
|
60
|
+
note(links(item.to));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return shown;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A contact link's words, kept whole (AGL-3979): words that carry the phone
|
|
67
|
+
* number or the email are a label a cut would break — "Email
|
|
68
|
+
* hello@harborline.co" cut to a button's 28 characters is "Email" — so they
|
|
69
|
+
* keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number
|
|
70
|
+
* or address. `''` for words that carry neither.
|
|
71
|
+
*/ export function aiLayoutContactLabel(text, contact) {
|
|
72
|
+
if (!contact) return '';
|
|
73
|
+
const value = (text != null ? text : '').replace(/\s+/g, ' ').trim();
|
|
74
|
+
const found = says(value, contact);
|
|
75
|
+
if (!found.phone && !found.email) return '';
|
|
76
|
+
if (value.length <= AI_LAYOUT_CONTACT_LABEL_MAX) return value;
|
|
77
|
+
return (found.email ? contact.email : contact.phone).slice(0, AI_LAYOUT_CONTACT_LABEL_MAX);
|
|
78
|
+
}
|
|
79
|
+
/** The lines that link what is not yet shown: the phone, then the email. */ export function aiLayoutContactItems(contact, shown = {
|
|
80
|
+
phone: false,
|
|
81
|
+
email: false
|
|
82
|
+
}) {
|
|
83
|
+
return [
|
|
84
|
+
...contact.phoneHref && !shown.phone ? [
|
|
85
|
+
{
|
|
86
|
+
title: contact.phone,
|
|
87
|
+
text: '',
|
|
88
|
+
to: 'phone'
|
|
89
|
+
}
|
|
90
|
+
] : [],
|
|
91
|
+
...contact.emailHref && !shown.email ? [
|
|
92
|
+
{
|
|
93
|
+
title: contact.email,
|
|
94
|
+
text: '',
|
|
95
|
+
to: 'email'
|
|
96
|
+
}
|
|
97
|
+
] : []
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A page's sections with the phone and email in the section that places the
|
|
102
|
+
* site's form (AGL-3979), where no section of the page shows them yet: a list
|
|
103
|
+
* of the two links, under the words beside the form — in their column, in a
|
|
104
|
+
* row — or, in a section of one column, just before the form. The sections
|
|
105
|
+
* as given where there is no such section or nothing to add.
|
|
106
|
+
*/ export function aiLayoutWithContactLines(sections, formSection, contact) {
|
|
107
|
+
if (!contact || formSection === null || !sections[formSection]) return sections;
|
|
108
|
+
const shown = aiLayoutContactShown(sections.flatMap((section)=>section.blocks), contact);
|
|
109
|
+
const items = aiLayoutContactItems(contact, shown);
|
|
110
|
+
if (!items.length) return sections;
|
|
111
|
+
const section = sections[formSection];
|
|
112
|
+
const blocks = [
|
|
113
|
+
...section.blocks
|
|
114
|
+
];
|
|
115
|
+
const formAt = blocks.findIndex((block)=>block.kind === 'form');
|
|
116
|
+
const form = formAt === -1 ? null : blocks[formAt];
|
|
117
|
+
const cols = section.cols && section.cols.length >= 2 ? section.cols.length : 0;
|
|
118
|
+
const besideForm = cols ? [
|
|
119
|
+
...blocks
|
|
120
|
+
].reverse().find((block)=>block.kind !== 'form' && block.col !== undefined && block.col !== (form == null ? void 0 : form.col)) : undefined;
|
|
121
|
+
const list = _extends({
|
|
122
|
+
kind: 'list',
|
|
123
|
+
items
|
|
124
|
+
}, (besideForm == null ? void 0 : besideForm.col) !== undefined ? {
|
|
125
|
+
col: besideForm.col
|
|
126
|
+
} : (form == null ? void 0 : form.col) !== undefined ? {
|
|
127
|
+
col: form.col
|
|
128
|
+
} : {});
|
|
129
|
+
if (besideForm) blocks.splice(blocks.lastIndexOf(besideForm) + 1, 0, list);
|
|
130
|
+
else if (formAt !== -1) blocks.splice(formAt, 0, list);
|
|
131
|
+
else blocks.push(list);
|
|
132
|
+
return sections.map((each, index)=>index === formSection ? _extends({}, section, {
|
|
133
|
+
blocks
|
|
134
|
+
}) : each);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=ai-layout-contact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-contact.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 { AiSiteContact } from '../model/ai-site-details'\nimport type { AiLayoutBlock, AiLayoutItem, AiLayoutSection } from './ai-layout-language'\n\n/**\n * The business's phone and email on its pages (AGL-3979), by code.\n *\n * Told them, a model writes them into the copy where it sees fit — a header\n * button that calls, a line in an About page. Two places are not left to it:\n * the footer, which is on every page, and the page that places the site's\n * form, which is where a visitor goes to get in touch. Each shows the phone\n * and the email, as links that call and write, wherever the design did not\n * already show them. Nothing is added for a detail the person did not give,\n * so a start without them builds exactly the pages it built before.\n */\n\n/** The longest a contact link's words may be: a Page Link's label, which the palette holds to 40. */\nexport const AI_LAYOUT_CONTACT_LABEL_MAX = 40\n\nconst digitsOf = (value: string) => value.replace(/\\D/g, '')\n\n/** Whether words carry the phone number (by its digits) or the email address the job was given. */\nfunction says(text: string | undefined, contact: AiSiteContact): { phone: boolean; email: boolean } {\n const value = text ?? ''\n const digits = digitsOf(contact.phone)\n return {\n phone: Boolean(contact.phone) && digits.length >= 7 && digitsOf(value).includes(digits),\n email: Boolean(contact.email) && value.toLowerCase().includes(contact.email.toLowerCase()),\n }\n}\n\n/** Whether a link names the phone or the email (`phone`, `tel:…`, `email`, `mailto:…`). */\nfunction links(to: string | undefined): { phone: boolean; email: boolean } {\n const lower = (to ?? '').trim().toLowerCase()\n return {\n phone: lower === 'phone' || lower === 'call' || lower.startsWith('tel:'),\n email: lower === 'email' || lower === 'mail' || lower.startsWith('mailto:'),\n }\n}\n\n/** Which of the phone and the email some blocks already show, in their words or as links. */\nexport function aiLayoutContactShown(\n blocks: readonly AiLayoutBlock[],\n contact: AiSiteContact,\n): { phone: boolean; email: boolean } {\n const shown = { phone: false, email: false }\n const note = (found: { phone: boolean; email: boolean }) => {\n shown.phone ||= found.phone\n shown.email ||= found.email\n }\n for (const block of blocks) {\n note(says(block.text, contact))\n note(links(block.to))\n for (const item of block.items ?? []) {\n note(says(`${item.title} ${item.text}`, contact))\n note(links(item.to))\n }\n }\n return shown\n}\n\n/**\n * A contact link's words, kept whole (AGL-3979): words that carry the phone\n * number or the email are a label a cut would break — \"Email\n * hello@harborline.co\" cut to a button's 28 characters is \"Email\" — so they\n * keep up to {@link AI_LAYOUT_CONTACT_LABEL_MAX}, and past that the bare number\n * or address. `''` for words that carry neither.\n */\nexport function aiLayoutContactLabel(text: string | undefined, contact: AiSiteContact | null | undefined): string {\n if (!contact) return ''\n const value = (text ?? '').replace(/\\s+/g, ' ').trim()\n const found = says(value, contact)\n if (!found.phone && !found.email) return ''\n if (value.length <= AI_LAYOUT_CONTACT_LABEL_MAX) return value\n return (found.email ? contact.email : contact.phone).slice(0, AI_LAYOUT_CONTACT_LABEL_MAX)\n}\n\n/** The lines that link what is not yet shown: the phone, then the email. */\nexport function aiLayoutContactItems(\n contact: AiSiteContact,\n shown: { phone: boolean; email: boolean } = { phone: false, email: false },\n): AiLayoutItem[] {\n return [\n ...(contact.phoneHref && !shown.phone ? [{ title: contact.phone, text: '', to: 'phone' }] : []),\n ...(contact.emailHref && !shown.email ? [{ title: contact.email, text: '', to: 'email' }] : []),\n ]\n}\n\n/**\n * A page's sections with the phone and email in the section that places the\n * site's form (AGL-3979), where no section of the page shows them yet: a list\n * of the two links, under the words beside the form — in their column, in a\n * row — or, in a section of one column, just before the form. The sections\n * as given where there is no such section or nothing to add.\n */\nexport function aiLayoutWithContactLines(\n sections: readonly AiLayoutSection[],\n formSection: number | null,\n contact: AiSiteContact | null | undefined,\n): readonly AiLayoutSection[] {\n if (!contact || formSection === null || !sections[formSection]) return sections\n const shown = aiLayoutContactShown(sections.flatMap((section) => section.blocks), contact)\n const items = aiLayoutContactItems(contact, shown)\n if (!items.length) return sections\n const section = sections[formSection]\n const blocks = [...section.blocks]\n const formAt = blocks.findIndex((block) => block.kind === 'form')\n const form = formAt === -1 ? null : blocks[formAt]\n const cols = section.cols && section.cols.length >= 2 ? section.cols.length : 0\n const besideForm = cols\n ? [...blocks].reverse().find((block) => block.kind !== 'form' && block.col !== undefined && block.col !== form?.col)\n : undefined\n const list: AiLayoutBlock = {\n kind: 'list',\n items,\n ...(besideForm?.col !== undefined ? { col: besideForm.col } : form?.col !== undefined ? { col: form.col } : {}),\n }\n if (besideForm) blocks.splice(blocks.lastIndexOf(besideForm) + 1, 0, list)\n else if (formAt !== -1) blocks.splice(formAt, 0, list)\n else blocks.push(list)\n return sections.map((each, index) => (index === formSection ? { ...section, blocks } : each))\n}\n"],"names":["AI_LAYOUT_CONTACT_LABEL_MAX","digitsOf","value","replace","says","text","contact","digits","phone","Boolean","length","includes","email","toLowerCase","links","to","lower","trim","startsWith","aiLayoutContactShown","blocks","shown","note","found","block","item","items","title","aiLayoutContactLabel","slice","aiLayoutContactItems","phoneHref","emailHref","aiLayoutWithContactLines","sections","formSection","flatMap","section","formAt","findIndex","kind","form","cols","besideForm","reverse","find","col","undefined","list","splice","lastIndexOf","push","map","each","index"],"mappings":"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"}
|
|
@@ -50,6 +50,18 @@ export declare const AI_LAYOUT_TEXT_LIMITS: {
|
|
|
50
50
|
export type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS;
|
|
51
51
|
/** Collapses whitespace and takes out markup a page would print. */
|
|
52
52
|
export declare function aiLayoutCleanText(value: unknown): string;
|
|
53
|
+
/**
|
|
54
|
+
* "a" and "an" made to agree with the word after them, in English copy
|
|
55
|
+
* (AGL-3988): the beta.270 Sugar Hollow packages said "A easygoing session"
|
|
56
|
+
* twice, in their records and on the home page's cards. Only where the
|
|
57
|
+
* letters settle it: "a" before a word opening a, e, i or o becomes "an" —
|
|
58
|
+
* never before "one" or "once", or "eu" ("a European"); "an" before a word
|
|
59
|
+
* opening a consonant other than h becomes "a". A word opening u or h, or
|
|
60
|
+
* one in capitals ("an FAQ"), is said as the model wrote it, and so is a
|
|
61
|
+
* capital "A" inside a sentence, a letter rather than an article ("Plan A
|
|
62
|
+
* is"). Any other language's text comes back as it is.
|
|
63
|
+
*/
|
|
64
|
+
export declare function aiLayoutArticles(text: string, language?: string | null): string;
|
|
53
65
|
/**
|
|
54
66
|
* The line with every phone number and email address the brief did not give
|
|
55
67
|
* written as the gap (rule 14): a site's contact details are facts, and a
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { aiDanglingWord, detectOffVoiceCopy } from "../runtime/ai-doctrine-validators.js";
|
|
17
|
+
import { aiLayoutSpeaksOtherThanEnglish } from "./ai-layout-labels.js";
|
|
17
18
|
/**
|
|
18
19
|
* The words of a layout language document, made to fit where they are shown
|
|
19
20
|
* (AGL-3660). Every word is the model's; the compiler only cleans and fits
|
|
@@ -49,6 +50,32 @@
|
|
|
49
50
|
return value.replace(MARKUP, '')// A JSON escape a model wrote out as text ("\\u2014") is the character it names.
|
|
50
51
|
.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex)=>String.fromCharCode(parseInt(hex, 16))).replace(/\s+/g, ' ').trim();
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* "a" and "an" made to agree with the word after them, in English copy
|
|
55
|
+
* (AGL-3988): the beta.270 Sugar Hollow packages said "A easygoing session"
|
|
56
|
+
* twice, in their records and on the home page's cards. Only where the
|
|
57
|
+
* letters settle it: "a" before a word opening a, e, i or o becomes "an" —
|
|
58
|
+
* never before "one" or "once", or "eu" ("a European"); "an" before a word
|
|
59
|
+
* opening a consonant other than h becomes "a". A word opening u or h, or
|
|
60
|
+
* one in capitals ("an FAQ"), is said as the model wrote it, and so is a
|
|
61
|
+
* capital "A" inside a sentence, a letter rather than an article ("Plan A
|
|
62
|
+
* is"). Any other language's text comes back as it is.
|
|
63
|
+
*/ export function aiLayoutArticles(text, language) {
|
|
64
|
+
if (!text || aiLayoutSpeaksOtherThanEnglish(language)) return text;
|
|
65
|
+
return text.replace(RegExp("(?<![\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}'’-])(a|an|A|An)(\\s+)([A-Za-z][\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}'’-]*)", "gu"), (whole, article, space, word, at)=>{
|
|
66
|
+
// A capital opens a sentence; anywhere else "A" is a letter, never an article.
|
|
67
|
+
if (/^A/.test(article) && !/(?:^|[.!?:]["”’)]?\s*)$/.test(text.slice(0, at))) return whole;
|
|
68
|
+
// An initialism is said by its letters, which the letters alone do not settle.
|
|
69
|
+
if (/^[A-Z]{2,}/.test(word)) return whole;
|
|
70
|
+
const lower = word.toLowerCase();
|
|
71
|
+
const vowel = /^[aeio]/.test(lower) && !/^(?:one(?![a-z])|once|eu|ewe)/.test(lower);
|
|
72
|
+
const consonant = /^[bcdfgjklmnpqrstvwxyz]/.test(lower);
|
|
73
|
+
const an = article.toLowerCase() === 'an';
|
|
74
|
+
if (!an && vowel) return `${article === 'a' ? 'an' : 'An'}${space}${word}`;
|
|
75
|
+
if (an && consonant) return `${article === 'an' ? 'a' : 'A'}${space}${word}`;
|
|
76
|
+
return whole;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
52
79
|
/**
|
|
53
80
|
* The line with every trailing dangling word taken off, as rule 14 reads one,
|
|
54
81
|
* in the site's language (AGL-3828): a Spanish line cut to "…para la" loses
|
|
@@ -68,7 +95,7 @@
|
|
|
68
95
|
* room, else the last clause boundary that does, else the last whole word. A
|
|
69
96
|
* line that fits is returned clean, with any dangling tail taken off.
|
|
70
97
|
*/ function fitText(value, place, language) {
|
|
71
|
-
const text = aiLayoutCleanText(value);
|
|
98
|
+
const text = aiLayoutArticles(aiLayoutCleanText(value), language);
|
|
72
99
|
const limit = AI_LAYOUT_TEXT_LIMITS[place];
|
|
73
100
|
if (text.length <= limit) return withoutDanglingTail(text, language);
|
|
74
101
|
const room = text.slice(0, limit + 1);
|
|
@@ -107,7 +134,12 @@ const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
|
107
134
|
const digits = digitsOf(phone);
|
|
108
135
|
// A year, a price or a count is not a phone number.
|
|
109
136
|
if (digits.length < 7) return phone;
|
|
110
|
-
|
|
137
|
+
// One number with and without its country code is one number (AGL-3979):
|
|
138
|
+
// "+1 512 555 0142" given admits "(512) 555-0142" written.
|
|
139
|
+
const known = [
|
|
140
|
+
...knownDigits
|
|
141
|
+
].some((given)=>given === digits || Math.min(given.length, digits.length) >= 7 && (given.endsWith(digits) || digits.endsWith(given)) && Math.abs(given.length - digits.length) <= 3);
|
|
142
|
+
return known ? phone : '[phone number]';
|
|
111
143
|
});
|
|
112
144
|
}
|
|
113
145
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-copy.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 {\n aiDanglingWord,\n detectOffVoiceCopy,\n} from '../runtime/ai-doctrine-validators'\n\n/**\n * The words of a layout language document, made to fit where they are shown\n * (AGL-3660). Every word is the model's; the compiler only cleans and fits\n * them: markup a person would see is taken out, a line too long for its place\n * ends at a clean boundary instead of mid-word, a line that would end on a\n * dangling word (\"…for you and\") loses it, and a phone number or an email\n * address the brief never gave is written as the bracketed gap the doctrine\n * asks for (rule 14) rather than shown as a fact.\n */\n\n/** Characters each place holds before its line is ended. */\nexport const AI_LAYOUT_TEXT_LIMITS = {\n /** A page's h1. */\n title: 70,\n /** A section's heading. */\n heading: 80,\n /** A card's, a step's or a sub-heading's title. */\n itemTitle: 60,\n eyebrow: 40,\n /** A button's label; the palette holds a label to 40. */\n label: 28,\n lede: 240,\n text: 420,\n itemText: 240,\n listItem: 140,\n stat: 14,\n statLabel: 60,\n question: 120,\n answer: 420,\n quote: 240,\n note: 200,\n /** An image's description, which is its alt text. */\n alt: 160,\n} as const\n\nexport type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS\n\n/** Inline markup a model writes out of habit, which a page would print literally. */\nconst MARKUP = /(\\*\\*|__|`|~~|^#{1,6}\\s+|^\\s*[-*•]\\s+)/gm\n\n/** A sentence's end inside a line. */\nconst SENTENCE_END = /[.!?…](?=[\"'”’)\\]]?\\s)/g\n\n/** A clause boundary inside a line: a comma, a semicolon, a colon or a dash, with what follows. */\nconst CLAUSE_END = /\\s*(?:[,;:]|\\s[—–-])\\s/g\n\n/** Punctuation that may not end a line it was cut back to. */\nconst TRAILING_JOINERS = /[\\s,;:—–-]+$/\n\n/** Collapses whitespace and takes out markup a page would print. */\nexport function aiLayoutCleanText(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value\n .replace(MARKUP, '')\n // A JSON escape a model wrote out as text (\"\\\\u2014\") is the character it names.\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * The line with every trailing dangling word taken off, as rule 14 reads one,\n * in the site's language (AGL-3828): a Spanish line cut to \"…para la\" loses\n * both words, as an English one cut to \"…for the\" does.\n */\nfunction withoutDanglingTail(line: string, language?: string): string {\n let text = line.replace(TRAILING_JOINERS, '').trim()\n for (let guard = 0; guard < 6; guard += 1) {\n const word = aiDanglingWord(text, language)\n if (!word) break\n text = text\n .slice(0, text.length - word.length)\n .replace(TRAILING_JOINERS, '')\n .trim()\n }\n return text\n}\n\n/**\n * A line held to the characters its place holds, ended where a reader expects\n * a line to end: at the last sentence's end that leaves at least half the\n * room, else the last clause boundary that does, else the last whole word. A\n * line that fits is returned clean, with any dangling tail taken off.\n */\nfunction fitText(value: unknown, place: AiLayoutTextPlace, language?: string): string {\n const text = aiLayoutCleanText(value)\n const limit = AI_LAYOUT_TEXT_LIMITS[place]\n if (text.length <= limit) return withoutDanglingTail(text, language)\n const room = text.slice(0, limit + 1)\n const half = Math.floor(limit * 0.4)\n const lastMatch = (pattern: RegExp): number => {\n let at = -1\n for (const match of room.matchAll(pattern)) {\n const end = (match.index ?? 0) + (pattern === SENTENCE_END ? 1 : 0)\n if (end <= limit && end >= half) at = end\n }\n return at\n }\n const sentence = lastMatch(SENTENCE_END)\n if (sentence > 0) return withoutDanglingTail(room.slice(0, sentence), language)\n const clause = lastMatch(CLAUSE_END)\n if (clause > 0) return withoutDanglingTail(room.slice(0, clause), language)\n const space = room.lastIndexOf(' ', limit)\n return withoutDanglingTail(\n space > half ? room.slice(0, space) : room.slice(0, limit),\n language,\n )\n}\n\n/** A phone number as people write one: seven or more digits with the usual separators. */\nconst PHONE = /\\(?\\+?\\d[\\d\\s().-]{6,}\\d/g\nconst EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g\n\n/** The digits of a phone number, which is what two spellings of one number share. */\nfunction digitsOf(value: string): string {\n return value.replace(/\\D/g, '')\n}\n\n/**\n * The line with every phone number and email address the brief did not give\n * written as the gap (rule 14): a site's contact details are facts, and a\n * model that writes one the brief lacks is inventing it. `facts` is every\n * word the job was given — the brief, the site's answers and its profile.\n */\nexport function aiLayoutGuardContacts(line: string, facts: string): string {\n const known = facts.toLowerCase()\n const knownDigits = new Set((facts.match(PHONE) ?? []).map(digitsOf))\n return line\n .replace(EMAIL, (email) =>\n known.includes(email.toLowerCase()) ? email : '[email address]',\n )\n .replace(PHONE, (phone) => {\n const digits = digitsOf(phone)\n // A year, a price or a count is not a phone number.\n if (digits.length < 7) return phone\n return knownDigits.has(digits) ? phone : '[phone number]'\n })\n}\n\n/**\n * A line fitted to its place with invented contact details written as gaps;\n * `language` is the site's (see `aiLayoutFitText`).\n */\nexport function aiLayoutWords(\n value: unknown,\n place: AiLayoutTextPlace,\n facts: string,\n language?: string,\n): string {\n return aiLayoutFitText(\n aiLayoutGuardContacts(aiLayoutCleanText(value), facts),\n place,\n language,\n )\n}\n\n/**\n * A line held to the characters its place holds (see `fitText`), and none at\n * all where what is left is filler. `language` is the site's language code,\n * which says what a dangling word is (AGL-3828); absent, English.\n */\nexport function aiLayoutFitText(\n value: unknown,\n place: AiLayoutTextPlace,\n language?: string,\n): string {\n const text = fitText(value, place, language)\n return aiLayoutIsFiller(text) ? '' : text\n}\n\n/**\n * Whether a line is filler rule 14 refuses (\"lorem ipsum\", \"your text here\"):\n * such a line is no copy, and the compiler leaves it out rather than ship it.\n */\nexport function aiLayoutIsFiller(text: string): boolean {\n return detectOffVoiceCopy([{ at: 'line', text }]).length > 0\n}\n\n/** A quote that cannot be a real customer's, written as the gap the owner fills with one (rule 14). */\nexport function aiLayoutQuoteGap(value: unknown, language?: string): string {\n const text = aiLayoutFitText(value, 'quote', language)\n .replace(/^[\"“'‘]+|[\"”'’]+$/g, '')\n .trim()\n if (!text) return ''\n if (/^\\[.*\\]$/.test(text)) return text\n return `[${text.replace(/^\\[|\\]$/g, '')}]`\n}\n"],"names":["aiDanglingWord","detectOffVoiceCopy","AI_LAYOUT_TEXT_LIMITS","title","heading","itemTitle","eyebrow","label","lede","text","itemText","listItem","stat","statLabel","question","answer","quote","note","alt","MARKUP","SENTENCE_END","CLAUSE_END","TRAILING_JOINERS","aiLayoutCleanText","value","replace","_","hex","String","fromCharCode","parseInt","trim","withoutDanglingTail","line","language","guard","word","slice","length","fitText","place","limit","room","half","Math","floor","lastMatch","pattern","at","match","matchAll","end","index","sentence","clause","space","lastIndexOf","PHONE","EMAIL","digitsOf","aiLayoutGuardContacts","facts","known","toLowerCase","knownDigits","Set","map","email","includes","phone","digits","has","aiLayoutWords","aiLayoutFitText","aiLayoutIsFiller","aiLayoutQuoteGap","test"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-copy.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 {\n aiDanglingWord,\n detectOffVoiceCopy,\n} from '../runtime/ai-doctrine-validators'\nimport { aiLayoutSpeaksOtherThanEnglish } from './ai-layout-labels'\n\n/**\n * The words of a layout language document, made to fit where they are shown\n * (AGL-3660). Every word is the model's; the compiler only cleans and fits\n * them: markup a person would see is taken out, a line too long for its place\n * ends at a clean boundary instead of mid-word, a line that would end on a\n * dangling word (\"…for you and\") loses it, and a phone number or an email\n * address the brief never gave is written as the bracketed gap the doctrine\n * asks for (rule 14) rather than shown as a fact.\n */\n\n/** Characters each place holds before its line is ended. */\nexport const AI_LAYOUT_TEXT_LIMITS = {\n /** A page's h1. */\n title: 70,\n /** A section's heading. */\n heading: 80,\n /** A card's, a step's or a sub-heading's title. */\n itemTitle: 60,\n eyebrow: 40,\n /** A button's label; the palette holds a label to 40. */\n label: 28,\n lede: 240,\n text: 420,\n itemText: 240,\n listItem: 140,\n stat: 14,\n statLabel: 60,\n question: 120,\n answer: 420,\n quote: 240,\n note: 200,\n /** An image's description, which is its alt text. */\n alt: 160,\n} as const\n\nexport type AiLayoutTextPlace = keyof typeof AI_LAYOUT_TEXT_LIMITS\n\n/** Inline markup a model writes out of habit, which a page would print literally. */\nconst MARKUP = /(\\*\\*|__|`|~~|^#{1,6}\\s+|^\\s*[-*•]\\s+)/gm\n\n/** A sentence's end inside a line. */\nconst SENTENCE_END = /[.!?…](?=[\"'”’)\\]]?\\s)/g\n\n/** A clause boundary inside a line: a comma, a semicolon, a colon or a dash, with what follows. */\nconst CLAUSE_END = /\\s*(?:[,;:]|\\s[—–-])\\s/g\n\n/** Punctuation that may not end a line it was cut back to. */\nconst TRAILING_JOINERS = /[\\s,;:—–-]+$/\n\n/** Collapses whitespace and takes out markup a page would print. */\nexport function aiLayoutCleanText(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value\n .replace(MARKUP, '')\n // A JSON escape a model wrote out as text (\"\\\\u2014\") is the character it names.\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/**\n * \"a\" and \"an\" made to agree with the word after them, in English copy\n * (AGL-3988): the beta.270 Sugar Hollow packages said \"A easygoing session\"\n * twice, in their records and on the home page's cards. Only where the\n * letters settle it: \"a\" before a word opening a, e, i or o becomes \"an\" —\n * never before \"one\" or \"once\", or \"eu\" (\"a European\"); \"an\" before a word\n * opening a consonant other than h becomes \"a\". A word opening u or h, or\n * one in capitals (\"an FAQ\"), is said as the model wrote it, and so is a\n * capital \"A\" inside a sentence, a letter rather than an article (\"Plan A\n * is\"). Any other language's text comes back as it is.\n */\nexport function aiLayoutArticles(text: string, language?: string | null): string {\n if (!text || aiLayoutSpeaksOtherThanEnglish(language)) return text\n return text.replace(\n /(?<![\\p{L}\\p{N}'’-])(a|an|A|An)(\\s+)([A-Za-z][\\p{L}\\p{N}'’-]*)/gu,\n (whole, article: string, space: string, word: string, at: number) => {\n // A capital opens a sentence; anywhere else \"A\" is a letter, never an article.\n if (/^A/.test(article) && !/(?:^|[.!?:][\"”’)]?\\s*)$/.test(text.slice(0, at))) return whole\n // An initialism is said by its letters, which the letters alone do not settle.\n if (/^[A-Z]{2,}/.test(word)) return whole\n const lower = word.toLowerCase()\n const vowel = /^[aeio]/.test(lower) && !/^(?:one(?![a-z])|once|eu|ewe)/.test(lower)\n const consonant = /^[bcdfgjklmnpqrstvwxyz]/.test(lower)\n const an = article.toLowerCase() === 'an'\n if (!an && vowel) return `${article === 'a' ? 'an' : 'An'}${space}${word}`\n if (an && consonant) return `${article === 'an' ? 'a' : 'A'}${space}${word}`\n return whole\n },\n )\n}\n\n/**\n * The line with every trailing dangling word taken off, as rule 14 reads one,\n * in the site's language (AGL-3828): a Spanish line cut to \"…para la\" loses\n * both words, as an English one cut to \"…for the\" does.\n */\nfunction withoutDanglingTail(line: string, language?: string): string {\n let text = line.replace(TRAILING_JOINERS, '').trim()\n for (let guard = 0; guard < 6; guard += 1) {\n const word = aiDanglingWord(text, language)\n if (!word) break\n text = text\n .slice(0, text.length - word.length)\n .replace(TRAILING_JOINERS, '')\n .trim()\n }\n return text\n}\n\n/**\n * A line held to the characters its place holds, ended where a reader expects\n * a line to end: at the last sentence's end that leaves at least half the\n * room, else the last clause boundary that does, else the last whole word. A\n * line that fits is returned clean, with any dangling tail taken off.\n */\nfunction fitText(value: unknown, place: AiLayoutTextPlace, language?: string): string {\n const text = aiLayoutArticles(aiLayoutCleanText(value), language)\n const limit = AI_LAYOUT_TEXT_LIMITS[place]\n if (text.length <= limit) return withoutDanglingTail(text, language)\n const room = text.slice(0, limit + 1)\n const half = Math.floor(limit * 0.4)\n const lastMatch = (pattern: RegExp): number => {\n let at = -1\n for (const match of room.matchAll(pattern)) {\n const end = (match.index ?? 0) + (pattern === SENTENCE_END ? 1 : 0)\n if (end <= limit && end >= half) at = end\n }\n return at\n }\n const sentence = lastMatch(SENTENCE_END)\n if (sentence > 0) return withoutDanglingTail(room.slice(0, sentence), language)\n const clause = lastMatch(CLAUSE_END)\n if (clause > 0) return withoutDanglingTail(room.slice(0, clause), language)\n const space = room.lastIndexOf(' ', limit)\n return withoutDanglingTail(\n space > half ? room.slice(0, space) : room.slice(0, limit),\n language,\n )\n}\n\n/** A phone number as people write one: seven or more digits with the usual separators. */\nconst PHONE = /\\(?\\+?\\d[\\d\\s().-]{6,}\\d/g\nconst EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g\n\n/** The digits of a phone number, which is what two spellings of one number share. */\nfunction digitsOf(value: string): string {\n return value.replace(/\\D/g, '')\n}\n\n/**\n * The line with every phone number and email address the brief did not give\n * written as the gap (rule 14): a site's contact details are facts, and a\n * model that writes one the brief lacks is inventing it. `facts` is every\n * word the job was given — the brief, the site's answers and its profile.\n */\nexport function aiLayoutGuardContacts(line: string, facts: string): string {\n const known = facts.toLowerCase()\n const knownDigits = new Set((facts.match(PHONE) ?? []).map(digitsOf))\n return line\n .replace(EMAIL, (email) =>\n known.includes(email.toLowerCase()) ? email : '[email address]',\n )\n .replace(PHONE, (phone) => {\n const digits = digitsOf(phone)\n // A year, a price or a count is not a phone number.\n if (digits.length < 7) return phone\n // One number with and without its country code is one number (AGL-3979):\n // \"+1 512 555 0142\" given admits \"(512) 555-0142\" written.\n const known = [...knownDigits].some(\n (given) => given === digits || (Math.min(given.length, digits.length) >= 7 && (given.endsWith(digits) || digits.endsWith(given)) && Math.abs(given.length - digits.length) <= 3),\n )\n return known ? phone : '[phone number]'\n })\n}\n\n/**\n * A line fitted to its place with invented contact details written as gaps;\n * `language` is the site's (see `aiLayoutFitText`).\n */\nexport function aiLayoutWords(\n value: unknown,\n place: AiLayoutTextPlace,\n facts: string,\n language?: string,\n): string {\n return aiLayoutFitText(\n aiLayoutGuardContacts(aiLayoutCleanText(value), facts),\n place,\n language,\n )\n}\n\n/**\n * A line held to the characters its place holds (see `fitText`), and none at\n * all where what is left is filler. `language` is the site's language code,\n * which says what a dangling word is (AGL-3828); absent, English.\n */\nexport function aiLayoutFitText(\n value: unknown,\n place: AiLayoutTextPlace,\n language?: string,\n): string {\n const text = fitText(value, place, language)\n return aiLayoutIsFiller(text) ? '' : text\n}\n\n/**\n * Whether a line is filler rule 14 refuses (\"lorem ipsum\", \"your text here\"):\n * such a line is no copy, and the compiler leaves it out rather than ship it.\n */\nexport function aiLayoutIsFiller(text: string): boolean {\n return detectOffVoiceCopy([{ at: 'line', text }]).length > 0\n}\n\n/** A quote that cannot be a real customer's, written as the gap the owner fills with one (rule 14). */\nexport function aiLayoutQuoteGap(value: unknown, language?: string): string {\n const text = aiLayoutFitText(value, 'quote', language)\n .replace(/^[\"“'‘]+|[\"”'’]+$/g, '')\n .trim()\n if (!text) return ''\n if (/^\\[.*\\]$/.test(text)) return text\n return `[${text.replace(/^\\[|\\]$/g, '')}]`\n}\n"],"names":["aiDanglingWord","detectOffVoiceCopy","aiLayoutSpeaksOtherThanEnglish","AI_LAYOUT_TEXT_LIMITS","title","heading","itemTitle","eyebrow","label","lede","text","itemText","listItem","stat","statLabel","question","answer","quote","note","alt","MARKUP","SENTENCE_END","CLAUSE_END","TRAILING_JOINERS","aiLayoutCleanText","value","replace","_","hex","String","fromCharCode","parseInt","trim","aiLayoutArticles","language","whole","article","space","word","at","test","slice","lower","toLowerCase","vowel","consonant","an","withoutDanglingTail","line","guard","length","fitText","place","limit","room","half","Math","floor","lastMatch","pattern","match","matchAll","end","index","sentence","clause","lastIndexOf","PHONE","EMAIL","digitsOf","aiLayoutGuardContacts","facts","known","knownDigits","Set","map","email","includes","phone","digits","some","given","min","endsWith","abs","aiLayoutWords","aiLayoutFitText","aiLayoutIsFiller","aiLayoutQuoteGap"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,cAAc,EACdC,kBAAkB,QACb,uCAAmC;AAC1C,SAASC,8BAA8B,QAAQ,wBAAoB;AAEnE;;;;;;;;CAQC,GAED,0DAA0D,GAC1D,OAAO,MAAMC,wBAAwB;IACnC,iBAAiB,GACjBC,OAAO;IACP,yBAAyB,GACzBC,SAAS;IACT,iDAAiD,GACjDC,WAAW;IACXC,SAAS;IACT,uDAAuD,GACvDC,OAAO;IACPC,MAAM;IACNC,MAAM;IACNC,UAAU;IACVC,UAAU;IACVC,MAAM;IACNC,WAAW;IACXC,UAAU;IACVC,QAAQ;IACRC,OAAO;IACPC,MAAM;IACN,mDAAmD,GACnDC,KAAK;AACP,EAAU;AAIV,mFAAmF,GACnF,MAAMC,SAAS;AAEf,oCAAoC,GACpC,MAAMC,eAAe;AAErB,iGAAiG,GACjG,MAAMC,aAAa;AAEnB,4DAA4D,GAC5D,MAAMC,mBAAmB;AAEzB,kEAAkE,GAClE,OAAO,SAASC,kBAAkBC,KAAc;IAC9C,IAAI,OAAOA,UAAU,UAAU,OAAO;IACtC,OAAOA,MACJC,OAAO,CAACN,QAAQ,GACjB,iFAAiF;KAChFM,OAAO,CAAC,wBAAwB,CAACC,GAAGC,MAAgBC,OAAOC,YAAY,CAACC,SAASH,KAAK,MACtFF,OAAO,CAAC,QAAQ,KAChBM,IAAI;AACT;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASC,iBAAiBvB,IAAY,EAAEwB,QAAwB;IACrE,IAAI,CAACxB,QAAQR,+BAA+BgC,WAAW,OAAOxB;IAC9D,OAAOA,KAAKgB,OAAO,CACjB,+u0BACA,CAACS,OAAOC,SAAiBC,OAAeC,MAAcC;QACpD,+EAA+E;QAC/E,IAAI,KAAKC,IAAI,CAACJ,YAAY,CAAC,0BAA0BI,IAAI,CAAC9B,KAAK+B,KAAK,CAAC,GAAGF,MAAM,OAAOJ;QACrF,+EAA+E;QAC/E,IAAI,aAAaK,IAAI,CAACF,OAAO,OAAOH;QACpC,MAAMO,QAAQJ,KAAKK,WAAW;QAC9B,MAAMC,QAAQ,UAAUJ,IAAI,CAACE,UAAU,CAAC,gCAAgCF,IAAI,CAACE;QAC7E,MAAMG,YAAY,0BAA0BL,IAAI,CAACE;QACjD,MAAMI,KAAKV,QAAQO,WAAW,OAAO;QACrC,IAAI,CAACG,MAAMF,OAAO,OAAO,GAAGR,YAAY,MAAM,OAAO,OAAOC,QAAQC,MAAM;QAC1E,IAAIQ,MAAMD,WAAW,OAAO,GAAGT,YAAY,OAAO,MAAM,MAAMC,QAAQC,MAAM;QAC5E,OAAOH;IACT;AAEJ;AAEA;;;;CAIC,GACD,SAASY,oBAAoBC,IAAY,EAAEd,QAAiB;IAC1D,IAAIxB,OAAOsC,KAAKtB,OAAO,CAACH,kBAAkB,IAAIS,IAAI;IAClD,IAAK,IAAIiB,QAAQ,GAAGA,QAAQ,GAAGA,SAAS,EAAG;QACzC,MAAMX,OAAOtC,eAAeU,MAAMwB;QAClC,IAAI,CAACI,MAAM;QACX5B,OAAOA,KACJ+B,KAAK,CAAC,GAAG/B,KAAKwC,MAAM,GAAGZ,KAAKY,MAAM,EAClCxB,OAAO,CAACH,kBAAkB,IAC1BS,IAAI;IACT;IACA,OAAOtB;AACT;AAEA;;;;;CAKC,GACD,SAASyC,QAAQ1B,KAAc,EAAE2B,KAAwB,EAAElB,QAAiB;IAC1E,MAAMxB,OAAOuB,iBAAiBT,kBAAkBC,QAAQS;IACxD,MAAMmB,QAAQlD,qBAAqB,CAACiD,MAAM;IAC1C,IAAI1C,KAAKwC,MAAM,IAAIG,OAAO,OAAON,oBAAoBrC,MAAMwB;IAC3D,MAAMoB,OAAO5C,KAAK+B,KAAK,CAAC,GAAGY,QAAQ;IACnC,MAAME,OAAOC,KAAKC,KAAK,CAACJ,QAAQ;IAChC,MAAMK,YAAY,CAACC;QACjB,IAAIpB,KAAK,CAAC;QACV,KAAK,MAAMqB,SAASN,KAAKO,QAAQ,CAACF,SAAU;gBAC7BC;YAAb,MAAME,MAAM,EAACF,eAAAA,MAAMG,KAAK,YAAXH,eAAe,KAAMD,CAAAA,YAAYtC,eAAe,IAAI,CAAA;YACjE,IAAIyC,OAAOT,SAASS,OAAOP,MAAMhB,KAAKuB;QACxC;QACA,OAAOvB;IACT;IACA,MAAMyB,WAAWN,UAAUrC;IAC3B,IAAI2C,WAAW,GAAG,OAAOjB,oBAAoBO,KAAKb,KAAK,CAAC,GAAGuB,WAAW9B;IACtE,MAAM+B,SAASP,UAAUpC;IACzB,IAAI2C,SAAS,GAAG,OAAOlB,oBAAoBO,KAAKb,KAAK,CAAC,GAAGwB,SAAS/B;IAClE,MAAMG,QAAQiB,KAAKY,WAAW,CAAC,KAAKb;IACpC,OAAON,oBACLV,QAAQkB,OAAOD,KAAKb,KAAK,CAAC,GAAGJ,SAASiB,KAAKb,KAAK,CAAC,GAAGY,QACpDnB;AAEJ;AAEA,wFAAwF,GACxF,MAAMiC,QAAQ;AACd,MAAMC,QAAQ;AAEd,mFAAmF,GACnF,SAASC,SAAS5C,KAAa;IAC7B,OAAOA,MAAMC,OAAO,CAAC,OAAO;AAC9B;AAEA;;;;;CAKC,GACD,OAAO,SAAS4C,sBAAsBtB,IAAY,EAAEuB,KAAa;QAElCA;IAD7B,MAAMC,QAAQD,MAAM5B,WAAW;IAC/B,MAAM8B,cAAc,IAAIC,IAAI,EAACH,eAAAA,MAAMX,KAAK,CAACO,kBAAZI,eAAsB,EAAE,EAAEI,GAAG,CAACN;IAC3D,OAAOrB,KACJtB,OAAO,CAAC0C,OAAO,CAACQ,QACfJ,MAAMK,QAAQ,CAACD,MAAMjC,WAAW,MAAMiC,QAAQ,mBAE/ClD,OAAO,CAACyC,OAAO,CAACW;QACf,MAAMC,SAASV,SAASS;QACxB,oDAAoD;QACpD,IAAIC,OAAO7B,MAAM,GAAG,GAAG,OAAO4B;QAC9B,yEAAyE;QACzE,2DAA2D;QAC3D,MAAMN,QAAQ;eAAIC;SAAY,CAACO,IAAI,CACjC,CAACC,QAAUA,UAAUF,UAAWvB,KAAK0B,GAAG,CAACD,MAAM/B,MAAM,EAAE6B,OAAO7B,MAAM,KAAK,KAAM+B,CAAAA,MAAME,QAAQ,CAACJ,WAAWA,OAAOI,QAAQ,CAACF,MAAK,KAAMzB,KAAK4B,GAAG,CAACH,MAAM/B,MAAM,GAAG6B,OAAO7B,MAAM,KAAK;QAEhL,OAAOsB,QAAQM,QAAQ;IACzB;AACJ;AAEA;;;CAGC,GACD,OAAO,SAASO,cACd5D,KAAc,EACd2B,KAAwB,EACxBmB,KAAa,EACbrC,QAAiB;IAEjB,OAAOoD,gBACLhB,sBAAsB9C,kBAAkBC,QAAQ8C,QAChDnB,OACAlB;AAEJ;AAEA;;;;CAIC,GACD,OAAO,SAASoD,gBACd7D,KAAc,EACd2B,KAAwB,EACxBlB,QAAiB;IAEjB,MAAMxB,OAAOyC,QAAQ1B,OAAO2B,OAAOlB;IACnC,OAAOqD,iBAAiB7E,QAAQ,KAAKA;AACvC;AAEA;;;CAGC,GACD,OAAO,SAAS6E,iBAAiB7E,IAAY;IAC3C,OAAOT,mBAAmB;QAAC;YAAEsC,IAAI;YAAQ7B;QAAK;KAAE,EAAEwC,MAAM,GAAG;AAC7D;AAEA,qGAAqG,GACrG,OAAO,SAASsC,iBAAiB/D,KAAc,EAAES,QAAiB;IAChE,MAAMxB,OAAO4E,gBAAgB7D,OAAO,SAASS,UAC1CR,OAAO,CAAC,sBAAsB,IAC9BM,IAAI;IACP,IAAI,CAACtB,MAAM,OAAO;IAClB,IAAI,WAAW8B,IAAI,CAAC9B,OAAO,OAAOA;IAClC,OAAO,CAAC,CAAC,EAAEA,KAAKgB,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC;AAC5C"}
|
|
@@ -53,12 +53,31 @@ export interface AiLayoutDesign {
|
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* How the first section of a page is drawn:
|
|
56
|
-
* - `cover`: a full-bleed photo with the words over it on a scrim
|
|
56
|
+
* - `cover`: a full-bleed photo with the words over it on a scrim, set at
|
|
57
|
+
* the start of the page;
|
|
58
|
+
* - `poster`: a full-bleed photo, taller, the words centered over it in
|
|
59
|
+
* display type (AGL-3985);
|
|
57
60
|
* - `split`: the words beside a tall photo, inside the page's measure;
|
|
61
|
+
* - `bleed`: the band in two halves, a photo filling one edge to edge and
|
|
62
|
+
* the words in the other (AGL-3985);
|
|
58
63
|
* - `editorial`: display type across the page, the lede and the buttons in
|
|
59
|
-
* a row under it, and a wide photo below
|
|
64
|
+
* a row under it, and a wide photo below;
|
|
65
|
+
* - `column`: a masthead — the words centered in a narrow column, a wide
|
|
66
|
+
* photo under them (AGL-3985);
|
|
67
|
+
* - `collage`: the words beside two photos, a tall one and a smaller one
|
|
68
|
+
* laid over its corner (AGL-3985);
|
|
69
|
+
* - `panel`: a full-bleed photo, undimmed, with the words on a card of the
|
|
70
|
+
* page's surface over its lower corner (AGL-3985);
|
|
71
|
+
* - `portrait`: a round photo over the words, all centered: a person's own
|
|
72
|
+
* page (AGL-3985).
|
|
60
73
|
*/
|
|
61
|
-
export type AiLayoutHeroVariant = 'cover' | 'split' | 'editorial';
|
|
74
|
+
export type AiLayoutHeroVariant = 'cover' | 'poster' | 'split' | 'bleed' | 'editorial' | 'column' | 'collage' | 'panel' | 'portrait';
|
|
75
|
+
/** Every hero arrangement, as {@link AiLayoutHeroVariant} lists them. */
|
|
76
|
+
export declare const AI_LAYOUT_HERO_VARIANTS: readonly AiLayoutHeroVariant[];
|
|
77
|
+
/** The heroes whose words take half the page or less, so their title is the page's title size, never display. */
|
|
78
|
+
export declare const AI_LAYOUT_HALF_HEROES: ReadonlySet<AiLayoutHeroVariant>;
|
|
79
|
+
/** The heroes that lay their words over a photo, which pins the section to the site's dark scheme. */
|
|
80
|
+
export declare const AI_LAYOUT_OVER_PHOTO_HEROES: ReadonlySet<AiLayoutHeroVariant>;
|
|
62
81
|
/**
|
|
63
82
|
* How a group of cards is drawn where no component draws it:
|
|
64
83
|
* - `cards`: the theme's cards (the compiler's first design);
|
|
@@ -99,15 +118,53 @@ export interface AiLayoutDesignChoices {
|
|
|
99
118
|
writing: 'grid' | 'ruled';
|
|
100
119
|
}
|
|
101
120
|
type Family = 'gallery' | 'editorial' | 'hospitality' | 'calm' | 'retail' | 'standard';
|
|
121
|
+
type GroupStyle = AiLayoutDesignChoices['group'];
|
|
122
|
+
/**
|
|
123
|
+
* A kind's LAYOUT SIGNATURE (AGL-3985): the arrangements its pages are drawn
|
|
124
|
+
* in, so a café, a law firm and a band never read as one page in three
|
|
125
|
+
* colors. Before it, six kinds shared one list of heroes and every kind drew
|
|
126
|
+
* its groups, its photo-led sections and its headings beside their items at
|
|
127
|
+
* the same odds; the guided start's 21 style previews, which the real
|
|
128
|
+
* compiler draws, read as one page — a header bar, a split or banded hero,
|
|
129
|
+
* one section.
|
|
130
|
+
*
|
|
131
|
+
* - `heroes`: the two first sections the kind opens with, each drawn at even
|
|
132
|
+
* odds (AGL-3899). The first is the one its style card shows. No two kinds
|
|
133
|
+
* share the same two.
|
|
134
|
+
* - `groups`: the group styles a section of items takes where its subject
|
|
135
|
+
* picks none.
|
|
136
|
+
* - `steps`: how its numbered groups read.
|
|
137
|
+
* - `features`: the chance a section of words takes a photo beside it — a
|
|
138
|
+
* café's or a salon's pages are photo-led, a law firm's or a writer's let
|
|
139
|
+
* the type carry them.
|
|
140
|
+
* - `coverClose`: the chance a closing call to action is drawn over a photo.
|
|
141
|
+
* - `splitHeads`: the chance a heading sits beside its items: an
|
|
142
|
+
* editorial, considered page.
|
|
143
|
+
* - `pictures`: how its work is laid out where it shows it as pictures.
|
|
144
|
+
*/
|
|
145
|
+
export interface AiLayoutKindSignature {
|
|
146
|
+
heroes: readonly [AiLayoutHeroVariant, AiLayoutHeroVariant];
|
|
147
|
+
groups: readonly GroupStyle[];
|
|
148
|
+
steps: readonly AiLayoutStepsVariant[];
|
|
149
|
+
features: number;
|
|
150
|
+
coverClose: number;
|
|
151
|
+
splitHeads: number;
|
|
152
|
+
pictures: readonly AiLayoutDesignChoices['pictures'][];
|
|
153
|
+
}
|
|
154
|
+
/** Each kind's signature, by its `AI_SITE_KINDS` id. */
|
|
155
|
+
export declare const AI_LAYOUT_KIND_SIGNATURES: Readonly<Record<string, AiLayoutKindSignature>>;
|
|
156
|
+
/** The signature of a kind; an unknown kind draws as a business site. */
|
|
157
|
+
export declare function aiLayoutKindSignature(kind: string): AiLayoutKindSignature;
|
|
102
158
|
/** The family a kind's pages are drawn in. */
|
|
103
159
|
export declare function aiLayoutFamily(kind: string): Family;
|
|
104
160
|
/**
|
|
105
161
|
* A site's choices, the same for every page of it, each drawn at even odds
|
|
106
|
-
* among the options
|
|
107
|
-
* are drawn apart
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* and every text section
|
|
162
|
+
* among the options its kind's signature lists (AGL-3899, AGL-3985), so two
|
|
163
|
+
* sites of one kind are drawn apart and two kinds never read as one page.
|
|
164
|
+
* Favoring each list's first option made the newest production sites one
|
|
165
|
+
* design: the kind's first hero won 55% before the pick, a business site
|
|
166
|
+
* opened split, groups were ruled two times in three and every text section
|
|
167
|
+
* took a photo.
|
|
111
168
|
*/
|
|
112
169
|
export declare function aiLayoutDesignChoices(design: AiLayoutDesign): AiLayoutDesignChoices;
|
|
113
170
|
/**
|