@aglyn/plugins-ai 1.0.0-beta.291 → 1.0.0-beta.293
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/components/ai-site-import-job.component.js +5 -4
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.js +3 -3
- package/src/lib/components/ai-site-import-start.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 +129 -29
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
- package/src/lib/components/ai-site-start-steps.component.js +42 -25
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.d.ts +17 -0
- package/src/lib/components/ai-site-style-previews.js +26 -95
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/declarations.server.js +22 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
- package/src/lib/jobs/ai-job-import-step.js +179 -49
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
- package/src/lib/jobs/ai-job-layout-language.js +156 -113
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +56 -17
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
- package/src/lib/jobs/ai-job-page-language.js +120 -66
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +27 -7
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
- package/src/lib/jobs/ai-job-site-datasets.js +36 -16
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +6 -2
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
- package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
- package/src/lib/jobs/ai-site-import-compile.js +1143 -0
- package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
- package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
- package/src/lib/jobs/ai-site-import-document.js +23 -489
- package/src/lib/jobs/ai-site-import-document.js.map +1 -1
- package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
- package/src/lib/jobs/ai-site-import-look.js +163 -52
- package/src/lib/jobs/ai-site-import-look.js.map +1 -1
- package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
- package/src/lib/jobs/ai-site-import-publish.js +19 -5
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.js +4 -2
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +10 -1
- package/src/lib/jobs/ai-site-publish.js +8 -3
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
- package/src/lib/layout-language/ai-layout-compiler.js +677 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
- package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
- package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
- package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
- package/src/lib/layout-language/ai-layout-frame.js +121 -29
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
- package/src/lib/layout-language/ai-layout-language.js +85 -120
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +11 -7
- package/src/lib/model/ai-credit-estimate.js +11 -7
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +307 -13
- package/src/lib/model/ai-site-import.js +227 -19
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +65 -1
- package/src/lib/model/ai-site-look.js +141 -23
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-photos.d.ts +2 -0
- package/src/lib/model/ai-site-photos.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +4 -3
- package/src/lib/model/ai-site-start-steps.js +11 -3
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +23 -2
- package/src/lib/model/ai-site-start.js +25 -3
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/providers/routing.d.ts +11 -0
- package/src/lib/providers/routing.js +23 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
- package/src/lib/runtime/ai-doctrine-validators.js +7 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +4 -0
- package/src/lib/runtime/ai-icon-library.js +4 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +5 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +2 -0
- package/src/lib/runtime/ai-palette.generated.js +19 -3
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +2 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
- package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +18 -1
- package/src/lib/site-import/site-import-crawl.js +70 -10
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-css.d.ts +110 -0
- package/src/lib/site-import/site-import-css.js +826 -0
- package/src/lib/site-import/site-import-css.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +116 -1
- package/src/lib/site-import/site-import-html.js +538 -46
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-layout.d.ts +79 -0
- package/src/lib/site-import/site-import-layout.js +1608 -0
- package/src/lib/site-import/site-import-layout.js.map +1 -0
- package/src/lib/site-import/site-import-nav.d.ts +40 -0
- package/src/lib/site-import/site-import-nav.js +437 -0
- package/src/lib/site-import/site-import-nav.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +8 -1
- package/src/lib/site-import/site-import-page.js +24 -2
- package/src/lib/site-import/site-import-page.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/site-import/site-import-review.d.ts +0 -40
- package/src/lib/site-import/site-import-review.js +0 -149
- package/src/lib/site-import/site-import-review.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-import.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\n/*==========================================\n * BRING MY SITE: THE WHOLE SITE, IN REVIEWED WAVES (AGL-3976, parent AGL-3973).\n *\n * The `import` job reads a website the workspace has PROVED it owns\n * (AGL-3974's claims) and brings it in as draft pages of a new site. This\n * module is the job's plan of record, pure, so the step, the doors, the\n * console and the specs read one set of rules:\n *\n * - **The inventory.** The crawl's pages, grouped: the MAIN pages (home, the\n * pages its menu links, every top-level page, and any page that belongs to\n * no collection), then each COLLECTION (an address prefix holding at least\n * `AI_SITE_IMPORT_COLLECTION_MIN` pages: `/blog/…`, `/news/…`, a dated\n * `/2024/05/…` archive).\n * - **Never past the plan.** Pages are kept in that order until the site's\n * room under its plan's page count is used; the rest are LEFT OUT, said so\n * with the reason, never dropped silently. So a plan's limit always leaves\n * out a collection's tail before it leaves out a main page.\n * - **Waves.** The kept pages are cut into waves of at most\n * `AI_SITE_IMPORT_WAVE_PAGES`. The first wave is the main pages, with the\n * site's header and footer built first; each later wave is one\n * collection's next slice, so a review is about one kind of page at a\n * time (\"Blog, 1 of 3\"). A wave runs, then waits for the person: nothing\n * starts the next one but them.\n * - **The price.** An imported page is an AI page (`AI_MEASURED_IMPORT_CREDITS`,\n * about 27 credits); a wave is quoted before it starts as its pages and,\n * for the first, its header and footer. Once the step says how each page\n * is built, a page copied as it is costs its listing and a page Aglyn AI\n * arranges its arrangement, and the quote names both (AGL-4083).\n * - **Home first (AGL-4083).** The read's verdict (`aiSiteImportReadVerdict`)\n * stops an import whose home page could not be read before anything is\n * planned, quoted or spent: a site seen without its home page is not the\n * site. The person is told why and checks again.\n *=========================================*/\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport type { SiteImportBlock, SiteImportForm } from '../site-import/site-import-html'\nimport { aiCreditRangeOrdered, aiCreditRangeText, AI_MEASURED_IMPORT_CREDITS, type AiCreditRange } from './ai-credit-estimate'\nimport type { AiJobItemLedger } from './ai-jobs.types'\n\n/** The job kind. */\nexport const AI_SITE_IMPORT_KIND = 'import' as const\n\n/** The input naming the proved domain the job brings in. */\nexport const AI_SITE_IMPORT_DOMAIN_INPUT = 'domain'\n\n/** The most pages one wave brings in. */\nexport const AI_SITE_IMPORT_WAVE_PAGES = 10\n\n/**\n * The most pages one import reads, whatever the plan allows: an unlimited\n * plan is still one crawl's worth, and the crawl's own time budget may stop\n * sooner (the inventory then says so).\n */\nexport const AI_SITE_IMPORT_MAX_PAGES = 200\n\n/** The fewest pages under one address prefix that make it a collection. */\nexport const AI_SITE_IMPORT_COLLECTION_MIN = 3\n\n/** The most photos brought in from one page. */\nexport const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8\n\n/**\n * The most characters of one page's text a wave keeps for the page step's\n * prompt, which reads no more than this; every word is kept on the read's\n * `blocks` (AGL-4083).\n */\nexport const AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS = 6_000\n\n/** The most sections one imported page is planned with: one for every second-level heading (AGL-4083). */\nexport const AI_SITE_IMPORT_MAX_SECTIONS = 24\n\n/** The most blocks of one page a wave's read keeps. */\nexport const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2_000\n\n/** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */\nexport const AI_SITE_IMPORT_READ_MAX_CHARS = 120_000\n\n/**\n * The most characters of blocks the reads of one import hold at once: they\n * ride the job document, which Firestore caps at 1 MiB, beside the\n * inventory and the ledger. A wave's later page past it keeps what fits and\n * says it was cut.\n */\nexport const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400_000\n\n/**\n * A page with more words of its own than this is copied as it is, never\n * arranged (AGL-4083): a policy, an article, a long story.\n */\nexport const AI_SITE_IMPORT_PROSE_WORDS = 900\n\n/** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */\nexport const AI_SITE_IMPORT_BLOCKED_STATUSES: ReadonlySet<number> = new Set([401, 403, 429])\n\n/** The most characters a section's name, a page's title or a label keeps. */\nexport const AI_SITE_IMPORT_LABEL_MAX_CHARS = 60\n\n/** A page with fewer words than this and no photo has nothing to bring in. */\nexport const AI_SITE_IMPORT_MIN_WORDS = 5\n\n/** The item ledger slot of the site's header and footer. */\nexport const AI_SITE_IMPORT_FRAME_SLOT = 'frame'\n\n/** The ceiling one imported page reserves: its answer pass and its listing pass, at 50 each. */\nexport const AI_SITE_IMPORT_PAGE_CEILING_CREDITS = 100\n\n/** The ceiling a page copied as it is reserves: its listing pass alone. */\nexport const AI_SITE_IMPORT_COPIED_CEILING_CREDITS = 50\n\n/** The ceiling the header and footer reserve: one answer at 50. */\nexport const AI_SITE_IMPORT_FRAME_CEILING_CREDITS = 50\n\n/** The most left-out pages listed by address; the rest are counted. */\nexport const AI_SITE_IMPORT_LEFT_OUT_LISTED = 60\n\n/** A page the crawl read, as the inventory keeps it: no text, only what plans it. */\nexport interface AiSiteImportListedPage {\n url: string\n path: string\n title: string\n foundBy: 'home' | 'sitemap' | 'link'\n words: number\n images: number\n}\n\nexport type AiSiteImportGroupKind = 'main' | 'collection'\n\nexport interface AiSiteImportGroup {\n /** `main`, or `c:<prefix>` for a collection. */\n id: string\n label: string\n kind: AiSiteImportGroupKind\n /** Pages of the group the crawl read, kept or not. */\n pages: number\n}\n\n/** Why a page the site has is not brought in. */\nexport type AiSiteImportLeftOutReason =\n /** Past the site's room under its plan's page count. */\n | 'plan-limit'\n /** robots.txt keeps AglynBot off it. */\n | 'robots'\n /** A sign-in page: refused outright. */\n | 'login-form'\n /** No text and no photo: nothing to bring in. */\n | 'empty'\n /** It could not be read: an error, no answer, or not a web page. */\n | 'unreadable'\n /** The site's bot protection or firewall turned AglynBot away (401, 403 or 429). */\n | 'blocked'\n /** Over the size one page may be. */\n | 'too-large'\n /** It sends visitors to another website. */\n | 'off-site'\n /** The read stopped (its clock, its bytes, its page count) before it got to it (AGL-4083). */\n | 'not-reached'\n\nexport const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[] = [\n 'plan-limit',\n 'robots',\n 'login-form',\n 'empty',\n 'unreadable',\n 'blocked',\n 'too-large',\n 'off-site',\n 'not-reached',\n]\n\n/** The reasons that mean the page was there and AglynBot could not read it: the \"could not be read\" of what was found. */\nexport const AI_SITE_IMPORT_UNREAD_REASONS: readonly AiSiteImportLeftOutReason[] = ['unreadable', 'blocked', 'too-large', 'not-reached']\n\nexport interface AiSiteImportLeftOut {\n url: string\n path: string\n title: string\n reason: AiSiteImportLeftOutReason\n}\n\n/** A photo brought into the site's media library, as a page places it. */\nexport interface AiSiteImportPhoto {\n mediaId: string\n src: string\n width?: number\n height?: number\n alt?: string\n /** The address on the old site it was read from, where a page that is copied places it (AGL-4084). */\n from?: string\n}\n\n/** What a wave's read found on one page, for its model answer. Never on the wire. */\nexport interface AiSiteImportPageRead {\n at: string\n title: string\n /** The page's text in order, cut to `AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS`; fenced only when a prompt reads it. */\n text: string\n /** The page's sections, from its headings. */\n sections: string[]\n /** Its photos' addresses, at most `AI_SITE_IMPORT_IMAGES_PER_PAGE`. */\n images: Array<{ url: string; alt: string }>\n /**\n * Every block of the page's main region, in order (AGL-4083): its words as\n * written, its headings, lists, links and pictures, within\n * `AI_SITE_IMPORT_READ_MAX_BLOCKS` and `AI_SITE_IMPORT_READ_MAX_CHARS`.\n * Untrusted data: fenced wherever a prompt reads it. Emptied once the page\n * is in; absent on a read made before it was kept.\n */\n blocks?: SiteImportBlock[]\n /** Set when `blocks` had to stop short of the page, at its budget or the wave's. */\n cut?: boolean\n /** The words of the page's main region, as the old page has them. */\n words?: number\n /** The forms the page asks visitors to fill in, by their fields. */\n forms?: SiteImportForm[]\n}\n\n/** How a page is built from its old page, as the step chose: its words copied as they are, or arranged by Aglyn AI. */\nexport type AiSiteImportBuild = 'copied' | 'arranged'\n\nexport interface AiSiteImportPage {\n /** The item ledger slot: `p0`, `p1`, … */\n slot: string\n url: string\n path: string\n title: string\n group: string\n wave: number\n /** The id the page's draft is written under, minted with the inventory. */\n draftId: string\n /** The page's address on the new site. */\n slug: string\n words: number\n images: number\n /** Filled by the wave's read; the text is cleared once the page is in. */\n read?: AiSiteImportPageRead | null\n /** Set when the wave's read found the page cannot be brought in after all. */\n refused?: AiSiteImportLeftOutReason | null\n /** How the step builds the page (`aiSiteImportPageBuild`); absent until it chooses, and the page is priced as an AI page. */\n build?: AiSiteImportBuild | null\n /** What came through once the page is in: its words on the new page. */\n brought?: { words: number } | null\n /** Filled once the page's photos are in the library. */\n photos?: {\n brought: number\n /** Already in the library from an earlier page or import: the same file, never a second copy. */\n reused: number\n skipped: number\n assets: AiSiteImportPhoto[]\n } | null\n}\n\nexport type AiSiteImportWaveStatus = 'waiting' | 'running' | 'review' | 'done'\n\nexport interface AiSiteImportWave {\n index: number\n label: string\n /** The group the wave brings in; the first wave's is `main`. */\n group: string\n slots: string[]\n /** Whether the wave builds the site's header and footer first. */\n frame: boolean\n status: AiSiteImportWaveStatus\n startedAt: string | null\n finishedAt: string | null\n}\n\nexport interface AiSiteImportState {\n domain: string\n /** The site's name, as Create site was given it: the header's name. */\n siteName: string\n ownership: { method: string | null; provenAt: number | null; detail: string | null }\n readAt: string\n /** The plan's page count; `null` when it has none. */\n pageLimit: number | null\n /** Pages the site already had when its import read the old site. */\n existingPages: number\n /** Pages the crawl read. */\n found: number\n /** Why the crawl stopped short of the whole site, or `null` when it did not. */\n stoppedBy: string | null\n groups: AiSiteImportGroup[]\n pages: AiSiteImportPage[]\n waves: AiSiteImportWave[]\n /** At most `AI_SITE_IMPORT_LEFT_OUT_LISTED`, by address. */\n leftOut: AiSiteImportLeftOut[]\n /** Every page left out, by reason, listed or not. */\n leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>>\n /** The header and footer's draft id, built in the first wave; `null` when there are no main pages. */\n frame: { draftId: string } | null\n /** The Trust & Safety row this import is logged under. */\n reviewId: string | null\n /**\n * \"Start every remaining wave\" (AGL-4083): each wave closes and the next\n * starts with no review between them; the last wave's close is the stop.\n * Off unless the person chose it.\n */\n autoContinue?: boolean\n /**\n * The wave \"Start every remaining wave\" was chosen at (AGL-4084): the one\n * start the person made, which the day's limit counts; the waves it starts\n * after it are the same start.\n */\n autoContinueFrom?: number | null\n /** What the read found of the site as a whole; a stopping verdict plans nothing. */\n verdict?: AiSiteImportReadVerdict | null\n /** What the home page says of the site as a whole (AGL-4084): its look, its header and footer, its listing. */\n home?: AiSiteImportHome | null\n /** The look pass, once it ran in the first wave (AGL-4084). */\n look?: AiSiteImportLook | null\n}\n\n/**\n * What the home page says of the site as a whole (AGL-4084), kept from the\n * read: the site's listing in its own words, its header's brand as the\n * source draws it, and the words its header and footer carry. Untrusted\n * data from the owner's site, fenced wherever a prompt reads it.\n */\nexport interface AiSiteImportHome {\n url: string\n title: string\n description: string\n ogImage: string | null\n siteName: string\n brand: {\n kind: 'logo-only' | 'logo-and-name' | 'name-only'\n /** The name as the header prints it, where it prints one. */\n text: string\n logo: { url: string; alt: string; type: 'svg' | 'raster' | null } | null\n }\n facts: {\n /** The header's and footer's lines, as written, in order. */\n lines: string[]\n emails: string[]\n phones: string[]\n social: string[]\n address: string\n }\n}\n\n/** What the look pass did (AGL-4084): no model is asked. */\nexport interface AiSiteImportLook {\n at: string\n /** The theme: drawn from the source (`applied`), the owner's kept (`kept`), or the source's look could not be read. */\n theme: 'applied' | 'kept' | 'unread'\n /** The source's colors it was drawn from, the strongest first. */\n colors: string[]\n /** The source's logo, as the library holds it, for the header's brand. */\n logo: AiSiteImportPhoto | null\n /** Whether the site's listing was taken from the home page's own words. */\n seo: boolean\n /** The look's seed, which every page of the import is drawn with. */\n seed: number | null\n /** The look's header arrangement, band rhythm and motion, which the header, the footer and every page are drawn in. */\n style?: { headerAlign?: string; rhythm?: string; motion?: string } | null\n}\n\n/*------------------------------------------\n * The read's verdict: Home first (AGL-4083)\n *-----------------------------------------*/\n\n/**\n * - `home-blocked`: bot protection or a firewall turned AglynBot away from\n * the home page (401, 403 or 429).\n * - `home-unreadable`: the home page could not be read for any other reason,\n * or read as an empty shell (a site that needs a browser to show its words).\n * - `mostly-blocked`: the home page came in, but more than half the site's\n * addresses were turned away; the import goes on, and says so.\n * - `ok`: anything else.\n */\nexport type AiSiteImportReadVerdictKind = 'ok' | 'home-blocked' | 'home-unreadable' | 'mostly-blocked'\n\n/** Why the home page did not come in. */\nexport type AiSiteImportHomeProblem =\n | 'blocked'\n | 'http-error'\n | 'unreachable'\n | 'not-html'\n | 'too-large'\n | 'off-site'\n | 'robots'\n | 'login-form'\n | 'not-reached'\n | 'empty'\n\nexport interface AiSiteImportReadVerdict {\n kind: AiSiteImportReadVerdictKind\n /** Addresses turned away by bot protection or a firewall, the home page among them. */\n blocked: number\n /** The site's addresses the read knew of: the sitemaps', or everything it asked for. */\n total: number\n /** What the home page answered, when it answered with an error. */\n homeStatus: number | null\n homeProblem: AiSiteImportHomeProblem | null\n /** Whose edge turned the home page away, when it said. */\n edge: 'vercel' | 'cloudflare' | null\n}\n\n/** A crawl as the verdict reads it; the crawler's `SiteImportInventory` is one. */\nexport interface AiSiteImportReadInput {\n pages: ReadonlyArray<{\n path: string\n foundBy: string\n blocks: ReadonlyArray<{ kind: string; text?: string; inline?: boolean }>\n images: readonly unknown[]\n }>\n skipped: ReadonlyArray<{ url: string; reason: string; status?: number; edge?: string | null }>\n sitemapUrls: number\n}\n\nconst pathOfUrl = (url: string): string => {\n try {\n return new URL(url).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n return ''\n }\n}\n\nconst wordsOf = (text: string) => (text.match(/[\\p{L}\\p{N}]+/gu) ?? []).length\n\n/** One verdict from what became of the home page and how much of the site was turned away. */\nfunction readVerdictOf(\n home: { problem: AiSiteImportHomeProblem; status: number | null; edge: string | null } | null,\n blocked: number,\n total: number,\n): AiSiteImportReadVerdict {\n const edge: AiSiteImportReadVerdict['edge'] = home?.edge === 'vercel' ? 'vercel' : home?.edge === 'cloudflare' ? 'cloudflare' : null\n const base = { blocked, total, homeStatus: home?.status ?? null, homeProblem: home?.problem ?? null, edge }\n if (home) return { kind: home.problem === 'blocked' ? 'home-blocked' : 'home-unreadable', ...base }\n return { kind: total > 0 && blocked * 2 > total ? 'mostly-blocked' : 'ok', ...base }\n}\n\n/** What a crawler skip of the home page means for it. */\nfunction homeProblemOfSkip(reason: string, status: number | undefined): AiSiteImportHomeProblem {\n switch (reason) {\n case 'http-error':\n return status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(status) ? 'blocked' : 'http-error'\n case 'off-origin-redirect':\n return 'off-site'\n case 'not-html':\n case 'too-large':\n case 'robots':\n case 'login-form':\n case 'not-reached':\n return reason\n default:\n return 'unreachable'\n }\n}\n\n/**\n * The read's verdict (AGL-4083), pure over the crawl: whether the home page\n * came in, and how much of the site bot protection turned away. An import\n * whose verdict stops (`aiSiteImportReadStops`) plans no wave, quotes\n * nothing and spends nothing: the person is told why and checks again.\n */\nexport function aiSiteImportReadVerdict(crawled: AiSiteImportReadInput): AiSiteImportReadVerdict {\n const blocked = crawled.skipped.filter(\n (entry) => entry.reason === 'http-error' && entry.status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(entry.status),\n ).length\n const total = Math.max(crawled.sitemapUrls, crawled.pages.length + crawled.skipped.length)\n const page = crawled.pages.find((entry) => entry.foundBy === 'home') ?? crawled.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const words = page.blocks.reduce((sum, block) => sum + (block.kind === 'image' || block.inline ? 0 : wordsOf(block.text ?? '')), 0)\n const empty = words < AI_SITE_IMPORT_MIN_WORDS && page.images.length === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = crawled.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n return readVerdictOf(\n skip\n ? { problem: homeProblemOfSkip(skip.reason, skip.status), status: skip.status ?? null, edge: skip.edge ?? null }\n : { problem: 'not-reached', status: null, edge: null },\n blocked,\n total,\n )\n}\n\n/** Whether a verdict stops the import before anything is planned: its home page is not in. */\nexport function aiSiteImportReadStops(verdict: AiSiteImportReadVerdict | null | undefined): boolean {\n return verdict?.kind === 'home-blocked' || verdict?.kind === 'home-unreadable'\n}\n\n/** The review an import that stopped at its read waits under: `blocked` or `unreadable`; `null` when it goes on. */\nexport function aiSiteImportReadVerdictReason(verdict: AiSiteImportReadVerdict | null | undefined): 'blocked' | 'unreadable' | null {\n if (verdict?.kind === 'home-blocked') return 'blocked'\n if (verdict?.kind === 'home-unreadable') return 'unreadable'\n return null\n}\n\n/** Where a site owner lets AglynBot through, on the edges that name themselves. */\nexport const AI_SITE_IMPORT_EDGE_HINTS: Readonly<Record<'vercel' | 'cloudflare', string>> = {\n vercel: 'Your site answers through Vercel: in its Firewall, under Rules, add a bypass rule for the user agent AglynBot.',\n cloudflare:\n 'Your site answers through Cloudflare: under Security, WAF, add a custom rule that skips the checks when the user agent contains AglynBot.',\n}\n\nconst NOTHING_SPENT = 'Nothing was brought in and no AI credits were used.'\n\n/** Why the home page could not be read, inside the sentence that says so. */\nfunction homeProblemText(verdict: AiSiteImportReadVerdict): string {\n switch (verdict.homeProblem) {\n case 'http-error':\n return verdict.homeStatus ? `it answered with an error, ${verdict.homeStatus}` : 'it answered with an error'\n case 'not-html':\n return 'it did not answer with a web page'\n case 'too-large':\n return 'it is larger than a page can be to bring in'\n case 'off-site':\n return 'it sends visitors to another website'\n case 'not-reached':\n return 'the read ran out of time before it'\n default:\n return 'it did not answer'\n }\n}\n\n/**\n * What the person is told about the read (AGL-4083), in console words: why\n * nothing was brought in and what to do, or that most of the site was turned\n * away. `null` when there is nothing to say.\n */\nexport function aiSiteImportReadVerdictMessage(verdict: AiSiteImportReadVerdict | null | undefined, domain: string): string | null {\n if (!verdict) return null\n if (verdict.kind === 'home-blocked') {\n const others = Math.max(0, verdict.blocked - 1)\n const more = others ? `, and ${others.toLocaleString('en-US')} other ${others === 1 ? 'page' : 'pages'}` : ''\n const lead = `Your site’s bot protection or firewall turned AglynBot away before it could read your home page${more}. ${NOTHING_SPENT} Allow AglynBot in your site’s bot or firewall settings, then choose Check again.`\n return verdict.edge ? `${lead} ${AI_SITE_IMPORT_EDGE_HINTS[verdict.edge]}` : lead\n }\n if (verdict.kind === 'home-unreadable') {\n switch (verdict.homeProblem) {\n case 'robots':\n return `Your site’s robots.txt asks AglynBot not to read your home page. ${NOTHING_SPENT} Allow AglynBot in your robots.txt, then choose Check again.`\n case 'login-form':\n return `Your home page asks for a password, and pages that do are never brought in. ${NOTHING_SPENT} Open your home page to visitors, then choose Check again.`\n case 'empty':\n return `No text could be read from your home page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet. ${NOTHING_SPENT}`\n default:\n return `Your home page could not be read (${homeProblemText(verdict)}). ${NOTHING_SPENT} Check that ${domain} is up and answers at https://${domain}/, then choose Check again.`\n }\n }\n if (verdict.kind === 'mostly-blocked') {\n return `${verdict.blocked.toLocaleString('en-US')} of ${verdict.total.toLocaleString('en-US')} pages were turned away by your site’s bot protection; see Not brought in.`\n }\n return null\n}\n\n/** The verdict an inventory reads from its own pages and skips, when the step did not hand it the crawl's. */\nfunction inventoryVerdict(input: Pick<AiSiteImportInventoryInput, 'pages' | 'skipped' | 'skippedOverflow'>): AiSiteImportReadVerdict {\n const blocked = input.skipped.filter((entry) => entry.reason === 'blocked').length\n const total = input.pages.length + input.skipped.length + Math.max(0, input.skippedOverflow ?? 0)\n const page = input.pages.find((entry) => entry.foundBy === 'home') ?? input.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const empty = page.words < AI_SITE_IMPORT_MIN_WORDS && page.images === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = input.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n const problem: AiSiteImportHomeProblem = !skip\n ? 'not-reached'\n : skip.reason === 'blocked'\n ? 'blocked'\n : skip.reason === 'robots' || skip.reason === 'login-form' || skip.reason === 'too-large' || skip.reason === 'off-site' || skip.reason === 'not-reached' || skip.reason === 'empty'\n ? skip.reason\n : 'unreachable'\n return readVerdictOf({ problem, status: null, edge: null }, blocked, total)\n}\n\n/*------------------------------------------\n * Grouping and waves\n *-----------------------------------------*/\n\nconst clipLabel = (text: string) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > AI_SITE_IMPORT_LABEL_MAX_CHARS\n ? `${line.slice(0, AI_SITE_IMPORT_LABEL_MAX_CHARS - 1).trimEnd()}…`\n : line\n}\n\nconst segmentsOf = (path: string) => String(path ?? '').split('/').filter(Boolean)\n\n/** A collection's prefix for a page address: its first segment, a dated archive's `posts`; `null` for a top-level page. */\nexport function aiSiteImportPrefix(path: string): string | null {\n const segments = segmentsOf(path)\n if (segments.length < 2) return null\n const first = segments[0].toLowerCase()\n return /^\\d{4}$/.test(first) ? 'posts' : first\n}\n\n/** \"news-and-events\" → \"News and events\". */\nexport function aiSiteImportPrefixLabel(prefix: string): string {\n const words = decodeURIComponent(prefix).replace(/[-_+]+/g, ' ').trim()\n return words ? `${words.charAt(0).toUpperCase()}${words.slice(1)}` : 'Pages'\n}\n\n/**\n * The inventory's groups, in the order they are brought in: the main pages,\n * then each collection — one the home page's menu links first, in menu order,\n * then the larger before the smaller.\n */\nexport function aiSiteImportGroups(\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[] = [],\n): { groups: AiSiteImportGroup[]; groupOf: (page: AiSiteImportListedPage) => string } {\n const counts = new Map<string, number>()\n for (const page of pages) {\n const prefix = aiSiteImportPrefix(page.path)\n if (prefix) counts.set(prefix, (counts.get(prefix) ?? 0) + 1)\n }\n const menuPaths = new Set(menu.map((path) => path.replace(/\\/+$/, '') || '/'))\n const collections = new Set([...counts].filter(([, count]) => count >= AI_SITE_IMPORT_COLLECTION_MIN).map(([prefix]) => prefix))\n const groupOf = (page: AiSiteImportListedPage) => {\n const path = page.path.replace(/\\/+$/, '') || '/'\n if (page.foundBy === 'home' || menuPaths.has(path)) return 'main'\n const prefix = aiSiteImportPrefix(page.path)\n return prefix && collections.has(prefix) ? `c:${prefix}` : 'main'\n }\n const menuRank = (prefix: string) => {\n const index = menu.findIndex((path) => {\n const first = segmentsOf(path)[0]?.toLowerCase()\n return first === prefix\n })\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n const sized = new Map<string, number>()\n for (const page of pages) sized.set(groupOf(page), (sized.get(groupOf(page)) ?? 0) + 1)\n const ordered = [...collections]\n .filter((prefix) => (sized.get(`c:${prefix}`) ?? 0) > 0)\n .sort((a, b) => menuRank(a) - menuRank(b) || (sized.get(`c:${b}`) ?? 0) - (sized.get(`c:${a}`) ?? 0) || a.localeCompare(b))\n const groups: AiSiteImportGroup[] = []\n if ((sized.get('main') ?? 0) > 0) groups.push({ id: 'main', label: 'Main pages', kind: 'main', pages: sized.get('main') ?? 0 })\n for (const prefix of ordered) {\n groups.push({ id: `c:${prefix}`, label: aiSiteImportPrefixLabel(prefix), kind: 'collection', pages: sized.get(`c:${prefix}`) ?? 0 })\n }\n return { groups, groupOf }\n}\n\n/**\n * The main pages in the order a visitor meets them: home, then the menu's\n * order, then the rest as the crawl read them. A collection keeps the crawl's\n * order, which is its sitemap's.\n */\nfunction orderedWithin(\n group: string,\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[],\n): AiSiteImportListedPage[] {\n if (group !== 'main') return [...pages]\n const rank = (page: AiSiteImportListedPage) => {\n if (page.foundBy === 'home' || segmentsOf(page.path).length === 0) return -1\n const path = page.path.replace(/\\/+$/, '') || '/'\n const index = menu.findIndex((entry) => (entry.replace(/\\/+$/, '') || '/') === path)\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n return pages\n .map((page, order) => ({ page, order }))\n .sort((a, b) => rank(a.page) - rank(b.page) || a.order - b.order)\n .map(({ page }) => page)\n}\n\n/**\n * The address a page takes on the new site: one segment, as every Aglyn page\n * has, from the last segment of its old address; home is the root. Two pages\n * that would take the same address are told apart by their collection, then\n * by a number. The draft writer still makes it unique on the site itself.\n */\nexport function aiSiteImportSlug(path: string, taken: Set<string>): string {\n const segments = segmentsOf(path)\n if (!segments.length) {\n if (!taken.has('/')) {\n taken.add('/')\n return '/'\n }\n }\n const clean = (value: string) =>\n decodeURIComponent(value)\n .toLowerCase()\n .replace(/\\.(?:html?|php|aspx?)$/, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const last = clean(segments[segments.length - 1] ?? '') || 'page'\n const prefixed = segments.length > 1 ? clean(`${segments[0]}-${segments[segments.length - 1]}`) : last\n for (const candidate of [last, prefixed]) {\n if (candidate && !taken.has(candidate)) {\n taken.add(candidate)\n return candidate\n }\n }\n let attempt = 2\n while (taken.has(`${last}-${attempt}`)) attempt += 1\n const slug = `${last}-${attempt}`\n taken.add(slug)\n return slug\n}\n\nexport interface AiSiteImportInventoryInput {\n domain: string\n siteName: string\n ownership: AiSiteImportState['ownership']\n readAt: string\n pages: readonly AiSiteImportListedPage[]\n /** Addresses the home page's menu links, in its order. */\n menu: readonly string[]\n /** What the crawl could not read, by reason. */\n skipped: ReadonlyArray<{ url: string; reason: AiSiteImportLeftOutReason }>\n /** Skips the crawl only counted. */\n skippedOverflow?: number\n stoppedBy: string | null\n /**\n * The read's verdict from the crawl itself (`aiSiteImportReadVerdict`), with\n * the status and edge the home page answered with; read from `pages` and\n * `skipped` when absent.\n */\n verdict?: AiSiteImportReadVerdict | null\n /** The plan's page count; `null` for none. */\n pageLimit: number | null\n existingPages: number\n /** Mints one draft id; the inventory names every page's draft before any is written. */\n mintId: () => string\n reviewId?: string | null\n wavePages?: number\n}\n\n/**\n * The site's inventory cut into waves, every page left out said with its\n * reason. Pure: the step reads the site, this decides what comes in.\n */\nexport function aiSiteImportInventory(input: AiSiteImportInventoryInput): AiSiteImportState {\n const wavePages = Math.max(1, Math.floor(input.wavePages ?? AI_SITE_IMPORT_WAVE_PAGES))\n const limit = input.pageLimit === null || !Number.isFinite(input.pageLimit) ? null : Math.max(0, Math.floor(input.pageLimit))\n const leftOut: AiSiteImportLeftOut[] = []\n const leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>> = {}\n const leave = (entry: AiSiteImportLeftOut) => {\n leftOutCount[entry.reason] = (leftOutCount[entry.reason] ?? 0) + 1\n if (leftOut.length < AI_SITE_IMPORT_LEFT_OUT_LISTED) leftOut.push(entry)\n }\n // The read asks for a wave's worth past the plan's room, so a page its page\n // budget never reached is past the plan; any other stop is not the plan's.\n const unreached: AiSiteImportLeftOutReason = input.stoppedBy === 'page-budget' && limit !== null ? 'plan-limit' : 'not-reached'\n for (const skipped of input.skipped) {\n let path = '/'\n try {\n path = new URL(skipped.url).pathname\n } catch {\n // A malformed address keeps the root as its path.\n }\n leave({ url: skipped.url, path, title: '', reason: skipped.reason === 'not-reached' ? unreached : skipped.reason })\n }\n if (input.skippedOverflow) leftOutCount['unreadable'] = (leftOutCount['unreadable'] ?? 0) + input.skippedOverflow\n\n // Home first: a site read without its home page plans nothing at all.\n const verdict = input.verdict ?? inventoryVerdict(input)\n if (aiSiteImportReadStops(verdict)) {\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups: [],\n pages: [],\n waves: [],\n leftOut,\n leftOutCount,\n frame: null,\n reviewId: input.reviewId ?? null,\n verdict,\n }\n }\n\n const readable = input.pages.filter((page) => {\n if (page.words >= AI_SITE_IMPORT_MIN_WORDS || page.images > 0) return true\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'empty' })\n return false\n })\n const { groups, groupOf } = aiSiteImportGroups(readable, input.menu)\n let room = limit === null ? Number.POSITIVE_INFINITY : Math.max(0, limit - Math.max(0, input.existingPages))\n const pages: AiSiteImportPage[] = []\n const waves: AiSiteImportWave[] = []\n const taken = new Set<string>()\n for (const group of groups) {\n const members = orderedWithin(group.id, readable.filter((page) => groupOf(page) === group.id), input.menu)\n const kept: AiSiteImportListedPage[] = []\n for (const page of members) {\n if (room > 0) {\n kept.push(page)\n room -= 1\n } else {\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'plan-limit' })\n }\n }\n const parts = Math.ceil(kept.length / wavePages)\n for (let part = 0; part < parts; part += 1) {\n const index = waves.length\n const slice = kept.slice(part * wavePages, (part + 1) * wavePages)\n const slots: string[] = []\n for (const page of slice) {\n const slot = `p${pages.length}`\n slots.push(slot)\n pages.push({\n slot,\n url: page.url,\n path: page.path,\n title: clipLabel(page.title) || aiSiteImportPrefixLabel(segmentsOf(page.path).pop() ?? 'Home'),\n group: group.id,\n wave: index,\n draftId: input.mintId(),\n slug: aiSiteImportSlug(page.path, taken),\n words: page.words,\n images: page.images,\n })\n }\n waves.push({\n index,\n label: parts > 1 ? `${group.label}, ${part + 1} of ${parts}` : group.label,\n group: group.id,\n slots,\n frame: index === 0 && group.kind === 'main',\n status: 'waiting',\n startedAt: null,\n finishedAt: null,\n })\n }\n }\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups,\n pages,\n waves,\n leftOut,\n leftOutCount,\n frame: waves[0]?.frame ? { draftId: input.mintId() } : null,\n reviewId: input.reviewId ?? null,\n verdict,\n }\n}\n\n/** The item ledger an inventory starts: the header and footer, then every page, all pending. */\nexport function aiSiteImportLedger(state: AiSiteImportState): AiJobItemLedger[] {\n const row = (slot: string, op: string, label: string): AiJobItemLedger => ({\n slot,\n op,\n label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n creditsRefunded: 0,\n outputs: [],\n })\n return [\n ...(state.frame ? [row(AI_SITE_IMPORT_FRAME_SLOT, 'layout', 'Header and footer')] : []),\n ...state.pages.map((page) => row(page.slot, 'page', page.title)),\n ]\n}\n\n/*------------------------------------------\n * What runs next\n *-----------------------------------------*/\n\nexport type AiSiteImportWork =\n | { kind: 'inventory' }\n | { kind: 'start-wave'; wave: AiSiteImportWave }\n /** Pages of the running wave its read did not reach before its clock ran out. */\n | { kind: 'read'; wave: AiSiteImportWave; pages: AiSiteImportPage[] }\n | { kind: 'frame'; wave: AiSiteImportWave }\n | { kind: 'images'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'page'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'close-wave'; wave: AiSiteImportWave }\n | { kind: 'done' }\n\nconst OPEN = new Set(['pending', 'running'])\n\n/** Whether an item is still to be built. */\nexport function aiSiteImportItemOpen(items: readonly AiJobItemLedger[] | null | undefined, slot: string): boolean {\n const row = (items ?? []).find((entry) => entry.slot === slot)\n return Boolean(row && OPEN.has(row.status))\n}\n\n/** The wave running now, else `null`. */\nexport function aiSiteImportRunningWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'running') ?? null\n}\n\n/** The next wave a person may start, else `null`. */\nexport function aiSiteImportNextWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n if (!state || aiSiteImportRunningWave(state) || aiSiteImportReadStops(state.verdict)) return null\n return state.waves.find((wave) => wave.status === 'waiting') ?? null\n}\n\n/** The wave a person is reviewing, else `null`. */\nexport function aiSiteImportReviewWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'review') ?? null\n}\n\n/** What the next pass of the step does. */\nexport function aiSiteImportNextWork(\n state: AiSiteImportState | null | undefined,\n items: readonly AiJobItemLedger[] | null | undefined,\n): AiSiteImportWork {\n // A read that stopped at its home page reads again: the person's Check again.\n if (!state || aiSiteImportReadStops(state.verdict)) return { kind: 'inventory' }\n const running = aiSiteImportRunningWave(state)\n if (!running) {\n const next = aiSiteImportNextWave(state)\n return next ? { kind: 'start-wave', wave: next } : { kind: 'done' }\n }\n // A page the wave's read has not reached yet is read before anything is built.\n const unread = running.slots\n .map((slot) => state.pages.find((page) => page.slot === slot))\n .filter((page): page is AiSiteImportPage => !!page && !page.read && !page.refused && aiSiteImportItemOpen(items, page.slot))\n if (unread.length) return { kind: 'read', wave: running, pages: unread }\n if (running.frame && state.frame && aiSiteImportItemOpen(items, AI_SITE_IMPORT_FRAME_SLOT)) {\n return { kind: 'frame', wave: running }\n }\n for (const slot of running.slots) {\n if (!aiSiteImportItemOpen(items, slot)) continue\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page) continue\n return page.photos ? { kind: 'page', wave: running, page } : { kind: 'images', wave: running, page }\n }\n return { kind: 'close-wave', wave: running }\n}\n\n/*------------------------------------------\n * The state, one pass at a time\n *-----------------------------------------*/\n\nconst withPage = (state: AiSiteImportState, slot: string, patch: Partial<AiSiteImportPage>): AiSiteImportState => ({\n ...state,\n pages: state.pages.map((page) => (page.slot === slot ? { ...page, ...patch } : page)),\n})\n\nconst withWave = (state: AiSiteImportState, index: number, patch: Partial<AiSiteImportWave>): AiSiteImportState => ({\n ...state,\n waves: state.waves.map((wave) => (wave.index === index ? { ...wave, ...patch } : wave)),\n})\n\n/** A wave started: running, with what its read found on each page. */\nexport function aiSiteImportWaveStarted(\n state: AiSiteImportState,\n index: number,\n at: string,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n // The wave reviewed before this one is done: starting the next is the person's say-so.\n const next = withWave(\n { ...state, waves: state.waves.map((wave) => (wave.status === 'review' ? { ...wave, status: 'done' as const } : wave)) },\n index,\n { status: 'running', startedAt: at, finishedAt: null },\n )\n return aiSiteImportPagesRead(next, reads)\n}\n\n/** How many characters a block holds, as the job document stores it. */\nexport function aiSiteImportBlockChars(block: SiteImportBlock): number {\n if (block.kind === 'image') return block.url.length + block.alt.length\n return block.text.length + (block.kind === 'link' ? block.href.length : 0)\n}\n\n/** A page's blocks in order, as many as fit `maxChars` and `maxBlocks`, and whether any had to go. */\nexport function aiSiteImportCapBlocks(\n blocks: readonly SiteImportBlock[],\n maxChars: number,\n maxBlocks: number = AI_SITE_IMPORT_READ_MAX_BLOCKS,\n): { blocks: SiteImportBlock[]; chars: number; cut: boolean } {\n const kept: SiteImportBlock[] = []\n let chars = 0\n for (const block of blocks) {\n const size = aiSiteImportBlockChars(block)\n if (kept.length >= maxBlocks || chars + size > maxChars) return { blocks: kept, chars, cut: true }\n kept.push(block)\n chars += size\n }\n return { blocks: kept, chars, cut: false }\n}\n\nconst readChars = (read: AiSiteImportPageRead | null | undefined) =>\n (read?.blocks ?? []).reduce((total, block) => total + aiSiteImportBlockChars(block), 0)\n\n/**\n * More of a running wave's pages, read now. Their blocks share\n * `AI_SITE_IMPORT_WAVE_READ_MAX_CHARS` with every read still held: a page\n * past it keeps what fits, in order, and is marked `cut`.\n */\nexport function aiSiteImportPagesRead(\n state: AiSiteImportState,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n let held = state.pages.filter((page) => !reads.has(page.slot)).reduce((total, page) => total + readChars(page.read), 0)\n let next = state\n for (const [slot, read] of reads) {\n if (typeof read === 'string') {\n next = withPage(next, slot, { refused: read, read: null })\n continue\n }\n let kept = read\n if (read.blocks) {\n const room = Math.max(0, Math.min(AI_SITE_IMPORT_READ_MAX_CHARS, AI_SITE_IMPORT_WAVE_READ_MAX_CHARS - held))\n const capped = aiSiteImportCapBlocks(read.blocks, room)\n held += capped.chars\n kept = { ...read, blocks: capped.blocks, ...(capped.cut || read.cut ? { cut: true } : {}) }\n }\n next = withPage(next, slot, { read: kept, refused: null, title: clipLabel(read.title) || next.pages.find((page) => page.slot === slot)?.title || '' })\n }\n return next\n}\n\n/** A page's photos are in the library. */\nexport function aiSiteImportPhotosIn(\n state: AiSiteImportState,\n slot: string,\n photos: NonNullable<AiSiteImportPage['photos']>,\n): AiSiteImportState {\n return withPage(state, slot, { photos })\n}\n\n/** A page is in: its text and its blocks are no longer kept. */\nexport function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page?.read) return state\n return withPage(state, slot, { read: { ...page.read, text: '', blocks: [] } })\n}\n\n/** A wave is in: waiting for its review, or done when no wave follows it. */\nexport function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState {\n const later = state.waves.some((wave) => wave.index > index && wave.status === 'waiting')\n // Start every remaining wave: no review between waves, the last one's close is the stop.\n let next = withWave(state, index, { status: later && !state.autoContinue ? 'review' : 'done', finishedAt: at })\n // What the wave read is no longer needed by any model answer.\n for (const slot of state.waves.find((wave) => wave.index === index)?.slots ?? []) next = aiSiteImportPageSettled(next, slot)\n return next\n}\n\n/** The state as the console reads it: every page's read text left off. */\nexport function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null {\n if (!state) return null\n return {\n ...state,\n pages: state.pages.map((page) =>\n page.read ? { ...page, read: { ...page.read, text: '', images: [], blocks: [], forms: [] } } : page,\n ),\n }\n}\n\n/*------------------------------------------\n * Credits\n *-----------------------------------------*/\n\n/**\n * How a page is built (AGL-4083), decided by code: every page of a collection\n * (a blog, the legal pages, news) and any page longer than\n * `AI_SITE_IMPORT_PROSE_WORDS` is copied as it is; the main pages are\n * arranged by Aglyn AI, their words kept as written.\n */\nexport function aiSiteImportPageBuild(page: Pick<AiSiteImportPage, 'group' | 'words'>): AiSiteImportBuild {\n return page.group !== 'main' || page.words > AI_SITE_IMPORT_PROSE_WORDS ? 'copied' : 'arranged'\n}\n\nconst whole = (value: number | undefined) => Math.max(0, Math.floor(Number.isFinite(value) ? (value as number) : 0))\n\n/**\n * What bringing in this many pages costs, quoted before it starts (AGL-3976):\n * each page an AI page as measured, its ceiling its two passes, and the\n * site's header and footer once when the wave builds them. Pages the step has\n * chosen a build for (AGL-4083) are priced by it: `arranged` at the\n * arrangement answer and its listing, `copied` at the listing alone.\n */\nexport function aiSiteImportCreditRange(\n pages: number,\n options: { frame?: boolean; arranged?: number; copied?: number } = {},\n): AiCreditRange {\n const count = whole(pages)\n const arranged = whole(options.arranged)\n const copied = whole(options.copied)\n const measured = AI_MEASURED_IMPORT_CREDITS\n return aiCreditRangeOrdered({\n likely:\n count * measured.page.median + arranged * measured.arranged.median + copied * measured.copied.median + (options.frame ? measured.frame.median : 0),\n p90: count * measured.page.p90 + arranged * measured.arranged.p90 + copied * measured.copied.p90 + (options.frame ? measured.frame.p90 : 0),\n ceiling:\n (count + arranged) * AI_SITE_IMPORT_PAGE_CEILING_CREDITS +\n copied * AI_SITE_IMPORT_COPIED_CEILING_CREDITS +\n (options.frame ? AI_SITE_IMPORT_FRAME_CEILING_CREDITS : 0),\n })\n}\n\n/** What a wave still owes, by how its pages are built, and whether its header and footer are owed. */\nexport function aiSiteImportWaveOwed(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): { pages: number; arranged: number; copied: number; frame: boolean } {\n const owed = (slot: string) => (items ? aiSiteImportItemOpen(items, slot) : true)\n const counts = { pages: 0, arranged: 0, copied: 0, frame: wave.frame && !!state.frame && owed(AI_SITE_IMPORT_FRAME_SLOT) }\n for (const slot of wave.slots) {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!owed(slot) || page?.refused) continue\n if (page?.build === 'arranged') counts.arranged += 1\n else if (page?.build === 'copied') counts.copied += 1\n else counts.pages += 1\n }\n return counts\n}\n\n/** A wave's quote: the pages it has still to bring in, and its header and footer while they are owed. */\nexport function aiSiteImportWaveCreditRange(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const owed = aiSiteImportWaveOwed(state, wave, items)\n return aiSiteImportCreditRange(owed.pages, { frame: owed.frame, arranged: owed.arranged, copied: owed.copied })\n}\n\n/** Every wave still waiting, quoted at once: \"Start every remaining wave\" (AGL-4083). */\nexport function aiSiteImportRemainingCreditRange(\n state: AiSiteImportState,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const total = { pages: 0, arranged: 0, copied: 0, frame: false }\n for (const wave of state.waves) {\n if (wave.status !== 'waiting') continue\n const owed = aiSiteImportWaveOwed(state, wave, items)\n total.pages += owed.pages\n total.arranged += owed.arranged\n total.copied += owed.copied\n total.frame = total.frame || owed.frame\n }\n return aiSiteImportCreditRange(total.pages, { frame: total.frame, arranged: total.arranged, copied: total.copied })\n}\n\n/** \"About 95 credits (up to 600): 7 pages arranged by Aglyn AI, 3 copied as they are\"; the build is named once the step has chosen. */\nexport function aiSiteImportQuoteText(\n range: AiCreditRange,\n owed: Pick<ReturnType<typeof aiSiteImportWaveOwed>, 'arranged' | 'copied'>,\n): string {\n const parts: string[] = []\n if (owed.arranged) parts.push(`${aiSiteImportPagesText(owed.arranged)} arranged by ${PLATFORM_BRAND_NAME} AI`)\n if (owed.copied) parts.push(`${owed.arranged ? owed.copied.toLocaleString('en-US') : aiSiteImportPagesText(owed.copied)} copied as ${owed.copied === 1 ? 'it is' : 'they are'}`)\n return parts.length ? `${aiCreditRangeText(range)}: ${parts.join(', ')}` : aiCreditRangeText(range)\n}\n\n/*------------------------------------------\n * Words\n *-----------------------------------------*/\n\n/** Why a page is not brought in, as a person reads it. */\nexport const AI_SITE_IMPORT_LEFT_OUT_COPY: Readonly<Record<AiSiteImportLeftOutReason, string>> = {\n 'plan-limit': 'Past your plan’s page count. Upgrade for more pages, then bring the rest in.',\n robots: 'The site’s robots.txt asks AglynBot not to read it.',\n 'login-form': 'A sign-in page. Pages that ask for a password are never brought in.',\n empty:\n 'No text could be read from this page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet.',\n unreadable: 'It could not be read just now.',\n blocked:\n 'The site’s bot protection or firewall turned AglynBot away. Allow AglynBot in your site’s bot or firewall settings, then bring the site in again.',\n 'too-large': 'Larger than a page can be to bring in.',\n 'off-site': 'It sends visitors to another website.',\n 'not-reached': 'Not reached: the read stopped before it got to this page.',\n}\n\n/** \"3 pages\" / \"1 page\". */\nexport function aiSiteImportPagesText(count: number): string {\n return `${count.toLocaleString('en-US')} ${count === 1 ? 'page' : 'pages'}`\n}\n\n/** The review a finished wave parks on, or the first wave's go-ahead once the site is read. */\nexport function aiSiteImportWaveMessage(\n state: AiSiteImportState,\n items: readonly AiJobItemLedger[] | null | undefined,\n): string {\n // A read that stopped at its home page says why, and what to do.\n if (aiSiteImportReadStops(state.verdict)) return aiSiteImportReadVerdictMessage(state.verdict, state.domain) ?? ''\n const next = aiSiteImportNextWave(state)\n const reviewing = aiSiteImportReviewWave(state)\n if (!reviewing) {\n const kept = state.pages.length\n return next\n ? `Your site is read: ${aiSiteImportPagesText(kept)} to bring in, in ${state.waves.length === 1 ? 'one wave' : `${state.waves.length} waves`}. Start the first wave when you are ready.`\n : 'Your site is read, and nothing on it can be brought in.'\n }\n const rows = (items ?? []).filter((row) => reviewing.slots.includes(row.slot))\n const done = rows.filter((row) => row.status === 'succeeded' || row.status === 'degraded').length\n const lead = `${reviewing.label} is in: ${aiSiteImportPagesText(done)} ready to review as drafts.`\n return next ? `${lead} Start ${next.label} when you are ready.` : lead\n}\n"],"names":["PLATFORM_BRAND_NAME","aiCreditRangeOrdered","aiCreditRangeText","AI_MEASURED_IMPORT_CREDITS","AI_SITE_IMPORT_KIND","AI_SITE_IMPORT_DOMAIN_INPUT","AI_SITE_IMPORT_WAVE_PAGES","AI_SITE_IMPORT_MAX_PAGES","AI_SITE_IMPORT_COLLECTION_MIN","AI_SITE_IMPORT_IMAGES_PER_PAGE","AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS","AI_SITE_IMPORT_MAX_SECTIONS","AI_SITE_IMPORT_READ_MAX_BLOCKS","AI_SITE_IMPORT_READ_MAX_CHARS","AI_SITE_IMPORT_WAVE_READ_MAX_CHARS","AI_SITE_IMPORT_PROSE_WORDS","AI_SITE_IMPORT_BLOCKED_STATUSES","Set","AI_SITE_IMPORT_LABEL_MAX_CHARS","AI_SITE_IMPORT_MIN_WORDS","AI_SITE_IMPORT_FRAME_SLOT","AI_SITE_IMPORT_PAGE_CEILING_CREDITS","AI_SITE_IMPORT_COPIED_CEILING_CREDITS","AI_SITE_IMPORT_FRAME_CEILING_CREDITS","AI_SITE_IMPORT_LEFT_OUT_LISTED","AI_SITE_IMPORT_LEFT_OUT_REASONS","AI_SITE_IMPORT_UNREAD_REASONS","pathOfUrl","url","URL","pathname","replace","wordsOf","text","match","length","readVerdictOf","home","blocked","total","edge","base","homeStatus","status","homeProblem","problem","kind","homeProblemOfSkip","reason","undefined","has","aiSiteImportReadVerdict","crawled","skip","skipped","filter","entry","Math","max","sitemapUrls","pages","page","find","foundBy","path","words","blocks","reduce","sum","block","inline","empty","images","aiSiteImportReadStops","verdict","aiSiteImportReadVerdictReason","AI_SITE_IMPORT_EDGE_HINTS","vercel","cloudflare","NOTHING_SPENT","homeProblemText","aiSiteImportReadVerdictMessage","domain","others","more","toLocaleString","lead","inventoryVerdict","input","skippedOverflow","clipLabel","line","String","trim","slice","trimEnd","segmentsOf","split","Boolean","aiSiteImportPrefix","segments","first","toLowerCase","test","aiSiteImportPrefixLabel","prefix","decodeURIComponent","charAt","toUpperCase","aiSiteImportGroups","menu","sized","counts","Map","set","get","menuPaths","map","collections","count","groupOf","menuRank","index","findIndex","Number","POSITIVE_INFINITY","ordered","sort","a","b","localeCompare","groups","push","id","label","orderedWithin","group","rank","order","aiSiteImportSlug","taken","add","clean","value","last","prefixed","candidate","attempt","slug","aiSiteImportInventory","leftOutCount","waves","wavePages","floor","limit","pageLimit","isFinite","leftOut","leave","unreached","stoppedBy","title","siteName","ownership","readAt","existingPages","found","frame","reviewId","readable","room","members","kept","parts","ceil","part","slots","slot","pop","wave","draftId","mintId","startedAt","finishedAt","aiSiteImportLedger","state","row","op","creditsSpent","creditsRefunded","outputs","OPEN","aiSiteImportItemOpen","items","aiSiteImportRunningWave","aiSiteImportNextWave","aiSiteImportReviewWave","aiSiteImportNextWork","running","next","unread","read","refused","photos","withPage","patch","withWave","aiSiteImportWaveStarted","at","reads","aiSiteImportPagesRead","aiSiteImportBlockChars","alt","href","aiSiteImportCapBlocks","maxChars","maxBlocks","chars","size","cut","readChars","held","min","capped","aiSiteImportPhotosIn","aiSiteImportPageSettled","aiSiteImportWaveClosed","later","some","autoContinue","aiSiteImportWire","forms","aiSiteImportPageBuild","whole","aiSiteImportCreditRange","options","arranged","copied","measured","likely","median","p90","ceiling","aiSiteImportWaveOwed","owed","build","aiSiteImportWaveCreditRange","aiSiteImportRemainingCreditRange","aiSiteImportQuoteText","range","aiSiteImportPagesText","join","AI_SITE_IMPORT_LEFT_OUT_COPY","robots","unreadable","aiSiteImportWaveMessage","reviewing","rows","includes","done"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-import.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\n/*==========================================\n * BRING MY SITE: THE WHOLE SITE, IN REVIEWED WAVES (AGL-3976, parent AGL-3973).\n *\n * The `import` job reads a website the workspace has PROVED it owns\n * (AGL-3974's claims) and brings it in as draft pages of a new site. This\n * module is the job's plan of record, pure, so the step, the doors, the\n * console and the specs read one set of rules:\n *\n * - **The inventory.** The crawl's pages, grouped: the MAIN pages (home, the\n * pages its menu links, every top-level page, and any page that belongs to\n * no collection), then each COLLECTION (an address prefix holding at least\n * `AI_SITE_IMPORT_COLLECTION_MIN` pages: `/blog/…`, `/news/…`, a dated\n * `/2024/05/…` archive).\n * - **Never past the plan.** Pages are kept in that order until the site's\n * room under its plan's page count is used; the rest are LEFT OUT, said so\n * with the reason, never dropped silently. So a plan's limit always leaves\n * out a collection's tail before it leaves out a main page.\n * - **Waves.** The kept pages are cut into waves of at most\n * `AI_SITE_IMPORT_WAVE_PAGES`. The first wave is the main pages, with the\n * site's header and footer built first; each later wave is one\n * collection's next slice, so a review is about one kind of page at a\n * time (\"Blog, 1 of 3\"). A wave runs, then waits for the person: nothing\n * starts the next one but them.\n * - **The price.** An imported page is an AI page (`AI_MEASURED_IMPORT_CREDITS`,\n * about 27 credits); a wave is quoted before it starts as its pages and,\n * for the first, its header and footer. Once the step says how each page\n * is built, a page copied as it is costs its listing and a page Aglyn AI\n * arranges its arrangement, and the quote names both (AGL-4083).\n * - **Home first (AGL-4083).** The read's verdict (`aiSiteImportReadVerdict`)\n * stops an import whose home page could not be read before anything is\n * planned, quoted or spent: a site seen without its home page is not the\n * site. The person is told why and checks again.\n *=========================================*/\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport type { SiteImportBlock, SiteImportForm } from '../site-import/site-import-html'\nimport { aiCreditRangeOrdered, aiCreditRangeText, AI_MEASURED_IMPORT_CREDITS, type AiCreditRange } from './ai-credit-estimate'\nimport type { AiJobItemLedger } from './ai-jobs.types'\n\n/** The job kind. */\nexport const AI_SITE_IMPORT_KIND = 'import' as const\n\n/** The input naming the proved domain the job brings in. */\nexport const AI_SITE_IMPORT_DOMAIN_INPUT = 'domain'\n\n/** The most pages one wave brings in. */\nexport const AI_SITE_IMPORT_WAVE_PAGES = 10\n\n/**\n * The most pages one import reads, whatever the plan allows: an unlimited\n * plan is still one crawl's worth, and the crawl's own time budget may stop\n * sooner (the inventory then says so). Only pages count: a file a link\n * points at is never fetched as one (AGL-4106), so aglyn.com's 209 pages fit\n * with room where 136 press-kit files once took 136 of 200.\n */\nexport const AI_SITE_IMPORT_MAX_PAGES = 400\n\n/** The fewest pages under one address prefix that make it a collection. */\nexport const AI_SITE_IMPORT_COLLECTION_MIN = 3\n\n/** The most photos brought in from one page. */\nexport const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8\n\n/**\n * The most characters of one page's text a wave keeps for the page step's\n * prompt, which reads no more than this; every word is kept on the read's\n * `blocks` (AGL-4083).\n */\nexport const AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS = 6_000\n\n/** The most sections one imported page is planned with: one for every second-level heading (AGL-4083). */\nexport const AI_SITE_IMPORT_MAX_SECTIONS = 24\n\n/** The most blocks of one page a wave's read keeps. */\nexport const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2_000\n\n/** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */\nexport const AI_SITE_IMPORT_READ_MAX_CHARS = 120_000\n\n/** The most characters one page's layout keeps on the job document (AGL-4106): its icons' drawings go first. */\nexport const AI_SITE_IMPORT_LAYOUT_MAX_CHARS = 48_000\n\n/**\n * The most characters of blocks (and their layouts, AGL-4106) the reads of one import hold at once: they\n * ride the job document, which Firestore caps at 1 MiB, beside the\n * inventory and the ledger. A wave's later page past it keeps what fits and\n * says it was cut.\n */\nexport const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400_000\n\n/**\n * A collection's page longer than this is prose: a policy, an article, a\n * long story (AGL-4083). A collection's page is copied as it is whatever its\n * length, and since AGL-4106 a main page is arranged whatever its length:\n * `/product` at 1,072 words is laid out as its source lays it out, not copied\n * as one column.\n */\nexport const AI_SITE_IMPORT_PROSE_WORDS = 900\n\n/** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */\nexport const AI_SITE_IMPORT_BLOCKED_STATUSES: ReadonlySet<number> = new Set([401, 403, 429])\n\n/** The most characters a section's name, a page's title or a label keeps. */\nexport const AI_SITE_IMPORT_LABEL_MAX_CHARS = 60\n\n/** A page with fewer words than this and no photo has nothing to bring in. */\nexport const AI_SITE_IMPORT_MIN_WORDS = 5\n\n/** The item ledger slot of the site's header and footer. */\nexport const AI_SITE_IMPORT_FRAME_SLOT = 'frame'\n\n/** The ceiling one imported page reserves: its answer pass and its listing pass, at 50 each. */\nexport const AI_SITE_IMPORT_PAGE_CEILING_CREDITS = 100\n\n/** The ceiling a page copied as it is reserves: its listing pass alone. */\nexport const AI_SITE_IMPORT_COPIED_CEILING_CREDITS = 50\n\n/** The ceiling the header and footer reserve: one answer at 50. */\nexport const AI_SITE_IMPORT_FRAME_CEILING_CREDITS = 50\n\n/** The most left-out pages listed by address; the rest are counted. */\nexport const AI_SITE_IMPORT_LEFT_OUT_LISTED = 60\n\n/** A page the crawl read, as the inventory keeps it: no text, only what plans it. */\nexport interface AiSiteImportListedPage {\n url: string\n path: string\n title: string\n foundBy: 'home' | 'sitemap' | 'link'\n words: number\n images: number\n}\n\nexport type AiSiteImportGroupKind = 'main' | 'collection'\n\nexport interface AiSiteImportGroup {\n /** `main`, or `c:<prefix>` for a collection. */\n id: string\n label: string\n kind: AiSiteImportGroupKind\n /** Pages of the group the crawl read, kept or not. */\n pages: number\n}\n\n/** Why a page the site has is not brought in. */\nexport type AiSiteImportLeftOutReason =\n /** Past the site's room under its plan's page count. */\n | 'plan-limit'\n /** robots.txt keeps AglynBot off it. */\n | 'robots'\n /** A sign-in page: refused outright. */\n | 'login-form'\n /** No text and no photo: nothing to bring in. */\n | 'empty'\n /** It could not be read: an error, no answer, or not a web page. */\n | 'unreadable'\n /** The site's bot protection or firewall turned AglynBot away (401, 403 or 429). */\n | 'blocked'\n /** Over the size one page may be. */\n | 'too-large'\n /** It sends visitors to another website. */\n | 'off-site'\n /** The read stopped (its clock, its bytes, its page count) before it got to it (AGL-4083). */\n | 'not-reached'\n /** A link to a file (a download, a PDF with no extension), not a page (AGL-4106). */\n | 'file'\n\nexport const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[] = [\n 'plan-limit',\n 'robots',\n 'login-form',\n 'empty',\n 'unreadable',\n 'blocked',\n 'too-large',\n 'off-site',\n 'not-reached',\n 'file',\n]\n\n/** The reasons that mean the page was there and AglynBot could not read it: the \"could not be read\" of what was found. */\nexport const AI_SITE_IMPORT_UNREAD_REASONS: readonly AiSiteImportLeftOutReason[] = ['unreadable', 'blocked', 'too-large', 'not-reached']\n\nexport interface AiSiteImportLeftOut {\n url: string\n path: string\n title: string\n reason: AiSiteImportLeftOutReason\n}\n\n/** A photo brought into the site's media library, as a page places it. */\nexport interface AiSiteImportPhoto {\n mediaId: string\n src: string\n width?: number\n height?: number\n alt?: string\n /** The address on the old site it was read from, where a page that is copied places it (AGL-4084). */\n from?: string\n}\n\n/*------------------------------------------\n * The source's layout, as the reader reads it (AGL-4106)\n *-----------------------------------------*/\n\n/**\n * One line of a page, by its index in the page's main-region blocks\n * (`AiSiteImportPageRead.blocks`, 0-based). The layout holds no words of\n * its own: every word is a block's, placed once.\n */\nexport type SiteImportLineRef = number\n\n/** A section's background, in the layout language's bands. */\nexport type SiteImportBand = 'plain' | 'soft' | 'brand' | 'dark'\n\n/** How the source draws a button: filled, outlined, or as a link with button padding. */\nexport type SiteImportButtonStyle = 'primary' | 'secondary' | 'quiet'\n\n/** What a repeated group of the source is. */\nexport type SiteImportGroupKind = 'cards' | 'steps' | 'stats' | 'faq' | 'quotes' | 'logos' | 'pricing' | 'list' | 'text'\n\nexport const SITE_IMPORT_GROUP_KINDS: readonly SiteImportGroupKind[] = ['cards', 'steps', 'stats', 'faq', 'quotes', 'logos', 'pricing', 'list', 'text']\n\n/** One line in place. `role` only where the source's own styling says it (an uppercase eyebrow, a large lede, a small note). */\nexport interface SiteImportLayoutLine {\n kind: 'line'\n line: SiteImportLineRef\n role?: 'eyebrow' | 'lede' | 'note'\n /** The source draws this line (a link, or a `<button>`'s words) as a button. Its destination is the link block's `href`. */\n button?: SiteImportButtonStyle\n}\n\n/** A picture in place: an image block. */\nexport interface SiteImportLayoutPicture {\n kind: 'picture'\n line: SiteImportLineRef\n /** Where clicking it goes, when it sits in a link. */\n href?: string\n /** It sits in a `<button>` (a video's poster, a lightbox): the picture is kept, the button is not a destination. */\n button?: true\n}\n\n/** An icon standing on its own, by index into `SiteImportLayout.icons`. */\nexport interface SiteImportLayoutIcon {\n kind: 'icon'\n icon: number\n}\n\n/** Where one of the page's forms sat, by index into `AiSiteImportPageRead.forms`. */\nexport interface SiteImportLayoutForm {\n kind: 'form'\n form: number\n}\n\n/**\n * A row of columns side by side; `cols` are relative widths in twelfths\n * (`[4, 8]`, `[1, 1, 1]`). Every list of lists is a list of objects: the\n * layout rides a Firestore document, which holds no array inside an array.\n */\nexport interface SiteImportLayoutRow {\n kind: 'row'\n cols: number[]\n cells: Array<{ nodes: SiteImportLayoutNode[] }>\n}\n\n/**\n * One item of a group. Every line of the item is in `lines`, in order, and\n * in exactly one of the named parts.\n */\nexport interface SiteImportLayoutItem {\n lines: SiteImportLineRef[]\n /** The item's name: its heading, its title link, or its first short line. */\n title?: SiteImportLineRef\n /** A stat's figure, a step's number, a plan's price. */\n figure?: SiteImportLineRef\n text?: SiteImportLineRef[]\n /** Its bullet points (a plan's features). */\n list?: SiteImportLineRef[]\n /** Its own call to action: a \"See … →\" link or a button, last in the item. */\n link?: SiteImportLineRef[]\n image?: SiteImportLineRef\n /** The icon that leads it, by index into `SiteImportLayout.icons`. */\n icon?: number\n /** The icon each of its bullet points carries (a check mark), when they share one. */\n listIcon?: number\n /** Where the item goes as a whole: its title link's address. */\n to?: string\n}\n\nexport interface SiteImportLayoutGroup {\n kind: 'group'\n group: SiteImportGroupKind\n /** Columns the source lays the items out in on a wide screen. */\n columns: number\n /** Each item is drawn as a box (a border, a shadow or a fill of its own). */\n boxed: boolean\n items: SiteImportLayoutItem[]\n}\n\n/** A table: rows of cells, each cell its lines (objects, not nested arrays, for Firestore). */\nexport interface SiteImportLayoutTable {\n kind: 'table'\n /** Whether the first row names the columns. */\n header: boolean\n rows: Array<{ cells: Array<{ lines: SiteImportLineRef[] }> }>\n}\n\n/** A tab strip and its panels; a panel the source did not draw is empty. */\nexport interface SiteImportLayoutTabs {\n kind: 'tabs'\n tabs: Array<{ label: SiteImportLineRef | null; selected?: true; panel: SiteImportLayoutNode[] }>\n}\n\nexport type SiteImportLayoutNode =\n | SiteImportLayoutLine\n | SiteImportLayoutPicture\n | SiteImportLayoutIcon\n | SiteImportLayoutForm\n | SiteImportLayoutRow\n | SiteImportLayoutGroup\n | SiteImportLayoutTable\n | SiteImportLayoutTabs\n\n/** Where the hero's picture sits against its words. */\nexport type SiteImportHeroPicture = 'beside' | 'behind' | 'below' | 'above'\n\nexport interface SiteImportLayoutSection {\n /** Its `aria-label`, its heading, or \"Section n\": neutralized, cut to a label. */\n name: string\n /** How its edges were found: a landmark (`<section>`), a container of its own, or its headings. */\n from: 'landmark' | 'container' | 'heading'\n band: SiteImportBand\n /** Its background color as `#rrggbb`, when it has one. */\n background: string | null\n /** A gradient or a photo behind it. */\n backdrop?: { kind: 'gradient' } | { kind: 'picture'; url: string }\n align: 'start' | 'center'\n /** Set on the section holding the page's first `<h1>`. */\n hero?: { picture: SiteImportHeroPicture | null }\n nodes: SiteImportLayoutNode[]\n /** Every line of the section, in order. */\n lines: SiteImportLineRef[]\n}\n\n/** An icon the page draws, once however often it repeats. */\nexport interface SiteImportIconShape {\n viewBox: string\n /** One path, for the Besigner's icon element (`iconPath`, drawn on a 24-unit square). */\n path?: string\n /** A small drawing of several shapes, geometry only, for a picture of it. */\n svg?: string\n}\n\n/** What the source's look is made of (AGL-4106), read from its stylesheets. */\nexport interface SiteImportLookFacts {\n fonts: {\n /** The family its body text is set in, as named, when it is not a system font. */\n body: string | null\n /** The family its headings are set in. */\n headings: string | null\n /** The families it loads from Google Fonts, in order. */\n google: string[]\n }\n /** The page's own background, `#rrggbb`. */\n background: string | null\n /** Its body text color. */\n text: string | null\n /** The fill of its filled buttons, the most used first. */\n buttonFill: string | null\n /** The text color on them. */\n buttonText: string | null\n /** Its buttons' corner radius, px. */\n buttonRadius: number | null\n /** The color its text links are drawn in. */\n link: string | null\n /** Its header bar's background. */\n header: string | null\n}\n\n/** A page's layout, as the source lays it out (AGL-4106). */\nexport interface SiteImportLayout {\n v: 1\n sections: SiteImportLayoutSection[]\n icons: SiteImportIconShape[]\n look: SiteImportLookFacts\n}\n\n/** One entry of a menu, as the source's header shows it. */\nexport interface SiteImportNavEntry {\n label: string\n /** The line under it in a mega menu. */\n text?: string\n /** Where it goes: absolute http(s), `mailto:` or `tel:`. */\n href: string\n /** Its icon, when it has a one-path icon. */\n iconPath?: string\n viewBox?: string\n /** The heading of the menu's column it sits in (\"By team\"). */\n column?: string\n}\n\n/** A top-level item of the source's menu: a link, or a menu of entries under a name. */\nexport interface SiteImportNavGroup {\n label: string\n /** Where the name itself goes; `null` when it only opens its menu. */\n href: string | null\n /** Empty for a plain link. */\n items: SiteImportNavEntry[]\n}\n\n/** A button in the source's header outside its menu (\"Sign in\", \"Start free\"). */\nexport interface SiteImportNavCta {\n label: string\n href: string | null\n style: SiteImportButtonStyle\n}\n\n/** The source's header menu (AGL-4106). Untrusted data from the owner's site. */\nexport interface SiteImportNavTree {\n items: SiteImportNavGroup[]\n ctas: SiteImportNavCta[]\n /** The source shows a menu button on phones. */\n phoneMenu: boolean\n /** The header bar's background, `#rrggbb`. */\n background: string | null\n}\n\nexport interface SiteImportFooterLink {\n label: string\n href: string\n}\n\n/** The source's footer (AGL-4106): its columns, profiles and facts, as written. */\nexport interface SiteImportFooter {\n columns: Array<{ heading: string; links: SiteImportFooterLink[] }>\n /** Its links to profiles on other sites. */\n social: Array<SiteImportFooterLink & { network: string }>\n /** Links outside any column and not a profile (Privacy, Terms, Status). */\n links: SiteImportFooterLink[]\n /** Its lines of text outside links (a tagline), as written. */\n text: string[]\n address: string | null\n phone: string | null\n email: string | null\n /** Its © line, word for word. */\n copyright: string | null\n}\n\n/** Every line a layout's nodes place, in order. */\nexport function siteImportLayoutLines(nodes: readonly SiteImportLayoutNode[]): SiteImportLineRef[] {\n const out: SiteImportLineRef[] = []\n const visit = (list: readonly SiteImportLayoutNode[]) => {\n for (const node of list) {\n switch (node.kind) {\n case 'line':\n case 'picture':\n out.push(node.line)\n break\n case 'row':\n for (const cell of node.cells) visit(cell.nodes)\n break\n case 'group':\n for (const item of node.items) out.push(...item.lines)\n break\n case 'table':\n for (const row of node.rows) for (const cell of row.cells) out.push(...cell.lines)\n break\n case 'tabs':\n for (const tab of node.tabs) {\n if (tab.label !== null) out.push(tab.label)\n visit(tab.panel)\n }\n break\n default:\n break\n }\n }\n }\n visit(nodes)\n return out\n}\n\n/** How many characters a layout weighs on the job document. */\nexport function siteImportLayoutChars(layout: SiteImportLayout | null | undefined): number {\n return layout ? JSON.stringify(layout).length : 0\n}\n\n/**\n * A layout over only the first `count` lines (a read the wave's budget cut\n * short): references past the cut are dropped, and what is left empty goes.\n */\nexport function siteImportLayoutWithin(layout: SiteImportLayout, count: number): SiteImportLayout {\n const keep = (ref: SiteImportLineRef) => ref >= 0 && ref < count\n const sections = layout.sections\n .map((section) => ({ ...section, nodes: siteImportFilterNodes(section.nodes, keep), lines: section.lines.filter(keep) }))\n .filter((section) => section.lines.length)\n return { ...layout, sections }\n}\n\n/** Nodes with only the lines `keep` keeps; what is left empty goes. */\nexport function siteImportFilterNodes(list: readonly SiteImportLayoutNode[], keep: (ref: SiteImportLineRef) => boolean): SiteImportLayoutNode[] {\n const nodes = (list: readonly SiteImportLayoutNode[]): SiteImportLayoutNode[] =>\n list.flatMap((node): SiteImportLayoutNode[] => {\n switch (node.kind) {\n case 'line':\n case 'picture':\n return keep(node.line) ? [node] : []\n case 'icon':\n case 'form':\n return [node]\n case 'row': {\n const cells = node.cells.map((cell) => ({ nodes: nodes(cell.nodes) }))\n return cells.some((cell) => cell.nodes.length) ? [{ ...node, cells }] : []\n }\n case 'group': {\n const items = node.items\n .map((item) => trimItem(item, keep))\n .filter((item) => item.lines.length || item.icon !== undefined)\n return items.length ? [{ ...node, items }] : []\n }\n case 'table': {\n const rows = node.rows\n .map((row) => ({ cells: row.cells.map((cell) => ({ lines: cell.lines.filter(keep) })) }))\n .filter((row) => row.cells.some((cell) => cell.lines.length))\n return rows.length ? [{ ...node, rows }] : []\n }\n case 'tabs': {\n const tabs = node.tabs.map((tab) => ({ ...tab, label: tab.label !== null && keep(tab.label) ? tab.label : null, panel: nodes(tab.panel) }))\n return tabs.some((tab) => tab.label !== null || tab.panel.length) ? [{ ...node, tabs }] : []\n }\n default:\n return []\n }\n })\n return nodes(list)\n}\n\nfunction trimItem(item: SiteImportLayoutItem, keep: (ref: SiteImportLineRef) => boolean): SiteImportLayoutItem {\n const out: SiteImportLayoutItem = { ...item, lines: item.lines.filter(keep) }\n for (const key of ['title', 'figure', 'image'] as const) if (out[key] !== undefined && !keep(out[key] as number)) delete out[key]\n for (const key of ['text', 'list', 'link'] as const) {\n const list = out[key]?.filter(keep)\n if (list?.length) out[key] = list\n else delete out[key]\n }\n return out\n}\n\n/** A layout with its icons' drawings dropped, for a page that must be lighter. */\nexport function siteImportLayoutWithoutDrawings(layout: SiteImportLayout): SiteImportLayout {\n return { ...layout, icons: layout.icons.map((icon) => (icon.svg ? { viewBox: icon.viewBox } : icon)) }\n}\n\n/** What a wave's read found on one page, for its model answer. Never on the wire. */\nexport interface AiSiteImportPageRead {\n at: string\n title: string\n /** The page's text in order, cut to `AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS`; fenced only when a prompt reads it. */\n text: string\n /** The page's sections, from its headings. */\n sections: string[]\n /** Its photos' addresses, at most `AI_SITE_IMPORT_IMAGES_PER_PAGE`. */\n images: Array<{ url: string; alt: string }>\n /**\n * Every block of the page's main region, in order (AGL-4083): its words as\n * written, its headings, lists, links and pictures, within\n * `AI_SITE_IMPORT_READ_MAX_BLOCKS` and `AI_SITE_IMPORT_READ_MAX_CHARS`.\n * Untrusted data: fenced wherever a prompt reads it. Emptied once the page\n * is in; absent on a read made before it was kept.\n */\n blocks?: SiteImportBlock[]\n /** Set when `blocks` had to stop short of the page, at its budget or the wave's. */\n cut?: boolean\n /** The words of the page's main region, as the old page has them. */\n words?: number\n /** The forms the page asks visitors to fill in, by their fields. */\n forms?: SiteImportForm[]\n /**\n * How the source lays the page out (AGL-4106): its sections, bands, rows,\n * groups, pictures, icons and buttons, over `blocks` by index. Absent on a\n * read made before the reader, or when it did not fit the wave's budget.\n */\n layout?: SiteImportLayout | null\n /**\n * The page's browser title and meta description as the source writes them\n * (AGL-4107), which the new page's listing keeps word for word. Absent on a\n * read made before they were kept.\n */\n meta?: { title: string; description: string }\n}\n\n/** How a page is built from its old page, as the step chose: its words copied as they are, or arranged by Aglyn AI. */\nexport type AiSiteImportBuild = 'copied' | 'arranged'\n\nexport interface AiSiteImportPage {\n /** The item ledger slot: `p0`, `p1`, … */\n slot: string\n url: string\n path: string\n title: string\n group: string\n wave: number\n /** The id the page's draft is written under, minted with the inventory. */\n draftId: string\n /** The page's address on the new site. */\n slug: string\n words: number\n images: number\n /** Filled by the wave's read; the text is cleared once the page is in. */\n read?: AiSiteImportPageRead | null\n /** Set when the wave's read found the page cannot be brought in after all. */\n refused?: AiSiteImportLeftOutReason | null\n /** How the step builds the page (`aiSiteImportPageBuild`); absent until it chooses, and the page is priced as an AI page. */\n build?: AiSiteImportBuild | null\n /** What came through once the page is in: its words on the new page. */\n brought?: { words: number } | null\n /** Filled once the page's photos are in the library. */\n photos?: {\n brought: number\n /** Already in the library from an earlier page or import: the same file, never a second copy. */\n reused: number\n skipped: number\n assets: AiSiteImportPhoto[]\n } | null\n}\n\n/**\n * A wave's place in its import: waiting to start, running, in and waiting for\n * the person's review, in, or `stopped` — the import was canceled before the\n * wave was in, while it ran or before it started (AGL-4107).\n */\nexport type AiSiteImportWaveStatus = 'waiting' | 'running' | 'review' | 'done' | 'stopped'\n\nexport interface AiSiteImportWave {\n index: number\n label: string\n /** The group the wave brings in; the first wave's is `main`. */\n group: string\n slots: string[]\n /** Whether the wave builds the site's header and footer first. */\n frame: boolean\n status: AiSiteImportWaveStatus\n startedAt: string | null\n finishedAt: string | null\n}\n\nexport interface AiSiteImportState {\n domain: string\n /** The site's name, as Create site was given it: the header's name. */\n siteName: string\n ownership: { method: string | null; provenAt: number | null; detail: string | null }\n readAt: string\n /** The plan's page count; `null` when it has none. */\n pageLimit: number | null\n /** Pages the site already had when its import read the old site. */\n existingPages: number\n /** Pages the crawl read. */\n found: number\n /** Why the crawl stopped short of the whole site, or `null` when it did not. */\n stoppedBy: string | null\n groups: AiSiteImportGroup[]\n pages: AiSiteImportPage[]\n waves: AiSiteImportWave[]\n /** At most `AI_SITE_IMPORT_LEFT_OUT_LISTED`, by address. */\n leftOut: AiSiteImportLeftOut[]\n /** Every page left out, by reason, listed or not. */\n leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>>\n /** The header and footer's draft id, built in the first wave; `null` when there are no main pages. */\n frame: { draftId: string } | null\n /**\n * \"Start every remaining wave\" (AGL-4083): each wave closes and the next\n * starts with no review between them; the last wave's close is the stop.\n * Off unless the person chose it.\n */\n autoContinue?: boolean\n /**\n * The wave \"Start every remaining wave\" was chosen at (AGL-4084): the one\n * start the person made, which the day's limit counts; the waves it starts\n * after it are the same start.\n */\n autoContinueFrom?: number | null\n /** What the read found of the site as a whole; a stopping verdict plans nothing. */\n verdict?: AiSiteImportReadVerdict | null\n /** What the home page says of the site as a whole (AGL-4084): its look, its header and footer, its listing. */\n home?: AiSiteImportHome | null\n /** The look pass, once it ran in the first wave (AGL-4084). */\n look?: AiSiteImportLook | null\n}\n\n/**\n * What the home page says of the site as a whole (AGL-4084), kept from the\n * read: the site's listing in its own words, its header's brand as the\n * source draws it, and the words its header and footer carry. Untrusted\n * data from the owner's site, fenced wherever a prompt reads it.\n */\nexport interface AiSiteImportHome {\n url: string\n title: string\n description: string\n ogImage: string | null\n siteName: string\n brand: {\n kind: 'logo-only' | 'logo-and-name' | 'name-only'\n /** The name as the header prints it, where it prints one. */\n text: string\n logo: { url: string; alt: string; type: 'svg' | 'raster' | null } | null\n }\n facts: {\n /** The header's and footer's lines, as written, in order. */\n lines: string[]\n emails: string[]\n phones: string[]\n social: string[]\n address: string\n }\n /**\n * The source's header menu as its home page builds it (AGL-4108): its\n * items, their menus, its buttons. The header and footer are drawn from it\n * with no model; absent on a read made before the reader, or where the home\n * page has no menu the reader can find.\n */\n nav?: SiteImportNavTree | null\n /** The source's footer as its home page builds it (AGL-4108): its columns, profiles, facts and © line. */\n footer?: SiteImportFooter | null\n /** What the source's look is made of (AGL-4108): its colors, its button fill and corners, its fonts. */\n look?: SiteImportLookFacts | null\n}\n\n/**\n * Whether the site's header and footer are built from the source's own menu\n * (AGL-4108), with no model: the home page's menu was read. Then the frame\n * costs nothing and is quoted at nothing.\n */\nexport function aiSiteImportFrameIsRead(state: Pick<AiSiteImportState, 'home'> | null | undefined): boolean {\n const nav = state?.home?.nav\n return !!nav && (nav.items.length > 0 || nav.ctas.length > 0)\n}\n\n/** What the look pass did (AGL-4084): no model is asked. */\nexport interface AiSiteImportLook {\n at: string\n /** The theme: drawn from the source (`applied`), the owner's kept (`kept`), or the source's look could not be read. */\n theme: 'applied' | 'kept' | 'unread'\n /** The source's colors it was drawn from, the strongest first. */\n colors: string[]\n /** The source's logo, as the library holds it, for the header's brand. */\n logo: AiSiteImportPhoto | null\n /** Whether the site's listing was taken from the home page's own words. */\n seo: boolean\n /** The look's seed, which every page of the import is drawn with. */\n seed: number | null\n /** The look's header arrangement, band rhythm and motion, which the header, the footer and every page are drawn in. */\n style?: { headerAlign?: string; rhythm?: string; motion?: string } | null\n}\n\n/*------------------------------------------\n * The read's verdict: Home first (AGL-4083)\n *-----------------------------------------*/\n\n/**\n * - `home-blocked`: bot protection or a firewall turned AglynBot away from\n * the home page (401, 403 or 429).\n * - `home-unreadable`: the home page could not be read for any other reason,\n * or read as an empty shell (a site that needs a browser to show its words).\n * - `mostly-blocked`: the home page came in, but more than half the site's\n * addresses were turned away; the import goes on, and says so.\n * - `ok`: anything else.\n */\nexport type AiSiteImportReadVerdictKind = 'ok' | 'home-blocked' | 'home-unreadable' | 'mostly-blocked'\n\n/** Why the home page did not come in. */\nexport type AiSiteImportHomeProblem =\n | 'blocked'\n | 'http-error'\n | 'unreachable'\n | 'not-html'\n | 'too-large'\n | 'off-site'\n | 'robots'\n | 'login-form'\n | 'not-reached'\n | 'empty'\n\nexport interface AiSiteImportReadVerdict {\n kind: AiSiteImportReadVerdictKind\n /** Addresses turned away by bot protection or a firewall, the home page among them. */\n blocked: number\n /** The site's addresses the read knew of: the sitemaps', or everything it asked for. */\n total: number\n /** What the home page answered, when it answered with an error. */\n homeStatus: number | null\n homeProblem: AiSiteImportHomeProblem | null\n /** Whose edge turned the home page away, when it said. */\n edge: 'vercel' | 'cloudflare' | null\n}\n\n/** A crawl as the verdict reads it; the crawler's `SiteImportInventory` is one. */\nexport interface AiSiteImportReadInput {\n pages: ReadonlyArray<{\n path: string\n foundBy: string\n blocks: ReadonlyArray<{ kind: string; text?: string; inline?: boolean }>\n images: readonly unknown[]\n }>\n skipped: ReadonlyArray<{ url: string; reason: string; status?: number; edge?: string | null }>\n sitemapUrls: number\n}\n\nconst pathOfUrl = (url: string): string => {\n try {\n return new URL(url).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n return ''\n }\n}\n\nconst wordsOf = (text: string) => (text.match(/[\\p{L}\\p{N}]+/gu) ?? []).length\n\n/** One verdict from what became of the home page and how much of the site was turned away. */\nfunction readVerdictOf(\n home: { problem: AiSiteImportHomeProblem; status: number | null; edge: string | null } | null,\n blocked: number,\n total: number,\n): AiSiteImportReadVerdict {\n const edge: AiSiteImportReadVerdict['edge'] = home?.edge === 'vercel' ? 'vercel' : home?.edge === 'cloudflare' ? 'cloudflare' : null\n const base = { blocked, total, homeStatus: home?.status ?? null, homeProblem: home?.problem ?? null, edge }\n if (home) return { kind: home.problem === 'blocked' ? 'home-blocked' : 'home-unreadable', ...base }\n return { kind: total > 0 && blocked * 2 > total ? 'mostly-blocked' : 'ok', ...base }\n}\n\n/** What a crawler skip of the home page means for it. */\nfunction homeProblemOfSkip(reason: string, status: number | undefined): AiSiteImportHomeProblem {\n switch (reason) {\n case 'http-error':\n return status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(status) ? 'blocked' : 'http-error'\n case 'off-origin-redirect':\n return 'off-site'\n case 'not-html':\n case 'too-large':\n case 'robots':\n case 'login-form':\n case 'not-reached':\n return reason\n default:\n return 'unreachable'\n }\n}\n\n/**\n * The read's verdict (AGL-4083), pure over the crawl: whether the home page\n * came in, and how much of the site bot protection turned away. An import\n * whose verdict stops (`aiSiteImportReadStops`) plans no wave, quotes\n * nothing and spends nothing: the person is told why and checks again.\n */\nexport function aiSiteImportReadVerdict(crawled: AiSiteImportReadInput): AiSiteImportReadVerdict {\n const blocked = crawled.skipped.filter(\n (entry) => entry.reason === 'http-error' && entry.status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(entry.status),\n ).length\n const total = Math.max(crawled.sitemapUrls, crawled.pages.length + crawled.skipped.length)\n const page = crawled.pages.find((entry) => entry.foundBy === 'home') ?? crawled.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const words = page.blocks.reduce((sum, block) => sum + (block.kind === 'image' || block.inline ? 0 : wordsOf(block.text ?? '')), 0)\n const empty = words < AI_SITE_IMPORT_MIN_WORDS && page.images.length === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = crawled.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n return readVerdictOf(\n skip\n ? { problem: homeProblemOfSkip(skip.reason, skip.status), status: skip.status ?? null, edge: skip.edge ?? null }\n : { problem: 'not-reached', status: null, edge: null },\n blocked,\n total,\n )\n}\n\n/** Whether a verdict stops the import before anything is planned: its home page is not in. */\nexport function aiSiteImportReadStops(verdict: AiSiteImportReadVerdict | null | undefined): boolean {\n return verdict?.kind === 'home-blocked' || verdict?.kind === 'home-unreadable'\n}\n\n/** The review an import that stopped at its read waits under: `blocked` or `unreadable`; `null` when it goes on. */\nexport function aiSiteImportReadVerdictReason(verdict: AiSiteImportReadVerdict | null | undefined): 'blocked' | 'unreadable' | null {\n if (verdict?.kind === 'home-blocked') return 'blocked'\n if (verdict?.kind === 'home-unreadable') return 'unreadable'\n return null\n}\n\n/** Where a site owner lets AglynBot through, on the edges that name themselves. */\nexport const AI_SITE_IMPORT_EDGE_HINTS: Readonly<Record<'vercel' | 'cloudflare', string>> = {\n vercel: 'Your site answers through Vercel: in its Firewall, under Rules, add a bypass rule for the user agent AglynBot.',\n cloudflare:\n 'Your site answers through Cloudflare: under Security, WAF, add a custom rule that skips the checks when the user agent contains AglynBot.',\n}\n\nconst NOTHING_SPENT = 'Nothing was brought in and no AI credits were used.'\n\n/** Why the home page could not be read, inside the sentence that says so. */\nfunction homeProblemText(verdict: AiSiteImportReadVerdict): string {\n switch (verdict.homeProblem) {\n case 'http-error':\n return verdict.homeStatus ? `it answered with an error, ${verdict.homeStatus}` : 'it answered with an error'\n case 'not-html':\n return 'it did not answer with a web page'\n case 'too-large':\n return 'it is larger than a page can be to bring in'\n case 'off-site':\n return 'it sends visitors to another website'\n case 'not-reached':\n return 'the read ran out of time before it'\n default:\n return 'it did not answer'\n }\n}\n\n/**\n * What the person is told about the read (AGL-4083), in console words: why\n * nothing was brought in and what to do, or that most of the site was turned\n * away. `null` when there is nothing to say.\n */\nexport function aiSiteImportReadVerdictMessage(verdict: AiSiteImportReadVerdict | null | undefined, domain: string): string | null {\n if (!verdict) return null\n if (verdict.kind === 'home-blocked') {\n const others = Math.max(0, verdict.blocked - 1)\n const more = others ? `, and ${others.toLocaleString('en-US')} other ${others === 1 ? 'page' : 'pages'}` : ''\n const lead = `Your site’s bot protection or firewall turned AglynBot away before it could read your home page${more}. ${NOTHING_SPENT} Allow AglynBot in your site’s bot or firewall settings, then choose Check again.`\n return verdict.edge ? `${lead} ${AI_SITE_IMPORT_EDGE_HINTS[verdict.edge]}` : lead\n }\n if (verdict.kind === 'home-unreadable') {\n switch (verdict.homeProblem) {\n case 'robots':\n return `Your site’s robots.txt asks AglynBot not to read your home page. ${NOTHING_SPENT} Allow AglynBot in your robots.txt, then choose Check again.`\n case 'login-form':\n return `Your home page asks for a password, and pages that do are never brought in. ${NOTHING_SPENT} Open your home page to visitors, then choose Check again.`\n case 'empty':\n return `No text could be read from your home page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet. ${NOTHING_SPENT}`\n default:\n return `Your home page could not be read (${homeProblemText(verdict)}). ${NOTHING_SPENT} Check that ${domain} is up and answers at https://${domain}/, then choose Check again.`\n }\n }\n if (verdict.kind === 'mostly-blocked') {\n return `${verdict.blocked.toLocaleString('en-US')} of ${verdict.total.toLocaleString('en-US')} pages were turned away by your site’s bot protection; see Not brought in.`\n }\n return null\n}\n\n/** The verdict an inventory reads from its own pages and skips, when the step did not hand it the crawl's. */\nfunction inventoryVerdict(input: Pick<AiSiteImportInventoryInput, 'pages' | 'skipped' | 'skippedOverflow'>): AiSiteImportReadVerdict {\n const blocked = input.skipped.filter((entry) => entry.reason === 'blocked').length\n const total = input.pages.length + input.skipped.length + Math.max(0, input.skippedOverflow ?? 0)\n const page = input.pages.find((entry) => entry.foundBy === 'home') ?? input.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const empty = page.words < AI_SITE_IMPORT_MIN_WORDS && page.images === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = input.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n const problem: AiSiteImportHomeProblem = !skip\n ? 'not-reached'\n : skip.reason === 'blocked'\n ? 'blocked'\n : skip.reason === 'robots' || skip.reason === 'login-form' || skip.reason === 'too-large' || skip.reason === 'off-site' || skip.reason === 'not-reached' || skip.reason === 'empty'\n ? skip.reason\n : 'unreachable'\n return readVerdictOf({ problem, status: null, edge: null }, blocked, total)\n}\n\n/*------------------------------------------\n * Grouping and waves\n *-----------------------------------------*/\n\nconst clipLabel = (text: string) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > AI_SITE_IMPORT_LABEL_MAX_CHARS\n ? `${line.slice(0, AI_SITE_IMPORT_LABEL_MAX_CHARS - 1).trimEnd()}…`\n : line\n}\n\nconst segmentsOf = (path: string) => String(path ?? '').split('/').filter(Boolean)\n\n/** A collection's prefix for a page address: its first segment, a dated archive's `posts`; `null` for a top-level page. */\nexport function aiSiteImportPrefix(path: string): string | null {\n const segments = segmentsOf(path)\n if (segments.length < 2) return null\n const first = segments[0].toLowerCase()\n return /^\\d{4}$/.test(first) ? 'posts' : first\n}\n\n/** \"news-and-events\" → \"News and events\". */\nexport function aiSiteImportPrefixLabel(prefix: string): string {\n const words = decodeURIComponent(prefix).replace(/[-_+]+/g, ' ').trim()\n return words ? `${words.charAt(0).toUpperCase()}${words.slice(1)}` : 'Pages'\n}\n\n/**\n * The inventory's groups, in the order they are brought in: the main pages,\n * then each collection — one the home page's menu links first, in menu order,\n * then the larger before the smaller.\n */\nexport function aiSiteImportGroups(\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[] = [],\n): { groups: AiSiteImportGroup[]; groupOf: (page: AiSiteImportListedPage) => string } {\n const counts = new Map<string, number>()\n for (const page of pages) {\n const prefix = aiSiteImportPrefix(page.path)\n if (prefix) counts.set(prefix, (counts.get(prefix) ?? 0) + 1)\n }\n const menuPaths = new Set(menu.map((path) => path.replace(/\\/+$/, '') || '/'))\n const collections = new Set([...counts].filter(([, count]) => count >= AI_SITE_IMPORT_COLLECTION_MIN).map(([prefix]) => prefix))\n const groupOf = (page: AiSiteImportListedPage) => {\n const path = page.path.replace(/\\/+$/, '') || '/'\n if (page.foundBy === 'home' || menuPaths.has(path)) return 'main'\n const prefix = aiSiteImportPrefix(page.path)\n return prefix && collections.has(prefix) ? `c:${prefix}` : 'main'\n }\n const menuRank = (prefix: string) => {\n const index = menu.findIndex((path) => {\n const first = segmentsOf(path)[0]?.toLowerCase()\n return first === prefix\n })\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n const sized = new Map<string, number>()\n for (const page of pages) sized.set(groupOf(page), (sized.get(groupOf(page)) ?? 0) + 1)\n const ordered = [...collections]\n .filter((prefix) => (sized.get(`c:${prefix}`) ?? 0) > 0)\n .sort((a, b) => menuRank(a) - menuRank(b) || (sized.get(`c:${b}`) ?? 0) - (sized.get(`c:${a}`) ?? 0) || a.localeCompare(b))\n const groups: AiSiteImportGroup[] = []\n if ((sized.get('main') ?? 0) > 0) groups.push({ id: 'main', label: 'Main pages', kind: 'main', pages: sized.get('main') ?? 0 })\n for (const prefix of ordered) {\n groups.push({ id: `c:${prefix}`, label: aiSiteImportPrefixLabel(prefix), kind: 'collection', pages: sized.get(`c:${prefix}`) ?? 0 })\n }\n return { groups, groupOf }\n}\n\n/**\n * The main pages in the order a visitor meets them: home, then the menu's\n * order, then the rest as the crawl read them. A collection keeps the crawl's\n * order, which is its sitemap's.\n */\nfunction orderedWithin(\n group: string,\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[],\n): AiSiteImportListedPage[] {\n if (group !== 'main') return [...pages]\n const rank = (page: AiSiteImportListedPage) => {\n if (page.foundBy === 'home' || segmentsOf(page.path).length === 0) return -1\n const path = page.path.replace(/\\/+$/, '') || '/'\n const index = menu.findIndex((entry) => (entry.replace(/\\/+$/, '') || '/') === path)\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n return pages\n .map((page, order) => ({ page, order }))\n .sort((a, b) => rank(a.page) - rank(b.page) || a.order - b.order)\n .map(({ page }) => page)\n}\n\n/**\n * The address a page takes on the new site: one segment, as every Aglyn page\n * has, from the last segment of its old address; home is the root. Two pages\n * that would take the same address are told apart by their collection, then\n * by a number. The draft writer still makes it unique on the site itself.\n */\nexport function aiSiteImportSlug(path: string, taken: Set<string>): string {\n const segments = segmentsOf(path)\n if (!segments.length) {\n if (!taken.has('/')) {\n taken.add('/')\n return '/'\n }\n }\n const clean = (value: string) =>\n decodeURIComponent(value)\n .toLowerCase()\n .replace(/\\.(?:html?|php|aspx?)$/, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const last = clean(segments[segments.length - 1] ?? '') || 'page'\n const prefixed = segments.length > 1 ? clean(`${segments[0]}-${segments[segments.length - 1]}`) : last\n for (const candidate of [last, prefixed]) {\n if (candidate && !taken.has(candidate)) {\n taken.add(candidate)\n return candidate\n }\n }\n let attempt = 2\n while (taken.has(`${last}-${attempt}`)) attempt += 1\n const slug = `${last}-${attempt}`\n taken.add(slug)\n return slug\n}\n\nexport interface AiSiteImportInventoryInput {\n domain: string\n siteName: string\n ownership: AiSiteImportState['ownership']\n readAt: string\n pages: readonly AiSiteImportListedPage[]\n /** Addresses the home page's menu links, in its order. */\n menu: readonly string[]\n /** What the crawl could not read, by reason. */\n skipped: ReadonlyArray<{ url: string; reason: AiSiteImportLeftOutReason }>\n /** Skips the crawl only counted. */\n skippedOverflow?: number\n stoppedBy: string | null\n /**\n * The read's verdict from the crawl itself (`aiSiteImportReadVerdict`), with\n * the status and edge the home page answered with; read from `pages` and\n * `skipped` when absent.\n */\n verdict?: AiSiteImportReadVerdict | null\n /** The plan's page count; `null` for none. */\n pageLimit: number | null\n existingPages: number\n /** Mints one draft id; the inventory names every page's draft before any is written. */\n mintId: () => string\n wavePages?: number\n}\n\n/**\n * The site's inventory cut into waves, every page left out said with its\n * reason. Pure: the step reads the site, this decides what comes in.\n */\nexport function aiSiteImportInventory(input: AiSiteImportInventoryInput): AiSiteImportState {\n const wavePages = Math.max(1, Math.floor(input.wavePages ?? AI_SITE_IMPORT_WAVE_PAGES))\n const limit = input.pageLimit === null || !Number.isFinite(input.pageLimit) ? null : Math.max(0, Math.floor(input.pageLimit))\n const leftOut: AiSiteImportLeftOut[] = []\n const leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>> = {}\n const leave = (entry: AiSiteImportLeftOut) => {\n leftOutCount[entry.reason] = (leftOutCount[entry.reason] ?? 0) + 1\n if (leftOut.length < AI_SITE_IMPORT_LEFT_OUT_LISTED) leftOut.push(entry)\n }\n // The read asks for a wave's worth past the plan's room, so a page its page\n // budget never reached is past the plan; any other stop is not the plan's.\n const unreached: AiSiteImportLeftOutReason = input.stoppedBy === 'page-budget' && limit !== null ? 'plan-limit' : 'not-reached'\n for (const skipped of input.skipped) {\n let path = '/'\n try {\n path = new URL(skipped.url).pathname\n } catch {\n // A malformed address keeps the root as its path.\n }\n leave({ url: skipped.url, path, title: '', reason: skipped.reason === 'not-reached' ? unreached : skipped.reason })\n }\n if (input.skippedOverflow) leftOutCount['unreadable'] = (leftOutCount['unreadable'] ?? 0) + input.skippedOverflow\n\n // Home first: a site read without its home page plans nothing at all.\n const verdict = input.verdict ?? inventoryVerdict(input)\n if (aiSiteImportReadStops(verdict)) {\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups: [],\n pages: [],\n waves: [],\n leftOut,\n leftOutCount,\n frame: null,\n verdict,\n }\n }\n\n const readable = input.pages.filter((page) => {\n if (page.words >= AI_SITE_IMPORT_MIN_WORDS || page.images > 0) return true\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'empty' })\n return false\n })\n const { groups, groupOf } = aiSiteImportGroups(readable, input.menu)\n let room = limit === null ? Number.POSITIVE_INFINITY : Math.max(0, limit - Math.max(0, input.existingPages))\n const pages: AiSiteImportPage[] = []\n const waves: AiSiteImportWave[] = []\n const taken = new Set<string>()\n for (const group of groups) {\n const members = orderedWithin(group.id, readable.filter((page) => groupOf(page) === group.id), input.menu)\n const kept: AiSiteImportListedPage[] = []\n for (const page of members) {\n if (room > 0) {\n kept.push(page)\n room -= 1\n } else {\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'plan-limit' })\n }\n }\n const parts = Math.ceil(kept.length / wavePages)\n for (let part = 0; part < parts; part += 1) {\n const index = waves.length\n const slice = kept.slice(part * wavePages, (part + 1) * wavePages)\n const slots: string[] = []\n for (const page of slice) {\n const slot = `p${pages.length}`\n slots.push(slot)\n pages.push({\n slot,\n url: page.url,\n path: page.path,\n title: clipLabel(page.title) || aiSiteImportPrefixLabel(segmentsOf(page.path).pop() ?? 'Home'),\n group: group.id,\n wave: index,\n draftId: input.mintId(),\n slug: aiSiteImportSlug(page.path, taken),\n words: page.words,\n images: page.images,\n })\n }\n waves.push({\n index,\n label: parts > 1 ? `${group.label}, ${part + 1} of ${parts}` : group.label,\n group: group.id,\n slots,\n frame: index === 0 && group.kind === 'main',\n status: 'waiting',\n startedAt: null,\n finishedAt: null,\n })\n }\n }\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups,\n pages,\n waves,\n leftOut,\n leftOutCount,\n frame: waves[0]?.frame ? { draftId: input.mintId() } : null,\n verdict,\n }\n}\n\n/** The item ledger an inventory starts: the header and footer, then every page, all pending. */\nexport function aiSiteImportLedger(state: AiSiteImportState): AiJobItemLedger[] {\n const row = (slot: string, op: string, label: string): AiJobItemLedger => ({\n slot,\n op,\n label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n creditsRefunded: 0,\n outputs: [],\n })\n return [\n ...(state.frame ? [row(AI_SITE_IMPORT_FRAME_SLOT, 'layout', 'Header and footer')] : []),\n ...state.pages.map((page) => row(page.slot, 'page', page.title)),\n ]\n}\n\n/*------------------------------------------\n * What runs next\n *-----------------------------------------*/\n\nexport type AiSiteImportWork =\n | { kind: 'inventory' }\n | { kind: 'start-wave'; wave: AiSiteImportWave }\n /** Pages of the running wave its read did not reach before its clock ran out. */\n | { kind: 'read'; wave: AiSiteImportWave; pages: AiSiteImportPage[] }\n | { kind: 'frame'; wave: AiSiteImportWave }\n | { kind: 'images'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'page'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'close-wave'; wave: AiSiteImportWave }\n | { kind: 'done' }\n\nconst OPEN = new Set(['pending', 'running'])\n\n/** Whether an item is still to be built. */\nexport function aiSiteImportItemOpen(items: readonly AiJobItemLedger[] | null | undefined, slot: string): boolean {\n const row = (items ?? []).find((entry) => entry.slot === slot)\n return Boolean(row && OPEN.has(row.status))\n}\n\n/** The wave running now, else `null`. */\nexport function aiSiteImportRunningWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'running') ?? null\n}\n\n/** The next wave a person may start, else `null`. */\nexport function aiSiteImportNextWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n if (!state || aiSiteImportRunningWave(state) || aiSiteImportReadStops(state.verdict)) return null\n return state.waves.find((wave) => wave.status === 'waiting') ?? null\n}\n\n/** The wave a person is reviewing, else `null`. */\nexport function aiSiteImportReviewWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'review') ?? null\n}\n\n/** What the next pass of the step does. */\nexport function aiSiteImportNextWork(\n state: AiSiteImportState | null | undefined,\n items: readonly AiJobItemLedger[] | null | undefined,\n): AiSiteImportWork {\n // A read that stopped at its home page reads again: the person's Check again.\n if (!state || aiSiteImportReadStops(state.verdict)) return { kind: 'inventory' }\n const running = aiSiteImportRunningWave(state)\n if (!running) {\n const next = aiSiteImportNextWave(state)\n return next ? { kind: 'start-wave', wave: next } : { kind: 'done' }\n }\n // A page the wave's read has not reached yet is read before anything is built.\n const unread = running.slots\n .map((slot) => state.pages.find((page) => page.slot === slot))\n .filter((page): page is AiSiteImportPage => !!page && !page.read && !page.refused && aiSiteImportItemOpen(items, page.slot))\n if (unread.length) return { kind: 'read', wave: running, pages: unread }\n if (running.frame && state.frame && aiSiteImportItemOpen(items, AI_SITE_IMPORT_FRAME_SLOT)) {\n return { kind: 'frame', wave: running }\n }\n for (const slot of running.slots) {\n if (!aiSiteImportItemOpen(items, slot)) continue\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page) continue\n return page.photos ? { kind: 'page', wave: running, page } : { kind: 'images', wave: running, page }\n }\n return { kind: 'close-wave', wave: running }\n}\n\n/*------------------------------------------\n * The state, one pass at a time\n *-----------------------------------------*/\n\nconst withPage = (state: AiSiteImportState, slot: string, patch: Partial<AiSiteImportPage>): AiSiteImportState => ({\n ...state,\n pages: state.pages.map((page) => (page.slot === slot ? { ...page, ...patch } : page)),\n})\n\nconst withWave = (state: AiSiteImportState, index: number, patch: Partial<AiSiteImportWave>): AiSiteImportState => ({\n ...state,\n waves: state.waves.map((wave) => (wave.index === index ? { ...wave, ...patch } : wave)),\n})\n\n/** A wave started: running, with what its read found on each page. */\nexport function aiSiteImportWaveStarted(\n state: AiSiteImportState,\n index: number,\n at: string,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n // The wave reviewed before this one is done: starting the next is the person's say-so.\n const next = withWave(\n { ...state, waves: state.waves.map((wave) => (wave.status === 'review' ? { ...wave, status: 'done' as const } : wave)) },\n index,\n { status: 'running', startedAt: at, finishedAt: null },\n )\n return aiSiteImportPagesRead(next, reads)\n}\n\n/** How many characters a block holds, as the job document stores it. */\nexport function aiSiteImportBlockChars(block: SiteImportBlock): number {\n if (block.kind === 'image') return block.url.length + block.alt.length\n return block.text.length + (block.kind === 'link' ? block.href.length : 0)\n}\n\n/** A page's blocks in order, as many as fit `maxChars` and `maxBlocks`, and whether any had to go. */\nexport function aiSiteImportCapBlocks(\n blocks: readonly SiteImportBlock[],\n maxChars: number,\n maxBlocks: number = AI_SITE_IMPORT_READ_MAX_BLOCKS,\n): { blocks: SiteImportBlock[]; chars: number; cut: boolean } {\n const kept: SiteImportBlock[] = []\n let chars = 0\n for (const block of blocks) {\n const size = aiSiteImportBlockChars(block)\n if (kept.length >= maxBlocks || chars + size > maxChars) return { blocks: kept, chars, cut: true }\n kept.push(block)\n chars += size\n }\n return { blocks: kept, chars, cut: false }\n}\n\nconst readChars = (read: AiSiteImportPageRead | null | undefined) =>\n (read?.blocks ?? []).reduce((total, block) => total + aiSiteImportBlockChars(block), 0) + siteImportLayoutChars(read?.layout)\n\n/**\n * A page's layout in the room the wave has left after its blocks (AGL-4106):\n * cut to the blocks kept, its drawings dropped when it would not fit with\n * them, and left off when it would not fit at all (the page is then laid out\n * from its lines alone).\n */\nfunction layoutWithin(layout: SiteImportLayout | null | undefined, blocks: number, left: number): SiteImportLayout | null {\n if (!layout) return null\n const room = Math.min(left, AI_SITE_IMPORT_LAYOUT_MAX_CHARS)\n const cut = siteImportLayoutWithin(layout, blocks)\n if (siteImportLayoutChars(cut) <= room) return cut\n const light = siteImportLayoutWithoutDrawings(cut)\n return siteImportLayoutChars(light) <= room ? light : null\n}\n\n/**\n * More of a running wave's pages, read now. Their blocks share\n * `AI_SITE_IMPORT_WAVE_READ_MAX_CHARS` with every read still held: a page\n * past it keeps what fits, in order, and is marked `cut`.\n */\nexport function aiSiteImportPagesRead(\n state: AiSiteImportState,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n let held = state.pages.filter((page) => !reads.has(page.slot)).reduce((total, page) => total + readChars(page.read), 0)\n let next = state\n for (const [slot, read] of reads) {\n if (typeof read === 'string') {\n next = withPage(next, slot, { refused: read, read: null })\n continue\n }\n let kept = read\n if (read.blocks) {\n const room = Math.max(0, Math.min(AI_SITE_IMPORT_READ_MAX_CHARS, AI_SITE_IMPORT_WAVE_READ_MAX_CHARS - held))\n const capped = aiSiteImportCapBlocks(read.blocks, room)\n held += capped.chars\n kept = { ...read, blocks: capped.blocks, ...(capped.cut || read.cut ? { cut: true } : {}) }\n if (read.layout) {\n const layout = layoutWithin(read.layout, capped.blocks.length, Math.max(0, AI_SITE_IMPORT_WAVE_READ_MAX_CHARS - held))\n held += siteImportLayoutChars(layout)\n kept = { ...kept, layout }\n }\n }\n next = withPage(next, slot, { read: kept, refused: null, title: clipLabel(read.title) || next.pages.find((page) => page.slot === slot)?.title || '' })\n }\n return next\n}\n\n/** A page's photos are in the library. */\nexport function aiSiteImportPhotosIn(\n state: AiSiteImportState,\n slot: string,\n photos: NonNullable<AiSiteImportPage['photos']>,\n): AiSiteImportState {\n return withPage(state, slot, { photos })\n}\n\n/** A page is in: its text and its blocks are no longer kept. */\nexport function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page?.read) return state\n return withPage(state, slot, { read: { ...page.read, text: '', blocks: [], layout: null } })\n}\n\n/** A wave is in: waiting for its review, or done when no wave follows it. */\nexport function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState {\n const later = state.waves.some((wave) => wave.index > index && wave.status === 'waiting')\n // Start every remaining wave: no review between waves, the last one's close is the stop.\n let next = withWave(state, index, { status: later && !state.autoContinue ? 'review' : 'done', finishedAt: at })\n // What the wave read is no longer needed by any model answer.\n for (const slot of state.waves.find((wave) => wave.index === index)?.slots ?? []) next = aiSiteImportPageSettled(next, slot)\n return next\n}\n\n/**\n * The state of an import a person canceled (AGL-4107): every wave settled. A\n * wave that ran, or was waiting to, is stopped; one already in, for review or\n * done, is in. On 10/29 a canceled import still showed its second wave as\n * running, with nothing running it.\n */\nexport function aiSiteImportCanceled(state: AiSiteImportState, at: string): AiSiteImportState {\n return {\n ...state,\n autoContinue: false,\n waves: state.waves.map((wave): AiSiteImportWave => {\n if (wave.status === 'running' || wave.status === 'waiting') return { ...wave, status: 'stopped', finishedAt: wave.status === 'running' ? at : wave.finishedAt }\n if (wave.status === 'review') return { ...wave, status: 'done' }\n return wave\n }),\n }\n}\n\n/** The state as the console reads it: every page's read text left off. */\nexport function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null {\n if (!state) return null\n return {\n ...state,\n pages: state.pages.map((page) =>\n page.read ? { ...page, read: { ...page.read, text: '', images: [], blocks: [], forms: [], layout: null } } : page,\n ),\n }\n}\n\n/*------------------------------------------\n * Credits\n *-----------------------------------------*/\n\n/**\n * How a page is built (AGL-4083), decided by code: every page of a collection\n * (a blog, the legal pages, news) is copied as it is; every main page is\n * arranged by Aglyn AI whatever its length (AGL-4106), its words kept as\n * written. Both are laid out from the source's own layout.\n */\nexport function aiSiteImportPageBuild(page: Pick<AiSiteImportPage, 'group' | 'words'>): AiSiteImportBuild {\n return page.group !== 'main' ? 'copied' : 'arranged'\n}\n\nconst whole = (value: number | undefined) => Math.max(0, Math.floor(Number.isFinite(value) ? (value as number) : 0))\n\n/**\n * What bringing in this many pages costs, quoted before it starts (AGL-3976):\n * each page an AI page as measured, its ceiling its two passes, and the\n * site's header and footer once when the wave builds them. Pages the step has\n * chosen a build for (AGL-4083) are priced by it: `arranged` at the\n * arrangement answer and its listing, `copied` at the listing alone.\n */\nexport function aiSiteImportCreditRange(\n pages: number,\n options: { frame?: boolean; arranged?: number; copied?: number } = {},\n): AiCreditRange {\n const count = whole(pages)\n const arranged = whole(options.arranged)\n const copied = whole(options.copied)\n const measured = AI_MEASURED_IMPORT_CREDITS\n return aiCreditRangeOrdered({\n likely:\n count * measured.page.median + arranged * measured.arranged.median + copied * measured.copied.median + (options.frame ? measured.frame.median : 0),\n p90: count * measured.page.p90 + arranged * measured.arranged.p90 + copied * measured.copied.p90 + (options.frame ? measured.frame.p90 : 0),\n ceiling:\n (count + arranged) * AI_SITE_IMPORT_PAGE_CEILING_CREDITS +\n copied * AI_SITE_IMPORT_COPIED_CEILING_CREDITS +\n (options.frame ? AI_SITE_IMPORT_FRAME_CEILING_CREDITS : 0),\n })\n}\n\n/** What a wave still owes, by how its pages are built, and whether its header and footer are owed. */\nexport function aiSiteImportWaveOwed(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): { pages: number; arranged: number; copied: number; frame: boolean } {\n const owed = (slot: string) => (items ? aiSiteImportItemOpen(items, slot) : true)\n // A header and footer drawn from the source's own menu ask no model (AGL-4108), so are owed nothing.\n const counts = { pages: 0, arranged: 0, copied: 0, frame: wave.frame && !!state.frame && !aiSiteImportFrameIsRead(state) && owed(AI_SITE_IMPORT_FRAME_SLOT) }\n for (const slot of wave.slots) {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!owed(slot) || page?.refused) continue\n if (page?.build === 'arranged') counts.arranged += 1\n else if (page?.build === 'copied') counts.copied += 1\n else counts.pages += 1\n }\n return counts\n}\n\n/** A wave's quote: the pages it has still to bring in, and its header and footer while they are owed. */\nexport function aiSiteImportWaveCreditRange(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const owed = aiSiteImportWaveOwed(state, wave, items)\n return aiSiteImportCreditRange(owed.pages, { frame: owed.frame, arranged: owed.arranged, copied: owed.copied })\n}\n\n/** Every wave still waiting, quoted at once: \"Start every remaining wave\" (AGL-4083). */\nexport function aiSiteImportRemainingCreditRange(\n state: AiSiteImportState,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const total = { pages: 0, arranged: 0, copied: 0, frame: false }\n for (const wave of state.waves) {\n if (wave.status !== 'waiting') continue\n const owed = aiSiteImportWaveOwed(state, wave, items)\n total.pages += owed.pages\n total.arranged += owed.arranged\n total.copied += owed.copied\n total.frame = total.frame || owed.frame\n }\n return aiSiteImportCreditRange(total.pages, { frame: total.frame, arranged: total.arranged, copied: total.copied })\n}\n\n/** \"About 95 credits (up to 600): 7 pages arranged by Aglyn AI, 3 copied as they are\"; the build is named once the step has chosen. */\nexport function aiSiteImportQuoteText(\n range: AiCreditRange,\n owed: Pick<ReturnType<typeof aiSiteImportWaveOwed>, 'arranged' | 'copied'>,\n): string {\n const parts: string[] = []\n if (owed.arranged) parts.push(`${aiSiteImportPagesText(owed.arranged)} arranged by ${PLATFORM_BRAND_NAME} AI`)\n if (owed.copied) parts.push(`${owed.arranged ? owed.copied.toLocaleString('en-US') : aiSiteImportPagesText(owed.copied)} copied as ${owed.copied === 1 ? 'it is' : 'they are'}`)\n return parts.length ? `${aiCreditRangeText(range)}: ${parts.join(', ')}` : aiCreditRangeText(range)\n}\n\n/*------------------------------------------\n * Words\n *-----------------------------------------*/\n\n/** Why a page is not brought in, as a person reads it. */\nexport const AI_SITE_IMPORT_LEFT_OUT_COPY: Readonly<Record<AiSiteImportLeftOutReason, string>> = {\n 'plan-limit': 'Past your plan’s page count. Upgrade for more pages, then bring the rest in.',\n robots: 'The site’s robots.txt asks AglynBot not to read it.',\n 'login-form': 'A sign-in page. Pages that ask for a password are never brought in.',\n empty:\n 'No text could be read from this page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet.',\n unreadable: 'It could not be read just now.',\n blocked:\n 'The site’s bot protection or firewall turned AglynBot away. Allow AglynBot in your site’s bot or firewall settings, then bring the site in again.',\n 'too-large': 'Larger than a page can be to bring in.',\n 'off-site': 'It sends visitors to another website.',\n 'not-reached': 'Not reached: the read stopped before it got to this page.',\n file: 'A file, not a page.',\n}\n\n/** \"3 pages\" / \"1 page\". */\nexport function aiSiteImportPagesText(count: number): string {\n return `${count.toLocaleString('en-US')} ${count === 1 ? 'page' : 'pages'}`\n}\n\n/** The review a finished wave parks on, or the first wave's go-ahead once the site is read. */\nexport function aiSiteImportWaveMessage(\n state: AiSiteImportState,\n items: readonly AiJobItemLedger[] | null | undefined,\n): string {\n // A read that stopped at its home page says why, and what to do.\n if (aiSiteImportReadStops(state.verdict)) return aiSiteImportReadVerdictMessage(state.verdict, state.domain) ?? ''\n const next = aiSiteImportNextWave(state)\n const reviewing = aiSiteImportReviewWave(state)\n if (!reviewing) {\n const kept = state.pages.length\n return next\n ? `Your site is read: ${aiSiteImportPagesText(kept)} to bring in, in ${state.waves.length === 1 ? 'one wave' : `${state.waves.length} waves`}. Start the first wave when you are ready.`\n : 'Your site is read, and nothing on it can be brought in.'\n }\n const rows = (items ?? []).filter((row) => reviewing.slots.includes(row.slot))\n const done = rows.filter((row) => row.status === 'succeeded' || row.status === 'degraded').length\n const lead = `${reviewing.label} is in: ${aiSiteImportPagesText(done)} ready to review as drafts.`\n return next ? `${lead} Start ${next.label} when you are ready.` : lead\n}\n"],"names":["PLATFORM_BRAND_NAME","aiCreditRangeOrdered","aiCreditRangeText","AI_MEASURED_IMPORT_CREDITS","AI_SITE_IMPORT_KIND","AI_SITE_IMPORT_DOMAIN_INPUT","AI_SITE_IMPORT_WAVE_PAGES","AI_SITE_IMPORT_MAX_PAGES","AI_SITE_IMPORT_COLLECTION_MIN","AI_SITE_IMPORT_IMAGES_PER_PAGE","AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS","AI_SITE_IMPORT_MAX_SECTIONS","AI_SITE_IMPORT_READ_MAX_BLOCKS","AI_SITE_IMPORT_READ_MAX_CHARS","AI_SITE_IMPORT_LAYOUT_MAX_CHARS","AI_SITE_IMPORT_WAVE_READ_MAX_CHARS","AI_SITE_IMPORT_PROSE_WORDS","AI_SITE_IMPORT_BLOCKED_STATUSES","Set","AI_SITE_IMPORT_LABEL_MAX_CHARS","AI_SITE_IMPORT_MIN_WORDS","AI_SITE_IMPORT_FRAME_SLOT","AI_SITE_IMPORT_PAGE_CEILING_CREDITS","AI_SITE_IMPORT_COPIED_CEILING_CREDITS","AI_SITE_IMPORT_FRAME_CEILING_CREDITS","AI_SITE_IMPORT_LEFT_OUT_LISTED","AI_SITE_IMPORT_LEFT_OUT_REASONS","AI_SITE_IMPORT_UNREAD_REASONS","SITE_IMPORT_GROUP_KINDS","siteImportLayoutLines","nodes","out","visit","list","node","kind","push","line","cell","cells","item","items","lines","row","rows","tab","tabs","label","panel","siteImportLayoutChars","layout","JSON","stringify","length","siteImportLayoutWithin","count","keep","ref","sections","map","section","siteImportFilterNodes","filter","flatMap","some","trimItem","icon","undefined","key","siteImportLayoutWithoutDrawings","icons","svg","viewBox","aiSiteImportFrameIsRead","state","nav","home","ctas","pathOfUrl","url","URL","pathname","replace","wordsOf","text","match","readVerdictOf","blocked","total","edge","base","homeStatus","status","homeProblem","problem","homeProblemOfSkip","reason","has","aiSiteImportReadVerdict","crawled","skip","skipped","entry","Math","max","sitemapUrls","pages","page","find","foundBy","path","words","blocks","reduce","sum","block","inline","empty","images","aiSiteImportReadStops","verdict","aiSiteImportReadVerdictReason","AI_SITE_IMPORT_EDGE_HINTS","vercel","cloudflare","NOTHING_SPENT","homeProblemText","aiSiteImportReadVerdictMessage","domain","others","more","toLocaleString","lead","inventoryVerdict","input","skippedOverflow","clipLabel","String","trim","slice","trimEnd","segmentsOf","split","Boolean","aiSiteImportPrefix","segments","first","toLowerCase","test","aiSiteImportPrefixLabel","prefix","decodeURIComponent","charAt","toUpperCase","aiSiteImportGroups","menu","sized","counts","Map","set","get","menuPaths","collections","groupOf","menuRank","index","findIndex","Number","POSITIVE_INFINITY","ordered","sort","a","b","localeCompare","groups","id","orderedWithin","group","rank","order","aiSiteImportSlug","taken","add","clean","value","last","prefixed","candidate","attempt","slug","aiSiteImportInventory","leftOutCount","waves","wavePages","floor","limit","pageLimit","isFinite","leftOut","leave","unreached","stoppedBy","title","siteName","ownership","readAt","existingPages","found","frame","readable","room","members","kept","parts","ceil","part","slots","slot","pop","wave","draftId","mintId","startedAt","finishedAt","aiSiteImportLedger","op","creditsSpent","creditsRefunded","outputs","OPEN","aiSiteImportItemOpen","aiSiteImportRunningWave","aiSiteImportNextWave","aiSiteImportReviewWave","aiSiteImportNextWork","running","next","unread","read","refused","photos","withPage","patch","withWave","aiSiteImportWaveStarted","at","reads","aiSiteImportPagesRead","aiSiteImportBlockChars","alt","href","aiSiteImportCapBlocks","maxChars","maxBlocks","chars","size","cut","readChars","layoutWithin","left","min","light","held","capped","aiSiteImportPhotosIn","aiSiteImportPageSettled","aiSiteImportWaveClosed","later","autoContinue","aiSiteImportCanceled","aiSiteImportWire","forms","aiSiteImportPageBuild","whole","aiSiteImportCreditRange","options","arranged","copied","measured","likely","median","p90","ceiling","aiSiteImportWaveOwed","owed","build","aiSiteImportWaveCreditRange","aiSiteImportRemainingCreditRange","aiSiteImportQuoteText","range","aiSiteImportPagesText","join","AI_SITE_IMPORT_LEFT_OUT_COPY","robots","unreadable","file","aiSiteImportWaveMessage","reviewing","includes","done"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import type { HostTheme, HostThemeSchemeColors } from '@aglyn/shared-data-types';
|
|
17
|
+
import type { HostTheme, HostThemeFontCategory, HostThemeSchemeColors } from '@aglyn/shared-data-types';
|
|
18
18
|
import { type AiSiteMotionStyle } from '../layout-language/ai-layout-motion';
|
|
19
19
|
import type { AiTool } from '../runtime/ai-runtime';
|
|
20
20
|
import { type AiSiteBase, type AiSiteButtons, type AiSiteChroma, type AiSiteCorners, type AiSiteDensity, type AiSiteGround, type AiSiteKind, type AiSiteRhythm } from './ai-site-kinds';
|
|
@@ -92,7 +92,33 @@ export interface AiSiteStyle {
|
|
|
92
92
|
* pages move in its kind's mood.
|
|
93
93
|
*/
|
|
94
94
|
motion?: AiSiteMotionStyle;
|
|
95
|
+
/**
|
|
96
|
+
* A site brought in's own page ground (AGL-4108), `#rrggbb`: the color its
|
|
97
|
+
* source's pages sit on, where it is light enough for the theme's text.
|
|
98
|
+
* Absent, the ground is drawn as `ground` says.
|
|
99
|
+
*/
|
|
100
|
+
page?: string;
|
|
101
|
+
/**
|
|
102
|
+
* A site brought in's own type (AGL-4108): the families its source sets
|
|
103
|
+
* its body and its headings in, where the theme font catalog has them,
|
|
104
|
+
* loaded in place of the pairing's. The pairing still sets the weights and
|
|
105
|
+
* the scale they are drawn at. Absent on every other site, whose type is
|
|
106
|
+
* the pairing's alone.
|
|
107
|
+
*/
|
|
108
|
+
sourceFonts?: {
|
|
109
|
+
body: AiSiteSourceFont;
|
|
110
|
+
heading: AiSiteSourceFont | null;
|
|
111
|
+
} | null;
|
|
112
|
+
}
|
|
113
|
+
/** A family a site brought in is set in (AGL-4108), with the weights the catalog says it has. */
|
|
114
|
+
export interface AiSiteSourceFont {
|
|
115
|
+
family: string;
|
|
116
|
+
category: HostThemeFontCategory;
|
|
117
|
+
/** The upright weights the family offers. */
|
|
118
|
+
weights: number[];
|
|
95
119
|
}
|
|
120
|
+
/** The weights of `wanted` a family offers, each the nearest it has where it lacks one. */
|
|
121
|
+
export declare function aiSiteFontWeightsIn(wanted: readonly number[], offered: readonly number[]): number[];
|
|
96
122
|
/** What the model may choose, every field optional. */
|
|
97
123
|
export type AiSiteLookAnswer = Partial<Pick<AiSiteStyle, 'base' | 'hue' | 'accent' | 'ground' | 'fonts' | 'corners' | 'buttons' | 'cards' | 'fields' | 'eyebrow' | 'header' | 'brand' | 'motion'>>;
|
|
98
124
|
/**
|
|
@@ -188,3 +214,41 @@ export declare const AI_SITE_DISPLAY_BOOST: Readonly<Record<string, {
|
|
|
188
214
|
* and dialog styles, its control heights — stays the base's.
|
|
189
215
|
*/
|
|
190
216
|
export declare function aiSiteTheme(style: AiSiteStyle, base: HostTheme): HostTheme;
|
|
217
|
+
/** A kind's first look as a built-in theme with a brand over it, and the style answers that say the same. */
|
|
218
|
+
export interface AiSiteKindDefaultLook {
|
|
219
|
+
/** The `builtin` starter theme a site created in this style starts with. */
|
|
220
|
+
theme: {
|
|
221
|
+
kind: 'builtin';
|
|
222
|
+
base: string;
|
|
223
|
+
brand: {
|
|
224
|
+
light: Record<string, string>;
|
|
225
|
+
dark?: Record<string, string>;
|
|
226
|
+
};
|
|
227
|
+
fonts: string | null;
|
|
228
|
+
};
|
|
229
|
+
/** The same look as the AI start's style answers name it (`fp-starter-{kind}-ai`). */
|
|
230
|
+
answerStyle: {
|
|
231
|
+
base: string;
|
|
232
|
+
hue?: number;
|
|
233
|
+
ground: AiSiteGround;
|
|
234
|
+
fonts: string | null;
|
|
235
|
+
corners: AiSiteCorners;
|
|
236
|
+
buttons: AiSiteButtons;
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A kind of site's FIRST look (AGL-4049), the one a site created in that
|
|
241
|
+
* style starts with and the one the platform's own starter listings carry:
|
|
242
|
+
* the kind's first base theme the deployment has (`starter`, or a built-in
|
|
243
|
+
* whose preset `known.presetIds` holds), its first font pairing the platform
|
|
244
|
+
* has, and a brand primary from the midpoint of its first hue range, colored
|
|
245
|
+
* as {@link aiSiteColors} colors one. A kind that suits any hue keeps the
|
|
246
|
+
* base's own colors. One derivation, so the style a person picks in Create
|
|
247
|
+
* site and the seeded `fp-starter-{kind}-base` listing look the same.
|
|
248
|
+
*
|
|
249
|
+
* Deterministic: no draw, the seed is the kind's id.
|
|
250
|
+
*/
|
|
251
|
+
export declare function aiSiteKindDefaultLook(kind: AiSiteKind, known: {
|
|
252
|
+
presetIds: ReadonlySet<string>;
|
|
253
|
+
pairingIds?: ReadonlySet<string>;
|
|
254
|
+
}): AiSiteKindDefaultLook;
|