@aglyn/plugins-ai 1.0.0-beta.292 → 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-credit-estimate.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 * WHAT A JOB REALLY COSTS, NEXT TO WHAT IT CAN COST (AGL-3722).\n *\n * Every job's cost used to be quoted at its CEILING: each pass at the\n * `AI_SITE_PASS_CREDITS` (50) a pass is reserved against. A Free Assist\n * build of two pages, a quote form and a layout was quoted 600 credits and\n * refused with 164 left, when builds like it measure about a fifth of that.\n *\n * So a job is now quoted as a RANGE: what it is likely to cost (the sum of\n * the measured medians of what it builds), what it costs nine times in ten\n * (the measured p90s), and its ceiling (the old figure, unchanged). The\n * meter still charges actual use, and the reservation each pass takes is\n * still the ceiling's 50 — only the words and the Free admission move:\n * a Free job is admitted outright when its p90 fits what is left, and asked\n * to be confirmed when it does not.\n *\n * This module is pure: the console, the doors and the specs read one table.\n */\n\n/** One measured cost: the median and the 90th percentile, in credits. */\nexport interface AiMeasuredCredits {\n median: number\n p90: number\n}\n\n/**\n * MEASURED IN PRODUCTION, credits per unit, over every `done` AI job since\n * 2026-10-01 (read 2026-10-09; a whole Free site job came to 148–160):\n *\n * page item 30 / 40 (a page of about `AI_MEASURED_PAGE_SECTIONS` sections, its listing included)\n * form 29 / 31\n * layout 23 / 24 (header and footer, one answer)\n * theme 4 / 8 (a site start's look)\n * plan step 21 / 70\n *\n * THE FAST TIER'S PART, REPRICED (AGL-3781). A site start's look, a Free\n * start's plan and every page's listing run on the fast tier, measured above\n * on Claude Haiku 4.5 at its cost. They run on Claude Haiku 5.5 at the\n * platform's markup now, so those parts are repriced from what production\n * sent, at the new rates and about 30% more tokens: over October 1–14 a look\n * 4 / 7 → 1 / 2, a Free start's plan 15 / 22 → 3 / 5, a listing 3 / 5 → 1 / 1\n * (2 less on every page), and a whole two-page Free site 148 → 121 at the\n * median. The other rows ran on the balanced tier and do not move.\n *\n * HOW TO RE-MEASURE: a read-only collection-group read of `aiJobs` in\n * production where `status == 'done'` and `createdAt` is in the window; group\n * each ledger row's (`items[]`) `creditsSpent` by its `op` (the site start's\n * look is the `theme` row), take the plan step's `steps[].creditsSpent` for\n * `plan`, and each job's `creditsSpent` for the whole-site band. Put each\n * group's median and 90th percentile here, and the dates in\n * `AI_MEASURED_AT`. `ai-credit-estimate.spec.ts` holds the table, the page\n * scaling and the measured whole-site band together, so a re-measure that\n * moves one moves the spec with it.\n */\nexport const AI_MEASURED_UNIT_CREDITS = {\n // 30 / 40 with its listing at Haiku 4.5's cost.\n page: { median: 28, p90: 38 },\n form: { median: 29, p90: 31 },\n layout: { median: 23, p90: 24 },\n // 4 / 8 on Haiku 4.5.\n theme: { median: 1, p90: 2 },\n // 21 / 70: the median is a Free start's plan, on the fast tier; the p90 a\n // plan on the balanced tier, which does not move.\n plan: { median: 4, p90: 70 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * MEASURED IN PRODUCTION, credits per unit of a PAID site start, whose plan\n * and parts run on the paid routing (Sonnet) rather than a Free start's\n * (Haiku) the table above is mostly made of: every `done` paid site job\n * since 2026-10-01, read 2026-10-12 (6 jobs; products from the 4 stores; AGL-3739).\n *\n * plan step 81 / 86 (a Free start's plan: 20 / 42 on Haiku 4.5, 3 / 5 repriced)\n * page item 29 / 49 (about `AI_MEASURED_PAGE_SECTIONS` sections; 27 / 47 with its listing repriced)\n * form 32 / 39\n * layout 29 / 29\n * theme 4 / 4 (1 / 1 repriced)\n * email 49 / 51\n * products 64 / 66 (a store's first products, one answer)\n *\n * A 5-page store with its welcome email was quoted about 277 off the table\n * above: its plan at a Free start's 21 where paid plans run about 81, and its\n * first products not counted at all where they run about 64. Those stores\n * came to 413-448; this table quotes one at about 404.\n *\n * Re-measure as the table above says, keeping only jobs whose plan step's\n * `tokens.model` is the paid routing's.\n */\nexport const AI_MEASURED_PAID_SITE_CREDITS = {\n plan: { median: 81, p90: 86 },\n page: { median: 27, p90: 47 },\n form: { median: 32, p90: 39 },\n layout: { median: 29, p90: 29 },\n theme: { median: 1, p90: 1 },\n email: { median: 49, p90: 51 },\n products: { median: 64, p90: 66 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * A page brought in from a person's own website (AGL-3976), priced as an AI\n * page: a paid site start's page as measured above, since an imported page is\n * one answer in the layout language and its listing, written inside one job\n * the way a site start writes its pages. Its site's header and footer, built\n * once in an import's first wave, is that table's layout.\n *\n * Re-measure from done `import` jobs' item ledgers once production has run\n * them: their `page` rows' `creditsSpent`, as the paid table is read.\n */\nexport const AI_MEASURED_IMPORT_CREDITS = {\n page: AI_MEASURED_PAID_SITE_CREDITS.page,\n frame: AI_MEASURED_PAID_SITE_CREDITS.layout,\n /**\n * A main page Aglyn AI arranges from its old page's own lines (AGL-4083):\n * one frame-sized structured answer, no prose, and its listing.\n * PROVISIONAL until the first live import measures it; it prices nothing\n * until the step marks a page `arranged`.\n */\n arranged: { median: 12, p90: 20 },\n /** A page copied as it is (AGL-4083): its listing pass alone. Provisional, as `arranged`. */\n copied: { median: 1, p90: 2 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * When the table above was read, and from when its jobs ran. Both tables\n * were measured before AGL-3781 asked a paid site's plan at medium effort and\n * the layout language's pages and frames at low. A lower effort draws less,\n * so their plan, page and layout figures stand as the higher reading until\n * the next re-measure: a quote is never under what is drawn.\n */\nexport const AI_MEASURED_AT = { read: '2026-10-09', since: '2026-10-01' } as const\n\n/**\n * What a whole Free two-page site job measured at, in credits, in the same\n * window: 148–160 with its plan, look and listings at Haiku 4.5's cost, 27\n * less at the median with them repriced (AGL-3781).\n */\nexport const AI_MEASURED_FREE_SITE_CREDITS = { low: 121, high: 133 } as const\n\n/** The sections the measured page item is about: a page is scaled around it. */\nexport const AI_MEASURED_PAGE_SECTIONS = 5\n\n/**\n * What a page of this many sections is likely to cost: the measured page\n * scaled by its sections around the ~5-section median — about 6 credits a\n * section, 8 at the p90.\n */\nexport function aiMeasuredPageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { median, p90 } = AI_MEASURED_UNIT_CREDITS.page\n return {\n median: Math.ceil((median * count) / AI_MEASURED_PAGE_SECTIONS),\n p90: Math.ceil((p90 * count) / AI_MEASURED_PAGE_SECTIONS),\n }\n}\n\n/**\n * What a page costs when it is written ON ITS OWN — a page an Assist build or\n * a page job writes, rather than one of a site start's pages (AGL-3722).\n *\n * The table's page item is a site start's: its pages are written one after\n * another inside one job, the later ones reading the prompt the first one\n * paid to write, so a 2-section second page measured 19–28. A page written on\n * its own pays that prompt itself and reads the site it lands on, and costs\n * what a site start's FIRST page costs: a 5-section home page measured 39–78\n * (median 40, p90 78), standalone page jobs' writing step 82–105 for a\n * 2-section page, and the Assist build pages of 2026-10-09 22–89 each. Quoting\n * a build's pages at the site start's scaled median told a Free workspace\n * \"about 48 credits\" for two 4-section pages (AGL-3722, 2026-10-10).\n *\n * So it is priced as a fixed cost — the prompt, the site, the listing — and a\n * cost per section: a 4-section page about 38 (p90 76), a 5-section 44 (p90 88).\n * The fixed part was 16 / 30 with its listing at Haiku 4.5's cost (AGL-3781).\n */\nexport const AI_MEASURED_STANDALONE_PAGE = {\n fixed: { median: 14, p90: 28 },\n perSection: { median: 6, p90: 12 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/** What a page of this many sections written on its own is likely to cost. */\nexport function aiMeasuredStandalonePageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { fixed, perSection } = AI_MEASURED_STANDALONE_PAGE\n return {\n median: fixed.median + perSection.median * count,\n p90: fixed.p90 + perSection.p90 * count,\n }\n}\n\n/**\n * What a creation costs when it is written ON ITS OWN (AGL-3722), where it\n * measured apart from a site start's: an Assist build's layout came to 24–28\n * (2026-10-09/10), against a site start's 23/24 written in its warm prompt.\n * A form measured the same either way (25–30 on its own, 29/31 in a site\n * start), so it keeps the table's figure.\n */\nexport const AI_MEASURED_STANDALONE_CREATION_CREDITS = {\n layout: { median: 26, p90: 30 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * What one pass of a kind with no measurement of its own is likely to cost\n * (a component, an email design, a template, an item another runner\n * builds), written on its own: a component job's writing step measured\n * 52–65, a form job's 37, a layout job's 63, and the Assist build of\n * 2026-10-09 a welcome email at 46 and a products step at 60. Until\n * 2026-10-10 this was a site start's form (29/31), which is written inside a\n * site job's warm prompt and is the cheapest single answer there is.\n */\nexport const AI_MEASURED_PASS_CREDITS: AiMeasuredCredits = { median: 50, p90: 65 }\n\n/** A job's cost as it is quoted: likely, nine times in ten, and at its worst. */\nexport interface AiCreditRange {\n /** The sum of the measured medians: what the job is likely to spend. */\n likely: number\n /** The sum of the measured p90s: what a Free job is admitted on. */\n p90: number\n /** Every pass at its reserve: what it can spend at most. */\n ceiling: number\n}\n\nexport const AI_CREDIT_RANGE_ZERO: AiCreditRange = { likely: 0, p90: 0, ceiling: 0 }\n\n/** Two ranges added. */\nexport function aiCreditRangeAdd(a: AiCreditRange, b: AiCreditRange): AiCreditRange {\n return { likely: a.likely + b.likely, p90: a.p90 + b.p90, ceiling: a.ceiling + b.ceiling }\n}\n\n/** A measured cost as a range with its own ceiling, the likely and p90 never above it. */\nexport function aiCreditRangeOf(measured: AiMeasuredCredits, ceiling: number): AiCreditRange {\n const top = Math.max(0, Math.ceil(ceiling))\n return { likely: Math.min(measured.median, top), p90: Math.min(measured.p90, top), ceiling: top }\n}\n\n/** A range whose likely and p90 are each at least the last, and at most the ceiling. */\nexport function aiCreditRangeOrdered(range: AiCreditRange): AiCreditRange {\n const ceiling = Math.max(0, Math.ceil(range.ceiling))\n const likely = Math.min(Math.max(0, Math.ceil(range.likely)), ceiling)\n const p90 = Math.min(Math.max(likely, Math.ceil(range.p90)), ceiling)\n return { likely, p90, ceiling }\n}\n\nconst n = (value: number) => value.toLocaleString('en-US')\n\n/**\n * The range as every surface quotes it: \"About 106 credits (up to 650)\".\n * A job whose likely figure is its ceiling says one number.\n */\nexport function aiCreditRangeText(range: AiCreditRange): string {\n if (range.likely >= range.ceiling) return `About ${n(range.ceiling)} credits`\n return `About ${n(range.likely)} credits (up to ${n(range.ceiling)})`\n}\n\n/**\n * Whether a Free job of this range needs the person's go-ahead before it\n * starts (AGL-3722): its p90 is more than what is left. A job whose p90\n * fits starts with no prompt; one that does not starts only when confirmed,\n * builds as much as what is left pays for, and pauses with Resume.\n */\nexport function aiCreditsNeedConfirm(range: Pick<AiCreditRange, 'p90'>, left: number): boolean {\n return Number.isFinite(left) && range.p90 > Math.max(0, Math.floor(left))\n}\n\n/** The smaller first build a Free job that does not fit is offered (AGL-3722). */\nexport interface AiCreditsSmaller {\n /** \"Build the home page first\". */\n label: string\n likely: number\n p90: number\n ceiling: number\n}\n\n/**\n * What a Free job that does not fit what is left says before it starts, and\n * the choices it offers: build what fits, a smaller first build, or Upgrade.\n * The door that refuses an unconfirmed start answers with it (status 409,\n * `code: AI_CREDITS_CONFIRM_CODE`), and the plan card and the guided start\n * draw it from the same figures.\n */\nexport interface AiCreditsPrompt extends AiCreditRange {\n left: number\n resetsOn: string\n smaller: AiCreditsSmaller | null\n}\n\n/** The `code` a door answers an unconfirmed Free start with. */\nexport const AI_CREDITS_CONFIRM_CODE = 'credits-confirm'\n\n/** \"Build what fits\": the go-ahead for a Free job past what is left. */\nexport const AI_CREDITS_BUILD_WHAT_FITS = 'Build what fits'\n\n/** The smaller first build's button: \"Build the home page first (about 64 credits)\". */\nexport function aiCreditsSmallerText(smaller: Pick<AiCreditsSmaller, 'label' | 'likely'>): string {\n return `${smaller.label} (about ${n(smaller.likely)} credits)`\n}\n\n/** Which way on a prompt offers, in the order it offers them. */\nexport type AiCreditsChoiceKey = 'fits' | 'smaller' | 'upgrade'\n\n/** One way on, as its option button reads it: a short label and the figure under it (AGL-3722). */\nexport interface AiCreditsChoice {\n key: AiCreditsChoiceKey\n label: string\n detail: string\n /** The one choice drawn as the primary button. */\n recommended: boolean\n}\n\nconst credit = (count: number) => `${n(count)} ${count === 1 ? 'credit' : 'credits'}`\n\n/**\n * The prompt's ways on, as its three option buttons read them (Zach,\n * 2026-10-10: \"more appealing and consistent\"): build what fits, the smaller\n * first build when there is one, and Upgrade when it can be offered — each a\n * short sentence-case label with its figure under it. The smaller build is\n * the recommended one when its p90 fits what is left, since it then finishes;\n * otherwise building what fits is.\n */\nexport function aiCreditsChoices(\n prompt: Pick<AiCreditsPrompt, 'left' | 'smaller'>,\n offers: { smaller: boolean; upgrade: boolean },\n): AiCreditsChoice[] {\n const left = Math.max(0, Math.floor(prompt.left))\n const smaller = offers.smaller ? prompt.smaller : null\n const smallerFits = smaller !== null && smaller.p90 <= left\n const choices: AiCreditsChoice[] = [\n {\n key: 'fits',\n label: AI_CREDITS_BUILD_WHAT_FITS,\n detail: `Uses your ${credit(left)} left, then pauses`,\n recommended: !smallerFits,\n },\n ]\n if (smaller) {\n choices.push({\n key: 'smaller',\n label: smaller.label,\n detail: smallerFits ? `About ${credit(smaller.likely)}, fits what you have left` : `About ${credit(smaller.likely)}`,\n recommended: smallerFits,\n })\n }\n if (offers.upgrade) choices.push({ key: 'upgrade', label: 'Upgrade', detail: 'Get more credits', recommended: false })\n return choices\n}\n\n/** A reset day as a person reads it: \"November 1\". */\nfunction resetLabel(resetsOn: string): string {\n const at = new Date(`${resetsOn}T00:00:00.000Z`)\n return Number.isFinite(at.getTime())\n ? at.toLocaleDateString('en-US', { month: 'long', day: 'numeric', timeZone: 'UTC' })\n : 'the first of next month'\n}\n\n/**\n * The plain sentence (Zach, 2026-10-09): \"This build is about N credits (up\n * to M). You have L left, so it will build as much as it can and pause when\n * your credits run out. You can upgrade or resume when they renew.\"\n */\nexport function aiCreditsPromptText(prompt: AiCreditsPrompt, noun: 'build' | 'site' = 'build'): string {\n const size =\n prompt.likely >= prompt.ceiling\n ? `about ${n(prompt.ceiling)} credits`\n : `about ${n(prompt.likely)} credits (up to ${n(prompt.ceiling)})`\n const left = Math.max(0, Math.floor(prompt.left))\n return (\n `This ${noun} is ${size}. You have ${n(left)} left, so it will build as much as it can and pause ` +\n `when your credits run out. You can upgrade or resume when they renew on ${resetLabel(prompt.resetsOn)}.`\n )\n}\n\n/**\n * The record a confirmed Free start keeps (AGL-3722): who said go, when,\n * what was left then and the range they were shown.\n */\nexport interface AiJobCreditsConfirmation extends AiCreditRange {\n by: string\n at: Date | string\n left: number\n}\n\n/** The confirmation a door keeps on the job for the prompt the person confirmed, or `null` when there was none. */\nexport function aiCreditsConfirmation(\n prompt: AiCreditsPrompt | null,\n by: string,\n at: Date,\n): AiJobCreditsConfirmation | null {\n if (!prompt) return null\n return { by, at, left: prompt.left, likely: prompt.likely, p90: prompt.p90, ceiling: prompt.ceiling }\n}\n\n/** The prompt a range needs against what is left, or `null` when it fits. */\nexport function aiCreditsPromptFor(\n range: AiCreditRange,\n credits: { left: number; resetsOn: string } | null | undefined,\n smaller: AiCreditsSmaller | null = null,\n): AiCreditsPrompt | null {\n if (!credits || !aiCreditsNeedConfirm(range, credits.left)) return null\n return { ...range, left: Math.max(0, Math.floor(credits.left)), resetsOn: credits.resetsOn, smaller }\n}\n"],"names":["AI_MEASURED_UNIT_CREDITS","page","median","p90","form","layout","theme","plan","AI_MEASURED_PAID_SITE_CREDITS","email","products","AI_MEASURED_IMPORT_CREDITS","frame","arranged","copied","AI_MEASURED_AT","read","since","AI_MEASURED_FREE_SITE_CREDITS","low","high","AI_MEASURED_PAGE_SECTIONS","aiMeasuredPageCredits","sections","count","Math","max","floor","Number","isFinite","ceil","AI_MEASURED_STANDALONE_PAGE","fixed","perSection","aiMeasuredStandalonePageCredits","AI_MEASURED_STANDALONE_CREATION_CREDITS","AI_MEASURED_PASS_CREDITS","AI_CREDIT_RANGE_ZERO","likely","ceiling","aiCreditRangeAdd","a","b","aiCreditRangeOf","measured","top","min","aiCreditRangeOrdered","range","n","value","toLocaleString","aiCreditRangeText","aiCreditsNeedConfirm","left","AI_CREDITS_CONFIRM_CODE","AI_CREDITS_BUILD_WHAT_FITS","aiCreditsSmallerText","smaller","label","credit","aiCreditsChoices","prompt","offers","smallerFits","choices","key","detail","recommended","push","upgrade","resetLabel","resetsOn","at","Date","getTime","toLocaleDateString","month","day","timeZone","aiCreditsPromptText","noun","size","aiCreditsConfirmation","by","aiCreditsPromptFor","credits"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-credit-estimate.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 * WHAT A JOB REALLY COSTS, NEXT TO WHAT IT CAN COST (AGL-3722).\n *\n * Every job's cost used to be quoted at its CEILING: each pass at the\n * `AI_SITE_PASS_CREDITS` (50) a pass is reserved against. A Free Assist\n * build of two pages, a quote form and a layout was quoted 600 credits and\n * refused with 164 left, when builds like it measure about a fifth of that.\n *\n * So a job is now quoted as a RANGE: what it is likely to cost (the sum of\n * the measured medians of what it builds), what it costs nine times in ten\n * (the measured p90s), and its ceiling (the old figure, unchanged). The\n * meter still charges actual use, and the reservation each pass takes is\n * still the ceiling's 50 — only the words and the Free admission move:\n * a Free job is admitted outright when its p90 fits what is left, and asked\n * to be confirmed when it does not.\n *\n * This module is pure: the console, the doors and the specs read one table.\n */\n\n/** One measured cost: the median and the 90th percentile, in credits. */\nexport interface AiMeasuredCredits {\n median: number\n p90: number\n}\n\n/**\n * MEASURED IN PRODUCTION, credits per unit, over every `done` AI job since\n * 2026-10-01 (read 2026-10-09; a whole Free site job came to 148–160):\n *\n * page item 30 / 40 (a page of about `AI_MEASURED_PAGE_SECTIONS` sections, its listing included)\n * form 29 / 31\n * layout 23 / 24 (header and footer, one answer)\n * theme 4 / 8 (a site start's look)\n * plan step 21 / 70\n *\n * THE FAST TIER'S PART, REPRICED (AGL-3781). A site start's look, a Free\n * start's plan and every page's listing run on the fast tier, measured above\n * on Claude Haiku 4.5 at its cost. They run on Claude Haiku 5.5 at the\n * platform's markup now, so those parts are repriced from what production\n * sent, at the new rates and about 30% more tokens: over October 1–14 a look\n * 4 / 7 → 1 / 2, a Free start's plan 15 / 22 → 3 / 5, a listing 3 / 5 → 1 / 1\n * (2 less on every page), and a whole two-page Free site 148 → 121 at the\n * median. The other rows ran on the balanced tier and do not move.\n *\n * HOW TO RE-MEASURE: a read-only collection-group read of `aiJobs` in\n * production where `status == 'done'` and `createdAt` is in the window; group\n * each ledger row's (`items[]`) `creditsSpent` by its `op` (the site start's\n * look is the `theme` row), take the plan step's `steps[].creditsSpent` for\n * `plan`, and each job's `creditsSpent` for the whole-site band. Put each\n * group's median and 90th percentile here, and the dates in\n * `AI_MEASURED_AT`. `ai-credit-estimate.spec.ts` holds the table, the page\n * scaling and the measured whole-site band together, so a re-measure that\n * moves one moves the spec with it.\n */\nexport const AI_MEASURED_UNIT_CREDITS = {\n // 30 / 40 with its listing at Haiku 4.5's cost.\n page: { median: 28, p90: 38 },\n form: { median: 29, p90: 31 },\n layout: { median: 23, p90: 24 },\n // 4 / 8 on Haiku 4.5.\n theme: { median: 1, p90: 2 },\n // 21 / 70: the median is a Free start's plan, on the fast tier; the p90 a\n // plan on the balanced tier, which does not move.\n plan: { median: 4, p90: 70 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * MEASURED IN PRODUCTION, credits per unit of a PAID site start, whose plan\n * and parts run on the paid routing (Sonnet) rather than a Free start's\n * (Haiku) the table above is mostly made of: every `done` paid site job\n * since 2026-10-01, read 2026-10-12 (6 jobs; products from the 4 stores; AGL-3739).\n *\n * plan step 81 / 86 (a Free start's plan: 20 / 42 on Haiku 4.5, 3 / 5 repriced)\n * page item 29 / 49 (about `AI_MEASURED_PAGE_SECTIONS` sections; 27 / 47 with its listing repriced)\n * form 32 / 39\n * layout 29 / 29\n * theme 4 / 4 (1 / 1 repriced)\n * email 49 / 51\n * products 64 / 66 (a store's first products, one answer)\n *\n * A 5-page store with its welcome email was quoted about 277 off the table\n * above: its plan at a Free start's 21 where paid plans run about 81, and its\n * first products not counted at all where they run about 64. Those stores\n * came to 413-448; this table quotes one at about 404.\n *\n * Re-measure as the table above says, keeping only jobs whose plan step's\n * `tokens.model` is the paid routing's.\n */\nexport const AI_MEASURED_PAID_SITE_CREDITS = {\n plan: { median: 81, p90: 86 },\n page: { median: 27, p90: 47 },\n form: { median: 32, p90: 39 },\n layout: { median: 29, p90: 29 },\n theme: { median: 1, p90: 1 },\n email: { median: 49, p90: 51 },\n products: { median: 64, p90: 66 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * A page brought in from a person's own website (AGL-3976), priced as an AI\n * page: a paid site start's page as measured above, since an imported page is\n * one answer in the layout language and its listing, written inside one job\n * the way a site start writes its pages. Its site's header and footer, built\n * once in an import's first wave, is that table's layout.\n *\n * Re-measure from done `import` jobs' item ledgers once production has run\n * them: their `page` rows' `creditsSpent`, as the paid table is read.\n */\nexport const AI_MEASURED_IMPORT_CREDITS = {\n page: AI_MEASURED_PAID_SITE_CREDITS.page,\n frame: AI_MEASURED_PAID_SITE_CREDITS.layout,\n /**\n * A main page whose design Aglyn AI answers (AGL-4107): one small answer on\n * the balanced tier over the page's read structure — about 1,000 to 1,300\n * tokens in, two thirds of them its cached instructions, and 500 to 900 out\n * with its thinking, $0.009–$0.017 at Sonnet 5.5's billed rates over the\n * recorded aglyn.com pages — and no listing, the source's own being kept.\n * The 10/29 run spent 36–58 a page on a smaller model for a line-by-line\n * answer twenty times the size. PROVISIONAL until the next live import\n * measures it.\n */\n arranged: { median: 16, p90: 30 },\n /** A page copied as it is (AGL-4083): no model, and a listing only where the source has none. Provisional, as `arranged`. */\n copied: { median: 1, p90: 2 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * When the table above was read, and from when its jobs ran. Both tables\n * were measured before AGL-3781 asked a paid site's plan at medium effort and\n * the layout language's pages and frames at low. A lower effort draws less,\n * so their plan, page and layout figures stand as the higher reading until\n * the next re-measure: a quote is never under what is drawn.\n */\nexport const AI_MEASURED_AT = { read: '2026-10-09', since: '2026-10-01' } as const\n\n/**\n * What a whole Free two-page site job measured at, in credits, in the same\n * window: 148–160 with its plan, look and listings at Haiku 4.5's cost, 27\n * less at the median with them repriced (AGL-3781).\n */\nexport const AI_MEASURED_FREE_SITE_CREDITS = { low: 121, high: 133 } as const\n\n/** The sections the measured page item is about: a page is scaled around it. */\nexport const AI_MEASURED_PAGE_SECTIONS = 5\n\n/**\n * What a page of this many sections is likely to cost: the measured page\n * scaled by its sections around the ~5-section median — about 6 credits a\n * section, 8 at the p90.\n */\nexport function aiMeasuredPageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { median, p90 } = AI_MEASURED_UNIT_CREDITS.page\n return {\n median: Math.ceil((median * count) / AI_MEASURED_PAGE_SECTIONS),\n p90: Math.ceil((p90 * count) / AI_MEASURED_PAGE_SECTIONS),\n }\n}\n\n/**\n * What a page costs when it is written ON ITS OWN — a page an Assist build or\n * a page job writes, rather than one of a site start's pages (AGL-3722).\n *\n * The table's page item is a site start's: its pages are written one after\n * another inside one job, the later ones reading the prompt the first one\n * paid to write, so a 2-section second page measured 19–28. A page written on\n * its own pays that prompt itself and reads the site it lands on, and costs\n * what a site start's FIRST page costs: a 5-section home page measured 39–78\n * (median 40, p90 78), standalone page jobs' writing step 82–105 for a\n * 2-section page, and the Assist build pages of 2026-10-09 22–89 each. Quoting\n * a build's pages at the site start's scaled median told a Free workspace\n * \"about 48 credits\" for two 4-section pages (AGL-3722, 2026-10-10).\n *\n * So it is priced as a fixed cost — the prompt, the site, the listing — and a\n * cost per section: a 4-section page about 38 (p90 76), a 5-section 44 (p90 88).\n * The fixed part was 16 / 30 with its listing at Haiku 4.5's cost (AGL-3781).\n */\nexport const AI_MEASURED_STANDALONE_PAGE = {\n fixed: { median: 14, p90: 28 },\n perSection: { median: 6, p90: 12 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/** What a page of this many sections written on its own is likely to cost. */\nexport function aiMeasuredStandalonePageCredits(sections: number): AiMeasuredCredits {\n const count = Math.max(1, Math.floor(Number.isFinite(sections) ? sections : AI_MEASURED_PAGE_SECTIONS))\n const { fixed, perSection } = AI_MEASURED_STANDALONE_PAGE\n return {\n median: fixed.median + perSection.median * count,\n p90: fixed.p90 + perSection.p90 * count,\n }\n}\n\n/**\n * What a creation costs when it is written ON ITS OWN (AGL-3722), where it\n * measured apart from a site start's: an Assist build's layout came to 24–28\n * (2026-10-09/10), against a site start's 23/24 written in its warm prompt.\n * A form measured the same either way (25–30 on its own, 29/31 in a site\n * start), so it keeps the table's figure.\n */\nexport const AI_MEASURED_STANDALONE_CREATION_CREDITS = {\n layout: { median: 26, p90: 30 },\n} as const satisfies Record<string, AiMeasuredCredits>\n\n/**\n * What one pass of a kind with no measurement of its own is likely to cost\n * (a component, an email design, a template, an item another runner\n * builds), written on its own: a component job's writing step measured\n * 52–65, a form job's 37, a layout job's 63, and the Assist build of\n * 2026-10-09 a welcome email at 46 and a products step at 60. Until\n * 2026-10-10 this was a site start's form (29/31), which is written inside a\n * site job's warm prompt and is the cheapest single answer there is.\n */\nexport const AI_MEASURED_PASS_CREDITS: AiMeasuredCredits = { median: 50, p90: 65 }\n\n/** A job's cost as it is quoted: likely, nine times in ten, and at its worst. */\nexport interface AiCreditRange {\n /** The sum of the measured medians: what the job is likely to spend. */\n likely: number\n /** The sum of the measured p90s: what a Free job is admitted on. */\n p90: number\n /** Every pass at its reserve: what it can spend at most. */\n ceiling: number\n}\n\nexport const AI_CREDIT_RANGE_ZERO: AiCreditRange = { likely: 0, p90: 0, ceiling: 0 }\n\n/** Two ranges added. */\nexport function aiCreditRangeAdd(a: AiCreditRange, b: AiCreditRange): AiCreditRange {\n return { likely: a.likely + b.likely, p90: a.p90 + b.p90, ceiling: a.ceiling + b.ceiling }\n}\n\n/** A measured cost as a range with its own ceiling, the likely and p90 never above it. */\nexport function aiCreditRangeOf(measured: AiMeasuredCredits, ceiling: number): AiCreditRange {\n const top = Math.max(0, Math.ceil(ceiling))\n return { likely: Math.min(measured.median, top), p90: Math.min(measured.p90, top), ceiling: top }\n}\n\n/** A range whose likely and p90 are each at least the last, and at most the ceiling. */\nexport function aiCreditRangeOrdered(range: AiCreditRange): AiCreditRange {\n const ceiling = Math.max(0, Math.ceil(range.ceiling))\n const likely = Math.min(Math.max(0, Math.ceil(range.likely)), ceiling)\n const p90 = Math.min(Math.max(likely, Math.ceil(range.p90)), ceiling)\n return { likely, p90, ceiling }\n}\n\nconst n = (value: number) => value.toLocaleString('en-US')\n\n/**\n * The range as every surface quotes it: \"About 106 credits (up to 650)\".\n * A job whose likely figure is its ceiling says one number.\n */\nexport function aiCreditRangeText(range: AiCreditRange): string {\n if (range.likely >= range.ceiling) return `About ${n(range.ceiling)} credits`\n return `About ${n(range.likely)} credits (up to ${n(range.ceiling)})`\n}\n\n/**\n * Whether a Free job of this range needs the person's go-ahead before it\n * starts (AGL-3722): its p90 is more than what is left. A job whose p90\n * fits starts with no prompt; one that does not starts only when confirmed,\n * builds as much as what is left pays for, and pauses with Resume.\n */\nexport function aiCreditsNeedConfirm(range: Pick<AiCreditRange, 'p90'>, left: number): boolean {\n return Number.isFinite(left) && range.p90 > Math.max(0, Math.floor(left))\n}\n\n/** The smaller first build a Free job that does not fit is offered (AGL-3722). */\nexport interface AiCreditsSmaller {\n /** \"Build the home page first\". */\n label: string\n likely: number\n p90: number\n ceiling: number\n}\n\n/**\n * What a Free job that does not fit what is left says before it starts, and\n * the choices it offers: build what fits, a smaller first build, or Upgrade.\n * The door that refuses an unconfirmed start answers with it (status 409,\n * `code: AI_CREDITS_CONFIRM_CODE`), and the plan card and the guided start\n * draw it from the same figures.\n */\nexport interface AiCreditsPrompt extends AiCreditRange {\n left: number\n resetsOn: string\n smaller: AiCreditsSmaller | null\n}\n\n/** The `code` a door answers an unconfirmed Free start with. */\nexport const AI_CREDITS_CONFIRM_CODE = 'credits-confirm'\n\n/** \"Build what fits\": the go-ahead for a Free job past what is left. */\nexport const AI_CREDITS_BUILD_WHAT_FITS = 'Build what fits'\n\n/** The smaller first build's button: \"Build the home page first (about 64 credits)\". */\nexport function aiCreditsSmallerText(smaller: Pick<AiCreditsSmaller, 'label' | 'likely'>): string {\n return `${smaller.label} (about ${n(smaller.likely)} credits)`\n}\n\n/** Which way on a prompt offers, in the order it offers them. */\nexport type AiCreditsChoiceKey = 'fits' | 'smaller' | 'upgrade'\n\n/** One way on, as its option button reads it: a short label and the figure under it (AGL-3722). */\nexport interface AiCreditsChoice {\n key: AiCreditsChoiceKey\n label: string\n detail: string\n /** The one choice drawn as the primary button. */\n recommended: boolean\n}\n\nconst credit = (count: number) => `${n(count)} ${count === 1 ? 'credit' : 'credits'}`\n\n/**\n * The prompt's ways on, as its three option buttons read them (Zach,\n * 2026-10-10: \"more appealing and consistent\"): build what fits, the smaller\n * first build when there is one, and Upgrade when it can be offered — each a\n * short sentence-case label with its figure under it. The smaller build is\n * the recommended one when its p90 fits what is left, since it then finishes;\n * otherwise building what fits is.\n */\nexport function aiCreditsChoices(\n prompt: Pick<AiCreditsPrompt, 'left' | 'smaller'>,\n offers: { smaller: boolean; upgrade: boolean },\n): AiCreditsChoice[] {\n const left = Math.max(0, Math.floor(prompt.left))\n const smaller = offers.smaller ? prompt.smaller : null\n const smallerFits = smaller !== null && smaller.p90 <= left\n const choices: AiCreditsChoice[] = [\n {\n key: 'fits',\n label: AI_CREDITS_BUILD_WHAT_FITS,\n detail: `Uses your ${credit(left)} left, then pauses`,\n recommended: !smallerFits,\n },\n ]\n if (smaller) {\n choices.push({\n key: 'smaller',\n label: smaller.label,\n detail: smallerFits ? `About ${credit(smaller.likely)}, fits what you have left` : `About ${credit(smaller.likely)}`,\n recommended: smallerFits,\n })\n }\n if (offers.upgrade) choices.push({ key: 'upgrade', label: 'Upgrade', detail: 'Get more credits', recommended: false })\n return choices\n}\n\n/** A reset day as a person reads it: \"November 1\". */\nfunction resetLabel(resetsOn: string): string {\n const at = new Date(`${resetsOn}T00:00:00.000Z`)\n return Number.isFinite(at.getTime())\n ? at.toLocaleDateString('en-US', { month: 'long', day: 'numeric', timeZone: 'UTC' })\n : 'the first of next month'\n}\n\n/**\n * The plain sentence (Zach, 2026-10-09): \"This build is about N credits (up\n * to M). You have L left, so it will build as much as it can and pause when\n * your credits run out. You can upgrade or resume when they renew.\"\n */\nexport function aiCreditsPromptText(prompt: AiCreditsPrompt, noun: 'build' | 'site' = 'build'): string {\n const size =\n prompt.likely >= prompt.ceiling\n ? `about ${n(prompt.ceiling)} credits`\n : `about ${n(prompt.likely)} credits (up to ${n(prompt.ceiling)})`\n const left = Math.max(0, Math.floor(prompt.left))\n return (\n `This ${noun} is ${size}. You have ${n(left)} left, so it will build as much as it can and pause ` +\n `when your credits run out. You can upgrade or resume when they renew on ${resetLabel(prompt.resetsOn)}.`\n )\n}\n\n/**\n * The record a confirmed Free start keeps (AGL-3722): who said go, when,\n * what was left then and the range they were shown.\n */\nexport interface AiJobCreditsConfirmation extends AiCreditRange {\n by: string\n at: Date | string\n left: number\n}\n\n/** The confirmation a door keeps on the job for the prompt the person confirmed, or `null` when there was none. */\nexport function aiCreditsConfirmation(\n prompt: AiCreditsPrompt | null,\n by: string,\n at: Date,\n): AiJobCreditsConfirmation | null {\n if (!prompt) return null\n return { by, at, left: prompt.left, likely: prompt.likely, p90: prompt.p90, ceiling: prompt.ceiling }\n}\n\n/** The prompt a range needs against what is left, or `null` when it fits. */\nexport function aiCreditsPromptFor(\n range: AiCreditRange,\n credits: { left: number; resetsOn: string } | null | undefined,\n smaller: AiCreditsSmaller | null = null,\n): AiCreditsPrompt | null {\n if (!credits || !aiCreditsNeedConfirm(range, credits.left)) return null\n return { ...range, left: Math.max(0, Math.floor(credits.left)), resetsOn: credits.resetsOn, smaller }\n}\n"],"names":["AI_MEASURED_UNIT_CREDITS","page","median","p90","form","layout","theme","plan","AI_MEASURED_PAID_SITE_CREDITS","email","products","AI_MEASURED_IMPORT_CREDITS","frame","arranged","copied","AI_MEASURED_AT","read","since","AI_MEASURED_FREE_SITE_CREDITS","low","high","AI_MEASURED_PAGE_SECTIONS","aiMeasuredPageCredits","sections","count","Math","max","floor","Number","isFinite","ceil","AI_MEASURED_STANDALONE_PAGE","fixed","perSection","aiMeasuredStandalonePageCredits","AI_MEASURED_STANDALONE_CREATION_CREDITS","AI_MEASURED_PASS_CREDITS","AI_CREDIT_RANGE_ZERO","likely","ceiling","aiCreditRangeAdd","a","b","aiCreditRangeOf","measured","top","min","aiCreditRangeOrdered","range","n","value","toLocaleString","aiCreditRangeText","aiCreditsNeedConfirm","left","AI_CREDITS_CONFIRM_CODE","AI_CREDITS_BUILD_WHAT_FITS","aiCreditsSmallerText","smaller","label","credit","aiCreditsChoices","prompt","offers","smallerFits","choices","key","detail","recommended","push","upgrade","resetLabel","resetsOn","at","Date","getTime","toLocaleDateString","month","day","timeZone","aiCreditsPromptText","noun","size","aiCreditsConfirmation","by","aiCreditsPromptFor","credits"],"mappings":"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"}
|
|
@@ -26,9 +26,11 @@ export declare const AI_SITE_IMPORT_WAVE_PAGES = 10;
|
|
|
26
26
|
/**
|
|
27
27
|
* The most pages one import reads, whatever the plan allows: an unlimited
|
|
28
28
|
* plan is still one crawl's worth, and the crawl's own time budget may stop
|
|
29
|
-
* sooner (the inventory then says so).
|
|
29
|
+
* sooner (the inventory then says so). Only pages count: a file a link
|
|
30
|
+
* points at is never fetched as one (AGL-4106), so aglyn.com's 209 pages fit
|
|
31
|
+
* with room where 136 press-kit files once took 136 of 200.
|
|
30
32
|
*/
|
|
31
|
-
export declare const AI_SITE_IMPORT_MAX_PAGES =
|
|
33
|
+
export declare const AI_SITE_IMPORT_MAX_PAGES = 400;
|
|
32
34
|
/** The fewest pages under one address prefix that make it a collection. */
|
|
33
35
|
export declare const AI_SITE_IMPORT_COLLECTION_MIN = 3;
|
|
34
36
|
/** The most photos brought in from one page. */
|
|
@@ -45,16 +47,21 @@ export declare const AI_SITE_IMPORT_MAX_SECTIONS = 24;
|
|
|
45
47
|
export declare const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2000;
|
|
46
48
|
/** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */
|
|
47
49
|
export declare const AI_SITE_IMPORT_READ_MAX_CHARS = 120000;
|
|
50
|
+
/** The most characters one page's layout keeps on the job document (AGL-4106): its icons' drawings go first. */
|
|
51
|
+
export declare const AI_SITE_IMPORT_LAYOUT_MAX_CHARS = 48000;
|
|
48
52
|
/**
|
|
49
|
-
* The most characters of blocks the reads of one import hold at once: they
|
|
53
|
+
* The most characters of blocks (and their layouts, AGL-4106) the reads of one import hold at once: they
|
|
50
54
|
* ride the job document, which Firestore caps at 1 MiB, beside the
|
|
51
55
|
* inventory and the ledger. A wave's later page past it keeps what fits and
|
|
52
56
|
* says it was cut.
|
|
53
57
|
*/
|
|
54
58
|
export declare const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400000;
|
|
55
59
|
/**
|
|
56
|
-
* A page
|
|
57
|
-
*
|
|
60
|
+
* A collection's page longer than this is prose: a policy, an article, a
|
|
61
|
+
* long story (AGL-4083). A collection's page is copied as it is whatever its
|
|
62
|
+
* length, and since AGL-4106 a main page is arranged whatever its length:
|
|
63
|
+
* `/product` at 1,072 words is laid out as its source lays it out, not copied
|
|
64
|
+
* as one column.
|
|
58
65
|
*/
|
|
59
66
|
export declare const AI_SITE_IMPORT_PROSE_WORDS = 900;
|
|
60
67
|
/** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */
|
|
@@ -110,7 +117,9 @@ export type AiSiteImportLeftOutReason =
|
|
|
110
117
|
/** It sends visitors to another website. */
|
|
111
118
|
| 'off-site'
|
|
112
119
|
/** The read stopped (its clock, its bytes, its page count) before it got to it (AGL-4083). */
|
|
113
|
-
| 'not-reached'
|
|
120
|
+
| 'not-reached'
|
|
121
|
+
/** A link to a file (a download, a PDF with no extension), not a page (AGL-4106). */
|
|
122
|
+
| 'file';
|
|
114
123
|
export declare const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[];
|
|
115
124
|
/** The reasons that mean the page was there and AglynBot could not read it: the "could not be read" of what was found. */
|
|
116
125
|
export declare const AI_SITE_IMPORT_UNREAD_REASONS: readonly AiSiteImportLeftOutReason[];
|
|
@@ -130,6 +139,250 @@ export interface AiSiteImportPhoto {
|
|
|
130
139
|
/** The address on the old site it was read from, where a page that is copied places it (AGL-4084). */
|
|
131
140
|
from?: string;
|
|
132
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* One line of a page, by its index in the page's main-region blocks
|
|
144
|
+
* (`AiSiteImportPageRead.blocks`, 0-based). The layout holds no words of
|
|
145
|
+
* its own: every word is a block's, placed once.
|
|
146
|
+
*/
|
|
147
|
+
export type SiteImportLineRef = number;
|
|
148
|
+
/** A section's background, in the layout language's bands. */
|
|
149
|
+
export type SiteImportBand = 'plain' | 'soft' | 'brand' | 'dark';
|
|
150
|
+
/** How the source draws a button: filled, outlined, or as a link with button padding. */
|
|
151
|
+
export type SiteImportButtonStyle = 'primary' | 'secondary' | 'quiet';
|
|
152
|
+
/** What a repeated group of the source is. */
|
|
153
|
+
export type SiteImportGroupKind = 'cards' | 'steps' | 'stats' | 'faq' | 'quotes' | 'logos' | 'pricing' | 'list' | 'text';
|
|
154
|
+
export declare const SITE_IMPORT_GROUP_KINDS: readonly SiteImportGroupKind[];
|
|
155
|
+
/** One line in place. `role` only where the source's own styling says it (an uppercase eyebrow, a large lede, a small note). */
|
|
156
|
+
export interface SiteImportLayoutLine {
|
|
157
|
+
kind: 'line';
|
|
158
|
+
line: SiteImportLineRef;
|
|
159
|
+
role?: 'eyebrow' | 'lede' | 'note';
|
|
160
|
+
/** The source draws this line (a link, or a `<button>`'s words) as a button. Its destination is the link block's `href`. */
|
|
161
|
+
button?: SiteImportButtonStyle;
|
|
162
|
+
}
|
|
163
|
+
/** A picture in place: an image block. */
|
|
164
|
+
export interface SiteImportLayoutPicture {
|
|
165
|
+
kind: 'picture';
|
|
166
|
+
line: SiteImportLineRef;
|
|
167
|
+
/** Where clicking it goes, when it sits in a link. */
|
|
168
|
+
href?: string;
|
|
169
|
+
/** It sits in a `<button>` (a video's poster, a lightbox): the picture is kept, the button is not a destination. */
|
|
170
|
+
button?: true;
|
|
171
|
+
}
|
|
172
|
+
/** An icon standing on its own, by index into `SiteImportLayout.icons`. */
|
|
173
|
+
export interface SiteImportLayoutIcon {
|
|
174
|
+
kind: 'icon';
|
|
175
|
+
icon: number;
|
|
176
|
+
}
|
|
177
|
+
/** Where one of the page's forms sat, by index into `AiSiteImportPageRead.forms`. */
|
|
178
|
+
export interface SiteImportLayoutForm {
|
|
179
|
+
kind: 'form';
|
|
180
|
+
form: number;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* A row of columns side by side; `cols` are relative widths in twelfths
|
|
184
|
+
* (`[4, 8]`, `[1, 1, 1]`). Every list of lists is a list of objects: the
|
|
185
|
+
* layout rides a Firestore document, which holds no array inside an array.
|
|
186
|
+
*/
|
|
187
|
+
export interface SiteImportLayoutRow {
|
|
188
|
+
kind: 'row';
|
|
189
|
+
cols: number[];
|
|
190
|
+
cells: Array<{
|
|
191
|
+
nodes: SiteImportLayoutNode[];
|
|
192
|
+
}>;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* One item of a group. Every line of the item is in `lines`, in order, and
|
|
196
|
+
* in exactly one of the named parts.
|
|
197
|
+
*/
|
|
198
|
+
export interface SiteImportLayoutItem {
|
|
199
|
+
lines: SiteImportLineRef[];
|
|
200
|
+
/** The item's name: its heading, its title link, or its first short line. */
|
|
201
|
+
title?: SiteImportLineRef;
|
|
202
|
+
/** A stat's figure, a step's number, a plan's price. */
|
|
203
|
+
figure?: SiteImportLineRef;
|
|
204
|
+
text?: SiteImportLineRef[];
|
|
205
|
+
/** Its bullet points (a plan's features). */
|
|
206
|
+
list?: SiteImportLineRef[];
|
|
207
|
+
/** Its own call to action: a "See … →" link or a button, last in the item. */
|
|
208
|
+
link?: SiteImportLineRef[];
|
|
209
|
+
image?: SiteImportLineRef;
|
|
210
|
+
/** The icon that leads it, by index into `SiteImportLayout.icons`. */
|
|
211
|
+
icon?: number;
|
|
212
|
+
/** The icon each of its bullet points carries (a check mark), when they share one. */
|
|
213
|
+
listIcon?: number;
|
|
214
|
+
/** Where the item goes as a whole: its title link's address. */
|
|
215
|
+
to?: string;
|
|
216
|
+
}
|
|
217
|
+
export interface SiteImportLayoutGroup {
|
|
218
|
+
kind: 'group';
|
|
219
|
+
group: SiteImportGroupKind;
|
|
220
|
+
/** Columns the source lays the items out in on a wide screen. */
|
|
221
|
+
columns: number;
|
|
222
|
+
/** Each item is drawn as a box (a border, a shadow or a fill of its own). */
|
|
223
|
+
boxed: boolean;
|
|
224
|
+
items: SiteImportLayoutItem[];
|
|
225
|
+
}
|
|
226
|
+
/** A table: rows of cells, each cell its lines (objects, not nested arrays, for Firestore). */
|
|
227
|
+
export interface SiteImportLayoutTable {
|
|
228
|
+
kind: 'table';
|
|
229
|
+
/** Whether the first row names the columns. */
|
|
230
|
+
header: boolean;
|
|
231
|
+
rows: Array<{
|
|
232
|
+
cells: Array<{
|
|
233
|
+
lines: SiteImportLineRef[];
|
|
234
|
+
}>;
|
|
235
|
+
}>;
|
|
236
|
+
}
|
|
237
|
+
/** A tab strip and its panels; a panel the source did not draw is empty. */
|
|
238
|
+
export interface SiteImportLayoutTabs {
|
|
239
|
+
kind: 'tabs';
|
|
240
|
+
tabs: Array<{
|
|
241
|
+
label: SiteImportLineRef | null;
|
|
242
|
+
selected?: true;
|
|
243
|
+
panel: SiteImportLayoutNode[];
|
|
244
|
+
}>;
|
|
245
|
+
}
|
|
246
|
+
export type SiteImportLayoutNode = SiteImportLayoutLine | SiteImportLayoutPicture | SiteImportLayoutIcon | SiteImportLayoutForm | SiteImportLayoutRow | SiteImportLayoutGroup | SiteImportLayoutTable | SiteImportLayoutTabs;
|
|
247
|
+
/** Where the hero's picture sits against its words. */
|
|
248
|
+
export type SiteImportHeroPicture = 'beside' | 'behind' | 'below' | 'above';
|
|
249
|
+
export interface SiteImportLayoutSection {
|
|
250
|
+
/** Its `aria-label`, its heading, or "Section n": neutralized, cut to a label. */
|
|
251
|
+
name: string;
|
|
252
|
+
/** How its edges were found: a landmark (`<section>`), a container of its own, or its headings. */
|
|
253
|
+
from: 'landmark' | 'container' | 'heading';
|
|
254
|
+
band: SiteImportBand;
|
|
255
|
+
/** Its background color as `#rrggbb`, when it has one. */
|
|
256
|
+
background: string | null;
|
|
257
|
+
/** A gradient or a photo behind it. */
|
|
258
|
+
backdrop?: {
|
|
259
|
+
kind: 'gradient';
|
|
260
|
+
} | {
|
|
261
|
+
kind: 'picture';
|
|
262
|
+
url: string;
|
|
263
|
+
};
|
|
264
|
+
align: 'start' | 'center';
|
|
265
|
+
/** Set on the section holding the page's first `<h1>`. */
|
|
266
|
+
hero?: {
|
|
267
|
+
picture: SiteImportHeroPicture | null;
|
|
268
|
+
};
|
|
269
|
+
nodes: SiteImportLayoutNode[];
|
|
270
|
+
/** Every line of the section, in order. */
|
|
271
|
+
lines: SiteImportLineRef[];
|
|
272
|
+
}
|
|
273
|
+
/** An icon the page draws, once however often it repeats. */
|
|
274
|
+
export interface SiteImportIconShape {
|
|
275
|
+
viewBox: string;
|
|
276
|
+
/** One path, for the Besigner's icon element (`iconPath`, drawn on a 24-unit square). */
|
|
277
|
+
path?: string;
|
|
278
|
+
/** A small drawing of several shapes, geometry only, for a picture of it. */
|
|
279
|
+
svg?: string;
|
|
280
|
+
}
|
|
281
|
+
/** What the source's look is made of (AGL-4106), read from its stylesheets. */
|
|
282
|
+
export interface SiteImportLookFacts {
|
|
283
|
+
fonts: {
|
|
284
|
+
/** The family its body text is set in, as named, when it is not a system font. */
|
|
285
|
+
body: string | null;
|
|
286
|
+
/** The family its headings are set in. */
|
|
287
|
+
headings: string | null;
|
|
288
|
+
/** The families it loads from Google Fonts, in order. */
|
|
289
|
+
google: string[];
|
|
290
|
+
};
|
|
291
|
+
/** The page's own background, `#rrggbb`. */
|
|
292
|
+
background: string | null;
|
|
293
|
+
/** Its body text color. */
|
|
294
|
+
text: string | null;
|
|
295
|
+
/** The fill of its filled buttons, the most used first. */
|
|
296
|
+
buttonFill: string | null;
|
|
297
|
+
/** The text color on them. */
|
|
298
|
+
buttonText: string | null;
|
|
299
|
+
/** Its buttons' corner radius, px. */
|
|
300
|
+
buttonRadius: number | null;
|
|
301
|
+
/** The color its text links are drawn in. */
|
|
302
|
+
link: string | null;
|
|
303
|
+
/** Its header bar's background. */
|
|
304
|
+
header: string | null;
|
|
305
|
+
}
|
|
306
|
+
/** A page's layout, as the source lays it out (AGL-4106). */
|
|
307
|
+
export interface SiteImportLayout {
|
|
308
|
+
v: 1;
|
|
309
|
+
sections: SiteImportLayoutSection[];
|
|
310
|
+
icons: SiteImportIconShape[];
|
|
311
|
+
look: SiteImportLookFacts;
|
|
312
|
+
}
|
|
313
|
+
/** One entry of a menu, as the source's header shows it. */
|
|
314
|
+
export interface SiteImportNavEntry {
|
|
315
|
+
label: string;
|
|
316
|
+
/** The line under it in a mega menu. */
|
|
317
|
+
text?: string;
|
|
318
|
+
/** Where it goes: absolute http(s), `mailto:` or `tel:`. */
|
|
319
|
+
href: string;
|
|
320
|
+
/** Its icon, when it has a one-path icon. */
|
|
321
|
+
iconPath?: string;
|
|
322
|
+
viewBox?: string;
|
|
323
|
+
/** The heading of the menu's column it sits in ("By team"). */
|
|
324
|
+
column?: string;
|
|
325
|
+
}
|
|
326
|
+
/** A top-level item of the source's menu: a link, or a menu of entries under a name. */
|
|
327
|
+
export interface SiteImportNavGroup {
|
|
328
|
+
label: string;
|
|
329
|
+
/** Where the name itself goes; `null` when it only opens its menu. */
|
|
330
|
+
href: string | null;
|
|
331
|
+
/** Empty for a plain link. */
|
|
332
|
+
items: SiteImportNavEntry[];
|
|
333
|
+
}
|
|
334
|
+
/** A button in the source's header outside its menu ("Sign in", "Start free"). */
|
|
335
|
+
export interface SiteImportNavCta {
|
|
336
|
+
label: string;
|
|
337
|
+
href: string | null;
|
|
338
|
+
style: SiteImportButtonStyle;
|
|
339
|
+
}
|
|
340
|
+
/** The source's header menu (AGL-4106). Untrusted data from the owner's site. */
|
|
341
|
+
export interface SiteImportNavTree {
|
|
342
|
+
items: SiteImportNavGroup[];
|
|
343
|
+
ctas: SiteImportNavCta[];
|
|
344
|
+
/** The source shows a menu button on phones. */
|
|
345
|
+
phoneMenu: boolean;
|
|
346
|
+
/** The header bar's background, `#rrggbb`. */
|
|
347
|
+
background: string | null;
|
|
348
|
+
}
|
|
349
|
+
export interface SiteImportFooterLink {
|
|
350
|
+
label: string;
|
|
351
|
+
href: string;
|
|
352
|
+
}
|
|
353
|
+
/** The source's footer (AGL-4106): its columns, profiles and facts, as written. */
|
|
354
|
+
export interface SiteImportFooter {
|
|
355
|
+
columns: Array<{
|
|
356
|
+
heading: string;
|
|
357
|
+
links: SiteImportFooterLink[];
|
|
358
|
+
}>;
|
|
359
|
+
/** Its links to profiles on other sites. */
|
|
360
|
+
social: Array<SiteImportFooterLink & {
|
|
361
|
+
network: string;
|
|
362
|
+
}>;
|
|
363
|
+
/** Links outside any column and not a profile (Privacy, Terms, Status). */
|
|
364
|
+
links: SiteImportFooterLink[];
|
|
365
|
+
/** Its lines of text outside links (a tagline), as written. */
|
|
366
|
+
text: string[];
|
|
367
|
+
address: string | null;
|
|
368
|
+
phone: string | null;
|
|
369
|
+
email: string | null;
|
|
370
|
+
/** Its © line, word for word. */
|
|
371
|
+
copyright: string | null;
|
|
372
|
+
}
|
|
373
|
+
/** Every line a layout's nodes place, in order. */
|
|
374
|
+
export declare function siteImportLayoutLines(nodes: readonly SiteImportLayoutNode[]): SiteImportLineRef[];
|
|
375
|
+
/** How many characters a layout weighs on the job document. */
|
|
376
|
+
export declare function siteImportLayoutChars(layout: SiteImportLayout | null | undefined): number;
|
|
377
|
+
/**
|
|
378
|
+
* A layout over only the first `count` lines (a read the wave's budget cut
|
|
379
|
+
* short): references past the cut are dropped, and what is left empty goes.
|
|
380
|
+
*/
|
|
381
|
+
export declare function siteImportLayoutWithin(layout: SiteImportLayout, count: number): SiteImportLayout;
|
|
382
|
+
/** Nodes with only the lines `keep` keeps; what is left empty goes. */
|
|
383
|
+
export declare function siteImportFilterNodes(list: readonly SiteImportLayoutNode[], keep: (ref: SiteImportLineRef) => boolean): SiteImportLayoutNode[];
|
|
384
|
+
/** A layout with its icons' drawings dropped, for a page that must be lighter. */
|
|
385
|
+
export declare function siteImportLayoutWithoutDrawings(layout: SiteImportLayout): SiteImportLayout;
|
|
133
386
|
/** What a wave's read found on one page, for its model answer. Never on the wire. */
|
|
134
387
|
export interface AiSiteImportPageRead {
|
|
135
388
|
at: string;
|
|
@@ -157,6 +410,21 @@ export interface AiSiteImportPageRead {
|
|
|
157
410
|
words?: number;
|
|
158
411
|
/** The forms the page asks visitors to fill in, by their fields. */
|
|
159
412
|
forms?: SiteImportForm[];
|
|
413
|
+
/**
|
|
414
|
+
* How the source lays the page out (AGL-4106): its sections, bands, rows,
|
|
415
|
+
* groups, pictures, icons and buttons, over `blocks` by index. Absent on a
|
|
416
|
+
* read made before the reader, or when it did not fit the wave's budget.
|
|
417
|
+
*/
|
|
418
|
+
layout?: SiteImportLayout | null;
|
|
419
|
+
/**
|
|
420
|
+
* The page's browser title and meta description as the source writes them
|
|
421
|
+
* (AGL-4107), which the new page's listing keeps word for word. Absent on a
|
|
422
|
+
* read made before they were kept.
|
|
423
|
+
*/
|
|
424
|
+
meta?: {
|
|
425
|
+
title: string;
|
|
426
|
+
description: string;
|
|
427
|
+
};
|
|
160
428
|
}
|
|
161
429
|
/** How a page is built from its old page, as the step chose: its words copied as they are, or arranged by Aglyn AI. */
|
|
162
430
|
export type AiSiteImportBuild = 'copied' | 'arranged';
|
|
@@ -193,7 +461,12 @@ export interface AiSiteImportPage {
|
|
|
193
461
|
assets: AiSiteImportPhoto[];
|
|
194
462
|
} | null;
|
|
195
463
|
}
|
|
196
|
-
|
|
464
|
+
/**
|
|
465
|
+
* A wave's place in its import: waiting to start, running, in and waiting for
|
|
466
|
+
* the person's review, in, or `stopped` — the import was canceled before the
|
|
467
|
+
* wave was in, while it ran or before it started (AGL-4107).
|
|
468
|
+
*/
|
|
469
|
+
export type AiSiteImportWaveStatus = 'waiting' | 'running' | 'review' | 'done' | 'stopped';
|
|
197
470
|
export interface AiSiteImportWave {
|
|
198
471
|
index: number;
|
|
199
472
|
label: string;
|
|
@@ -235,8 +508,6 @@ export interface AiSiteImportState {
|
|
|
235
508
|
frame: {
|
|
236
509
|
draftId: string;
|
|
237
510
|
} | null;
|
|
238
|
-
/** The Trust & Safety row this import is logged under. */
|
|
239
|
-
reviewId: string | null;
|
|
240
511
|
/**
|
|
241
512
|
* "Start every remaining wave" (AGL-4083): each wave closes and the next
|
|
242
513
|
* starts with no review between them; the last wave's close is the stop.
|
|
@@ -286,7 +557,24 @@ export interface AiSiteImportHome {
|
|
|
286
557
|
social: string[];
|
|
287
558
|
address: string;
|
|
288
559
|
};
|
|
560
|
+
/**
|
|
561
|
+
* The source's header menu as its home page builds it (AGL-4108): its
|
|
562
|
+
* items, their menus, its buttons. The header and footer are drawn from it
|
|
563
|
+
* with no model; absent on a read made before the reader, or where the home
|
|
564
|
+
* page has no menu the reader can find.
|
|
565
|
+
*/
|
|
566
|
+
nav?: SiteImportNavTree | null;
|
|
567
|
+
/** The source's footer as its home page builds it (AGL-4108): its columns, profiles, facts and © line. */
|
|
568
|
+
footer?: SiteImportFooter | null;
|
|
569
|
+
/** What the source's look is made of (AGL-4108): its colors, its button fill and corners, its fonts. */
|
|
570
|
+
look?: SiteImportLookFacts | null;
|
|
289
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Whether the site's header and footer are built from the source's own menu
|
|
574
|
+
* (AGL-4108), with no model: the home page's menu was read. Then the frame
|
|
575
|
+
* costs nothing and is quoted at nothing.
|
|
576
|
+
*/
|
|
577
|
+
export declare function aiSiteImportFrameIsRead(state: Pick<AiSiteImportState, 'home'> | null | undefined): boolean;
|
|
290
578
|
/** What the look pass did (AGL-4084): no model is asked. */
|
|
291
579
|
export interface AiSiteImportLook {
|
|
292
580
|
at: string;
|
|
@@ -417,7 +705,6 @@ export interface AiSiteImportInventoryInput {
|
|
|
417
705
|
existingPages: number;
|
|
418
706
|
/** Mints one draft id; the inventory names every page's draft before any is written. */
|
|
419
707
|
mintId: () => string;
|
|
420
|
-
reviewId?: string | null;
|
|
421
708
|
wavePages?: number;
|
|
422
709
|
}
|
|
423
710
|
/**
|
|
@@ -487,13 +774,20 @@ export declare function aiSiteImportPhotosIn(state: AiSiteImportState, slot: str
|
|
|
487
774
|
export declare function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState;
|
|
488
775
|
/** A wave is in: waiting for its review, or done when no wave follows it. */
|
|
489
776
|
export declare function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState;
|
|
777
|
+
/**
|
|
778
|
+
* The state of an import a person canceled (AGL-4107): every wave settled. A
|
|
779
|
+
* wave that ran, or was waiting to, is stopped; one already in, for review or
|
|
780
|
+
* done, is in. On 10/29 a canceled import still showed its second wave as
|
|
781
|
+
* running, with nothing running it.
|
|
782
|
+
*/
|
|
783
|
+
export declare function aiSiteImportCanceled(state: AiSiteImportState, at: string): AiSiteImportState;
|
|
490
784
|
/** The state as the console reads it: every page's read text left off. */
|
|
491
785
|
export declare function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null;
|
|
492
786
|
/**
|
|
493
787
|
* How a page is built (AGL-4083), decided by code: every page of a collection
|
|
494
|
-
* (a blog, the legal pages, news)
|
|
495
|
-
*
|
|
496
|
-
*
|
|
788
|
+
* (a blog, the legal pages, news) is copied as it is; every main page is
|
|
789
|
+
* arranged by Aglyn AI whatever its length (AGL-4106), its words kept as
|
|
790
|
+
* written. Both are laid out from the source's own layout.
|
|
497
791
|
*/
|
|
498
792
|
export declare function aiSiteImportPageBuild(page: Pick<AiSiteImportPage, 'group' | 'words'>): AiSiteImportBuild;
|
|
499
793
|
/**
|