@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
|
@@ -18,15 +18,21 @@
|
|
|
18
18
|
*
|
|
19
19
|
* The first wave's look pass, before the header and footer, with no model:
|
|
20
20
|
*
|
|
21
|
-
* - **The theme.** The owner's own home page is read once more as AglynBot
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
21
|
+
* - **The theme.** The owner's own home page is read once more as AglynBot,
|
|
22
|
+
* with the edge headers a site this platform serves gets whether or not a
|
|
23
|
+
* caller hands them in (AGL-4108: production handed none, our own edge
|
|
24
|
+
* challenged the read, and the 10/29 site kept the starter's indigo), and
|
|
25
|
+
* the signals a guided start reads off the sites a person likes
|
|
26
|
+
* (AGL-3975: colors, ground, type mood, corners, buttons, cards, header,
|
|
27
|
+
* density, motion) pin the look. What the reader found of the source's
|
|
28
|
+
* look (AGL-4106) pins it closer (AGL-4108): its buttons' fill is the
|
|
29
|
+
* brand and its links' color the accent, its page's ground is the site's,
|
|
30
|
+
* and its own font family is the site's wherever the theme font catalog
|
|
31
|
+
* has it, else a pairing of its type mood. The look is drawn and saved
|
|
32
|
+
* exactly as a guided start's (`aiSiteStyleFor`, `aiSaveSiteTheme`): a
|
|
33
|
+
* built-in base plus the brand colors and fonts, every color held to the
|
|
34
|
+
* theme's reading checks. A site whose owner already chose a theme keeps
|
|
35
|
+
* it.
|
|
30
36
|
* - **The logo.** The source header's logo, as the source draws it, into the
|
|
31
37
|
* site's media library once: an SVG is turned into a PNG 192 pixels tall
|
|
32
38
|
* (the library takes pictures, not drawings), a picture is kept as it is.
|
|
@@ -39,11 +45,13 @@
|
|
|
39
45
|
* site's theme, and a logo that cannot be brought in leaves the header its
|
|
40
46
|
* name.
|
|
41
47
|
*=========================================*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
48
|
+
import { themeFontFacts } from "@aglyn/aglyn/plugin-manager/plugin-theme-font-catalog";
|
|
42
49
|
import { aiSiteListingSeeded } from "../model/ai-site-entity.js";
|
|
43
50
|
import { aiSiteKindFor } from "../model/ai-site-kinds.js";
|
|
44
|
-
import { aiSiteSeed, aiSiteStyleFor } from "../model/ai-site-look.js";
|
|
51
|
+
import { aiHexOklch, aiSiteSeed, aiSiteStyleFor } from "../model/ai-site-look.js";
|
|
45
52
|
import { aiSiteIsStarterSrc } from "../model/ai-site-photos.js";
|
|
46
|
-
import { AGLYN_BOT_USER_AGENT } from "../site-import/site-import-crawl.js";
|
|
53
|
+
import { AGLYN_BOT_USER_AGENT, siteImportOrigins } from "../site-import/site-import-crawl.js";
|
|
54
|
+
import { NO_SITE_IMPORT_HEADERS, siteImportEdgeHeadersFor } from "../site-import/site-import-edge.js";
|
|
47
55
|
import { bringInSiteImportPhotos, siteImportImageKey, siteImportLogoUrl } from "../site-import/site-import-images.js";
|
|
48
56
|
import { aiSaveSiteTheme, aiWorkspaceSiteStyles } from "./ai-job-site-look.js";
|
|
49
57
|
import { aiSiteInspirationLookPins } from "./ai-site-inspiration.js";
|
|
@@ -52,36 +60,30 @@ import { AI_THEME_BRAND_COLORS_PER_SOURCE, AI_THEME_REFERENCE_MAX_BYTES, AI_THEM
|
|
|
52
60
|
/** The height an SVG logo is drawn at as a picture: crisp at twice the header's 40. */ export const AI_SITE_IMPORT_LOGO_HEIGHT = 192;
|
|
53
61
|
/** The largest logo file read. */ export const AI_SITE_IMPORT_LOGO_MAX_BYTES = 1024 * 1024;
|
|
54
62
|
/** One read's own clock in the look pass. */ const LOOK_READ_MS = 8000;
|
|
55
|
-
const sameHost = (a, b)=>{
|
|
56
|
-
try {
|
|
57
|
-
return new URL(a).host === new URL(b).host;
|
|
58
|
-
} catch (unused) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
63
|
/**
|
|
63
|
-
* The home page and its first stylesheets, read as AglynBot (AGL-4084)
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* The home page and its first stylesheets, read as AglynBot (AGL-4084).
|
|
65
|
+
* `headersFor` is asked for every request and every redirect hop, as the
|
|
66
|
+
* crawl asks it (`aiSiteImportLookHeaders`: the edge header only to the
|
|
67
|
+
* site's own origins). `null` when the page cannot be read.
|
|
66
68
|
*/ export async function readSiteImportLookPage(url, input) {
|
|
67
|
-
var _input_fetch;
|
|
69
|
+
var _input_fetch, _input_headersFor;
|
|
68
70
|
const fetchText = (_input_fetch = input.fetch) != null ? _input_fetch : fetchPublicTextDetailed;
|
|
69
|
-
const
|
|
70
|
-
const read = (address, maxBytes, accept
|
|
71
|
+
const headersFor = (_input_headersFor = input.headersFor) != null ? _input_headersFor : null;
|
|
72
|
+
const read = (address, maxBytes, accept)=>fetchText(address, _extends({
|
|
71
73
|
maxBytes,
|
|
72
74
|
accept,
|
|
73
75
|
userAgent: AGLYN_BOT_USER_AGENT,
|
|
74
76
|
timeoutMs: LOOK_READ_MS
|
|
75
77
|
}, input.signal ? {
|
|
76
78
|
signal: input.signal
|
|
77
|
-
} : {},
|
|
78
|
-
|
|
79
|
+
} : {}, headersFor ? {
|
|
80
|
+
headersFor
|
|
79
81
|
} : {})).catch(()=>null);
|
|
80
|
-
const page = await read(url, AI_THEME_REFERENCE_MAX_BYTES, /text\/html|application\/xhtml\+xml/i
|
|
82
|
+
const page = await read(url, AI_THEME_REFERENCE_MAX_BYTES, /text\/html|application\/xhtml\+xml/i);
|
|
81
83
|
if (!(page == null ? void 0 : page.ok)) return null;
|
|
82
84
|
const stylesheets = [];
|
|
83
85
|
for (const sheet of stylesheetUrlsOf(page.text, page.url).slice(0, AI_THEME_REFERENCE_STYLESHEETS)){
|
|
84
|
-
const css = await read(sheet, AI_THEME_STYLESHEET_MAX_BYTES, /text\/css/i
|
|
86
|
+
const css = await read(sheet, AI_THEME_STYLESHEET_MAX_BYTES, /text\/css/i);
|
|
85
87
|
if (css == null ? void 0 : css.ok) stylesheets.push(css.text);
|
|
86
88
|
}
|
|
87
89
|
return {
|
|
@@ -132,16 +134,40 @@ const hexOf = (value)=>{
|
|
|
132
134
|
}
|
|
133
135
|
return rankBrandColors(weights, AI_THEME_BRAND_COLORS_PER_SOURCE);
|
|
134
136
|
}
|
|
137
|
+
/** How colorful a color must be to carry a brand: the inspiration reader's own floor. */ const COLORFUL_CHROMA = 0.04;
|
|
138
|
+
const colorful = (hex)=>!!hex && /^#[0-9a-f]{6}$/i.test(hex) && aiHexOklch(hex).C >= COLORFUL_CHROMA;
|
|
139
|
+
/**
|
|
140
|
+
* The brand and accent a source's look facts give (AGL-4108): the fill of
|
|
141
|
+
* its filled buttons as the brand, the color of its links as the accent
|
|
142
|
+
* where it is a color of its own, and the reverse where its buttons are not
|
|
143
|
+
* colorful. The theme draws the brand at the nearest shade of it that reads
|
|
144
|
+
* (`aiSiteColors`): aglyn.com's light blue buttons, under white words at
|
|
145
|
+
* 2.4:1, come out at the deeper blue of its own links.
|
|
146
|
+
*/ export function aiSiteImportBrandOf(facts) {
|
|
147
|
+
const fill = colorful(facts == null ? void 0 : facts.buttonFill) ? (facts == null ? void 0 : facts.buttonFill).toLowerCase() : null;
|
|
148
|
+
const link = colorful(facts == null ? void 0 : facts.link) ? (facts == null ? void 0 : facts.link).toLowerCase() : null;
|
|
149
|
+
const brand = fill != null ? fill : link;
|
|
150
|
+
if (!brand) return null;
|
|
151
|
+
return {
|
|
152
|
+
brand,
|
|
153
|
+
accent: link && link !== brand ? link : null
|
|
154
|
+
};
|
|
155
|
+
}
|
|
135
156
|
/**
|
|
136
157
|
* The look a source's signals pin (AGL-4084), drawn the way a guided start's
|
|
137
158
|
* is: the kind of site its own description says, the import's seed, its
|
|
138
|
-
* strongest color as the brand.
|
|
159
|
+
* strongest color as the brand. What the reader found of its look
|
|
160
|
+
* (`facts`, AGL-4108) pins it closer: its buttons' fill as the brand and its
|
|
161
|
+
* links' color as the accent, its page's ground, and its own fonts where the
|
|
162
|
+
* catalog has them (`fonts`). `null` where the source showed no color to
|
|
139
163
|
* carry a brand, so the site keeps the theme it has.
|
|
140
164
|
*/ export function aiSiteImportStyle(site, input) {
|
|
141
|
-
var _input_avoid;
|
|
142
|
-
|
|
165
|
+
var _input_facts, _input_avoid;
|
|
166
|
+
const facts = (_input_facts = input.facts) != null ? _input_facts : null;
|
|
167
|
+
const own = aiSiteImportBrandOf(facts);
|
|
168
|
+
if (!site && !own) return null;
|
|
143
169
|
const kind = aiSiteKindFor(input.description);
|
|
144
|
-
const pins = aiSiteInspirationLookPins({
|
|
170
|
+
const pins = site ? aiSiteInspirationLookPins({
|
|
145
171
|
v: 1,
|
|
146
172
|
sites: [
|
|
147
173
|
site
|
|
@@ -150,9 +176,21 @@ const hexOf = (value)=>{
|
|
|
150
176
|
}, {
|
|
151
177
|
kind,
|
|
152
178
|
seed: input.seed
|
|
153
|
-
});
|
|
179
|
+
}) : {};
|
|
180
|
+
if (own) {
|
|
181
|
+
pins.brand = own.brand;
|
|
182
|
+
pins.hue = Math.round(aiHexOklch(own.brand).h) % 360;
|
|
183
|
+
if (own.accent) pins.accent = Math.round(aiHexOklch(own.accent).h) % 360;
|
|
184
|
+
}
|
|
185
|
+
// Its buttons' corners round the theme's shape where its stylesheets did not say (AGL-4108).
|
|
186
|
+
const radius = facts == null ? void 0 : facts.buttonRadius;
|
|
187
|
+
if (typeof radius === 'number' && Number.isFinite(radius) && radius >= 0) {
|
|
188
|
+
var _pins, _buttons, _pins1, _corners;
|
|
189
|
+
(_buttons = (_pins = pins).buttons) != null ? _buttons : _pins.buttons = radius >= 24 ? 'pill' : radius <= 2 ? 'square' : 'rounded';
|
|
190
|
+
(_corners = (_pins1 = pins).corners) != null ? _corners : _pins1.corners = radius <= 2 ? 'sharp' : radius <= 10 ? 'soft' : radius <= 20 ? 'round' : 'pill';
|
|
191
|
+
}
|
|
154
192
|
if (!pins.brand) return null;
|
|
155
|
-
|
|
193
|
+
const style = aiSiteStyleFor({
|
|
156
194
|
kind,
|
|
157
195
|
answer: {},
|
|
158
196
|
seed: input.seed,
|
|
@@ -160,6 +198,46 @@ const hexOf = (value)=>{
|
|
|
160
198
|
avoid: (_input_avoid = input.avoid) != null ? _input_avoid : [],
|
|
161
199
|
pins
|
|
162
200
|
});
|
|
201
|
+
const page = (facts == null ? void 0 : facts.background) && /^#[0-9a-f]{6}$/i.test(facts.background) ? facts.background.toLowerCase() : null;
|
|
202
|
+
return _extends({}, style, page ? {
|
|
203
|
+
page
|
|
204
|
+
} : {}, input.fonts ? {
|
|
205
|
+
sourceFonts: input.fonts
|
|
206
|
+
} : {});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The source's own fonts (AGL-4108), each where the theme font catalog has
|
|
210
|
+
* the family (Google Fonts', as the fonts plugin registers it), or the
|
|
211
|
+
* source loads it from Google Fonts itself: its body's and its headings'.
|
|
212
|
+
* `null` where its body's family is neither, and the pairing of the source's
|
|
213
|
+
* type mood sets the site instead.
|
|
214
|
+
*/ export async function aiSiteImportSourceFonts(facts, lookup = themeFontFacts) {
|
|
215
|
+
const fonts = facts == null ? void 0 : facts.fonts;
|
|
216
|
+
if (!(fonts == null ? void 0 : fonts.body)) return null;
|
|
217
|
+
const loaded = new Set(fonts.google.map((family)=>family.toLowerCase()));
|
|
218
|
+
const resolve = async (family)=>{
|
|
219
|
+
if (!family) return null;
|
|
220
|
+
const known = await lookup(family).catch(()=>undefined);
|
|
221
|
+
if (known) return {
|
|
222
|
+
family: known.family,
|
|
223
|
+
category: known.category,
|
|
224
|
+
weights: [
|
|
225
|
+
...known.weights
|
|
226
|
+
]
|
|
227
|
+
};
|
|
228
|
+
return loaded.has(family.toLowerCase()) ? {
|
|
229
|
+
family,
|
|
230
|
+
category: 'sans-serif',
|
|
231
|
+
weights: []
|
|
232
|
+
} : null;
|
|
233
|
+
};
|
|
234
|
+
const body = await resolve(fonts.body);
|
|
235
|
+
if (!body) return null;
|
|
236
|
+
const heading = fonts.headings && fonts.headings.toLowerCase() !== body.family.toLowerCase() ? await resolve(fonts.headings) : null;
|
|
237
|
+
return {
|
|
238
|
+
body,
|
|
239
|
+
heading
|
|
240
|
+
};
|
|
163
241
|
}
|
|
164
242
|
/**
|
|
165
243
|
* The site's listing in the home page's own words (AGL-4084), as field
|
|
@@ -231,8 +309,8 @@ async function writeHostDoc(firestore, hostId, updates) {
|
|
|
231
309
|
binary: true
|
|
232
310
|
}, input.signal ? {
|
|
233
311
|
signal: input.signal
|
|
234
|
-
} : {}, input.
|
|
235
|
-
|
|
312
|
+
} : {}, input.headersFor ? {
|
|
313
|
+
headersFor: input.headersFor
|
|
236
314
|
} : {}));
|
|
237
315
|
if (!fetched.ok || !((_fetched_data = fetched.data) == null ? void 0 : _fetched_data.byteLength)) return null;
|
|
238
316
|
const type = fetched.contentType.split(';')[0].trim().toLowerCase();
|
|
@@ -281,24 +359,14 @@ async function writeHostDoc(firestore, hostId, updates) {
|
|
|
281
359
|
}
|
|
282
360
|
}
|
|
283
361
|
/** The look pass (AGL-4084): the theme, the logo and the listing, from the owner's own home page. No model. */ export async function aiRunSiteImportLook(input, deps = {}) {
|
|
284
|
-
var _deps_others, _deps_ingest, _deps_readHost, _ref;
|
|
362
|
+
var _deps_others, _home_look, _deps_ingest, _deps_readHost, _ref;
|
|
285
363
|
const { firestore, hostId, home } = input;
|
|
286
|
-
|
|
287
|
-
if (deps.headersFor) {
|
|
288
|
-
try {
|
|
289
|
-
headers = await deps.headersFor(input.domain);
|
|
290
|
-
} catch (error) {
|
|
291
|
-
console.warn('[site-import] the edge headers could not be read; reading the look without them', {
|
|
292
|
-
domain: input.domain,
|
|
293
|
-
error
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
}
|
|
364
|
+
const headersFor = await aiSiteImportLookHeaders(input.domain, deps);
|
|
297
365
|
const seed = aiSiteSeed(input.jobId);
|
|
298
366
|
const page = await readSiteImportLookPage(home.url, _extends({}, deps.fetch ? {
|
|
299
367
|
fetch: deps.fetch
|
|
300
368
|
} : {}, {
|
|
301
|
-
|
|
369
|
+
headersFor
|
|
302
370
|
}, input.signal ? {
|
|
303
371
|
signal: input.signal
|
|
304
372
|
} : {}));
|
|
@@ -311,10 +379,15 @@ async function writeHostDoc(firestore, hostId, updates) {
|
|
|
311
379
|
orgId: input.orgId,
|
|
312
380
|
hostId
|
|
313
381
|
});
|
|
382
|
+
// What the reader found of its look, its buttons, ground and fonts (AGL-4108).
|
|
383
|
+
const facts = (_home_look = home.look) != null ? _home_look : null;
|
|
384
|
+
const fonts = await aiSiteImportSourceFonts(facts, deps.fontFacts);
|
|
314
385
|
const style = aiSiteImportStyle(site, {
|
|
315
386
|
seed,
|
|
316
387
|
description: `${home.siteName} ${home.description}`,
|
|
317
|
-
avoid
|
|
388
|
+
avoid,
|
|
389
|
+
facts,
|
|
390
|
+
fonts
|
|
318
391
|
});
|
|
319
392
|
let theme = 'unread';
|
|
320
393
|
if (style) {
|
|
@@ -334,7 +407,7 @@ async function writeHostDoc(firestore, hostId, updates) {
|
|
|
334
407
|
domain: input.domain,
|
|
335
408
|
logo: home.brand.logo,
|
|
336
409
|
alt: home.siteName || home.brand.text,
|
|
337
|
-
|
|
410
|
+
headersFor
|
|
338
411
|
}, input.signal ? {
|
|
339
412
|
signal: input.signal
|
|
340
413
|
} : {}), _extends({
|
|
@@ -388,15 +461,53 @@ async function writeHostDoc(firestore, hostId, updates) {
|
|
|
388
461
|
} : {}, typeof style.motion === 'string' ? {
|
|
389
462
|
motion: style.motion
|
|
390
463
|
} : {}) : null;
|
|
464
|
+
const colors = [
|
|
465
|
+
...new Set([
|
|
466
|
+
...facts ? [
|
|
467
|
+
facts.buttonFill,
|
|
468
|
+
facts.link
|
|
469
|
+
].filter(colorful).map((hex)=>hex.toLowerCase()) : [],
|
|
470
|
+
...(_ref = site == null ? void 0 : site.colors) != null ? _ref : []
|
|
471
|
+
])
|
|
472
|
+
];
|
|
391
473
|
return {
|
|
392
474
|
at: input.at,
|
|
393
475
|
theme,
|
|
394
|
-
colors
|
|
476
|
+
colors,
|
|
395
477
|
logo,
|
|
396
478
|
seo,
|
|
397
479
|
seed: style ? style.seed : seed,
|
|
398
480
|
style: drawn
|
|
399
481
|
};
|
|
400
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* The headers the look's reads of the source go with, per address (AGL-4108),
|
|
485
|
+
* exactly as the crawl's (`siteRequestHeaders`): the caller's where it hands
|
|
486
|
+
* them in, else the edge's own, so a site this platform serves is read past
|
|
487
|
+
* its challenge however the step was registered; and nothing for any address
|
|
488
|
+
* off the site's two origins, a redirect hop's included. Never throws.
|
|
489
|
+
*
|
|
490
|
+
* The 10/29 look read went without the header twice over: no caller handed
|
|
491
|
+
* one in, and the read passed headers the fetch does not take, which is why
|
|
492
|
+
* this is a function the fetch asks for every hop (`headersFor`).
|
|
493
|
+
*/ export async function aiSiteImportLookHeaders(domain, deps) {
|
|
494
|
+
const origins = siteImportOrigins(domain);
|
|
495
|
+
let inner = NO_SITE_IMPORT_HEADERS;
|
|
496
|
+
try {
|
|
497
|
+
if (deps.headersFor) {
|
|
498
|
+
const headers = await deps.headersFor(domain);
|
|
499
|
+
if (headers && Object.keys(headers).length) inner = ()=>headers;
|
|
500
|
+
} else {
|
|
501
|
+
var _deps_edgeHeadersFor;
|
|
502
|
+
inner = await ((_deps_edgeHeadersFor = deps.edgeHeadersFor) != null ? _deps_edgeHeadersFor : siteImportEdgeHeadersFor)(origins);
|
|
503
|
+
}
|
|
504
|
+
} catch (error) {
|
|
505
|
+
console.warn('[site-import] the edge headers could not be read; reading the look without them', {
|
|
506
|
+
domain,
|
|
507
|
+
error
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return (url)=>origins.includes(url.origin) ? inner(url) : {};
|
|
511
|
+
}
|
|
401
512
|
|
|
402
513
|
//# sourceMappingURL=ai-site-import-look.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-import-look.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 * THE SITE BROUGHT IN KEEPS ITS OWN LOOK (AGL-4084).\n *\n * The first wave's look pass, before the header and footer, with no model:\n *\n * - **The theme.** The owner's own home page is read once more as AglynBot\n * (with the edge headers a site this platform serves gets), and the\n * signals a guided start reads off the sites a person likes (AGL-3975:\n * colors, ground, type mood, corners, buttons, cards, header, density,\n * motion) pin the look: its strongest colorful color is the brand, its\n * second the accent. The look is drawn and saved exactly as a guided\n * start's (`aiSiteStyleFor`, `aiSaveSiteTheme`): a built-in base plus the\n * brand colors and a pairing of the source's type mood, never its font.\n * A site whose owner already chose a theme keeps it.\n * - **The logo.** The source header's logo, as the source draws it, into the\n * site's media library once: an SVG is turned into a PNG 192 pixels tall\n * (the library takes pictures, not drawings), a picture is kept as it is.\n * - **The listing.** The home page's own title, description and share\n * picture become the site's listing, only while the site still has the\n * listing it was created with: an owner's own words are never written\n * over.\n *\n * Nothing here fails a wave: a source whose look cannot be read keeps the\n * site's theme, and a logo that cannot be brought in leaves the header its\n * name.\n *=========================================*/\n\nimport type { PluginMediaIngest } from '@aglyn/aglyn/plugin-manager/plugin-media-ingest'\nimport { aiSiteListingSeeded } from '../model/ai-site-entity'\nimport type { AiSiteImportHome, AiSiteImportLook, AiSiteImportPhoto } from '../model/ai-site-import'\nimport { aiSiteKindFor } from '../model/ai-site-kinds'\nimport { aiSiteSeed, aiSiteStyleFor, type AiSiteStyle } from '../model/ai-site-look'\nimport { aiSiteIsStarterSrc } from '../model/ai-site-photos'\nimport { AGLYN_BOT_USER_AGENT, type SiteImportCrawlSeams } from '../site-import/site-import-crawl'\nimport { bringInSiteImportPhotos, siteImportImageKey, siteImportLogoUrl } from '../site-import/site-import-images'\nimport { aiSaveSiteTheme, aiWorkspaceSiteStyles } from './ai-job-site-look'\nimport { aiSiteInspirationLookPins } from './ai-site-inspiration'\nimport { aiSiteInspirationSignals, type AiSiteInspirationSite } from './ai-site-inspiration-signals'\nimport {\n AI_THEME_BRAND_COLORS_PER_SOURCE,\n AI_THEME_REFERENCE_MAX_BYTES,\n AI_THEME_REFERENCE_STYLESHEETS,\n AI_THEME_STYLESHEET_MAX_BYTES,\n AI_THEME_THEME_COLOR_WEIGHT,\n fetchPublicTextDetailed,\n rankBrandColors,\n stylesheetUrlsOf,\n type AiReferencePage,\n type PublicFetchOptions,\n type PublicFetchResult,\n} from './ai-theme-brand-inputs'\n\n/** The height an SVG logo is drawn at as a picture: crisp at twice the header's 40. */\nexport const AI_SITE_IMPORT_LOGO_HEIGHT = 192\n\n/** The largest logo file read. */\nexport const AI_SITE_IMPORT_LOGO_MAX_BYTES = 1024 * 1024\n\n/** One read's own clock in the look pass. */\nconst LOOK_READ_MS = 8_000\n\ntype Fetch = (url: string, options: PublicFetchOptions) => Promise<PublicFetchResult>\n\nconst sameHost = (a: string, b: string) => {\n try {\n return new URL(a).host === new URL(b).host\n } catch {\n return false\n }\n}\n\n/**\n * The home page and its first stylesheets, read as AglynBot (AGL-4084), the\n * edge headers going with the page and with a stylesheet only on the page's\n * own host. `null` when the page cannot be read.\n */\nexport async function readSiteImportLookPage(\n url: string,\n input: { fetch?: Fetch; headers?: Readonly<Record<string, string>> | null; signal?: AbortSignal },\n): Promise<AiReferencePage | null> {\n const fetchText = input.fetch ?? fetchPublicTextDetailed\n const headers = input.headers && Object.keys(input.headers).length ? input.headers : null\n const read = (address: string, maxBytes: number, accept: RegExp, own: boolean) =>\n fetchText(address, {\n maxBytes,\n accept,\n userAgent: AGLYN_BOT_USER_AGENT,\n timeoutMs: LOOK_READ_MS,\n ...(input.signal ? { signal: input.signal } : {}),\n ...(headers && own ? { headers } : {}),\n }).catch(() => null)\n const page = await read(url, AI_THEME_REFERENCE_MAX_BYTES, /text\\/html|application\\/xhtml\\+xml/i, true)\n if (!page?.ok) return null\n const stylesheets: string[] = []\n for (const sheet of stylesheetUrlsOf(page.text, page.url).slice(0, AI_THEME_REFERENCE_STYLESHEETS)) {\n const css = await read(sheet, AI_THEME_STYLESHEET_MAX_BYTES, /text\\/css/i, sameHost(sheet, page.url))\n if (css?.ok) stylesheets.push(css.text)\n }\n return { url: page.url, html: page.text, stylesheets }\n}\n\nconst SOLID_HEX = /(?<!&)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?![0-9a-z])/gi\nconst SOLID_RGB = /rgba?\\(\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*(?:[,/]\\s*([\\d.]+%?)\\s*)?\\)/gi\nconst THEME_COLOR = /<meta\\b[^>]*\\bname\\s*=\\s*[\"']?theme-color\\b[^>]*\\bcontent\\s*=\\s*[\"']?\\s*(#[0-9a-f]{3,6})/gi\n\nconst hexOf = (value: string): string | null => {\n const raw = value.trim().toLowerCase()\n if (/^#[0-9a-f]{6}$/.test(raw)) return raw\n if (/^#[0-9a-f]{3}$/.test(raw)) return `#${raw[1]}${raw[1]}${raw[2]}${raw[2]}${raw[3]}${raw[3]}`\n return null\n}\n\n/**\n * A source's brand colors as it DRAWS them (AGL-4084): the colors its markup\n * and stylesheets use solid, most used first, the colorful before the greys.\n * A translucent color is a tint of one (a hover, a focus ring), never a use\n * of it, so it is not counted: aglyn.com's focus rings, a light blue at a\n * quarter strength, outnumbered the deep blue its buttons and links are\n * drawn in, which the theme reader would otherwise take as its brand.\n */\nexport function aiSiteImportBrandColors(page: Pick<AiReferencePage, 'html' | 'stylesheets'>): string[] {\n const weights = new Map<string, number>()\n const add = (hex: string | null, weight = 1) => {\n if (hex) weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n for (const match of page.html.matchAll(THEME_COLOR)) add(hexOf(match[1] ?? ''), AI_THEME_THEME_COLOR_WEIGHT)\n for (const text of [page.html, ...page.stylesheets]) {\n for (const match of text.matchAll(SOLID_HEX)) add(hexOf(match[0]))\n for (const match of text.matchAll(SOLID_RGB)) {\n const alpha = match[4]\n if (alpha !== undefined && (alpha.endsWith('%') ? Number(alpha.slice(0, -1)) < 100 : Number(alpha) < 1)) continue\n const channels = [match[1], match[2], match[3]].map((value) => Math.min(255, Number(value)))\n add(`#${channels.map((value) => value.toString(16).padStart(2, '0')).join('')}`)\n }\n }\n return rankBrandColors(weights, AI_THEME_BRAND_COLORS_PER_SOURCE)\n}\n\n/**\n * The look a source's signals pin (AGL-4084), drawn the way a guided start's\n * is: the kind of site its own description says, the import's seed, its\n * strongest color as the brand. `null` where the source showed no color to\n * carry a brand, so the site keeps the theme it has.\n */\nexport function aiSiteImportStyle(\n site: AiSiteInspirationSite | null,\n input: { seed: number; description: string; avoid?: readonly AiSiteStyle[] },\n): AiSiteStyle | null {\n if (!site) return null\n const kind = aiSiteKindFor(input.description)\n const pins = aiSiteInspirationLookPins({ v: 1, sites: [site], unread: 0 }, { kind, seed: input.seed })\n if (!pins.brand) return null\n return aiSiteStyleFor({ kind, answer: {}, seed: input.seed, brand: pins.brand, avoid: input.avoid ?? [], pins })\n}\n\n/**\n * The site's listing in the home page's own words (AGL-4084), as field\n * paths: its title and description each only where the site still has the\n * one it was created with, so an owner's own listing is never written over.\n * `{}` with nothing to write.\n */\nexport function aiSiteImportListingUpdates(seo: unknown, displayName: unknown, home: Pick<AiSiteImportHome, 'title' | 'description'>): Record<string, string> {\n const seeded = aiSiteListingSeeded(seo, displayName)\n const current = (seo && typeof seo === 'object' ? seo : {}) as Record<string, unknown>\n const updates: Record<string, string> = {}\n const title = home.title.replace(/\\s+/g, ' ').trim()\n const description = home.description.replace(/\\s+/g, ' ').trim()\n if (title && seeded.title && title !== current['title']) updates['seo.title'] = title\n if (description && seeded.description && description !== current['description']) updates['seo.description'] = description\n return updates\n}\n\n/** Whether the site's share picture is still open: none, or the starter's (AGL-3988). */\nfunction shareOpen(seo: unknown): boolean {\n const image = (seo as { image?: unknown } | null | undefined)?.image\n if (image === undefined || image === null) return true\n return typeof image === 'string' && (!image.trim() || aiSiteIsStarterSrc(image))\n}\n\n/** Turns an SVG drawing into a PNG of a height; `null` where it cannot. */\nexport type AiSiteImportRasterize = (svg: Uint8Array, height: number) => Promise<Uint8Array | null>\n\nexport interface AiSiteImportLookDeps {\n fetch?: Fetch\n headersFor?: SiteImportCrawlSeams['headersFor']\n save?: typeof aiSaveSiteTheme\n others?: typeof aiWorkspaceSiteStyles\n ingest?: PluginMediaIngest | null\n rasterize?: AiSiteImportRasterize\n /** The site's document, as the listing reads it. */\n readHost?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<Record<string, unknown> | null>\n writeHost?: (firestore: FirebaseFirestore.Firestore, hostId: string, updates: Record<string, unknown>) => Promise<void>\n}\n\nexport interface AiSiteImportLookInput {\n firestore: FirebaseFirestore.Firestore\n hostId: string\n orgId: string\n /** The member the logo and the share picture are stored as. */\n uid: string\n /** The import job's id, which the look's seed is drawn from. */\n jobId: string\n domain: string\n home: AiSiteImportHome\n at: string\n signal?: AbortSignal\n}\n\nasync function readHostDoc(firestore: FirebaseFirestore.Firestore, hostId: string): Promise<Record<string, unknown> | null> {\n const snapshot = await firestore.collection('hosts').doc(hostId).get()\n return snapshot.exists ? ((snapshot.data() ?? {}) as Record<string, unknown>) : null\n}\n\nasync function writeHostDoc(firestore: FirebaseFirestore.Firestore, hostId: string, updates: Record<string, unknown>): Promise<void> {\n await firestore.collection('hosts').doc(hostId).update(updates)\n}\n\n/** Turns an SVG into a PNG in this process, where `sharp` is shipped; `null` where it is not. */\nasync function rasterizeHere(svg: Uint8Array, height: number): Promise<Uint8Array | null> {\n const images = await import('./ai-image-reads')\n const origin = images.aiImageReadsOrigin()\n if (origin) return images.requestAiLogoRaster(origin, svg, height)\n return images.rasterizeAiLogoSvg(svg, height)\n}\n\n/**\n * The source header's logo in the site's library (AGL-4084), once: the copy\n * a page already brought in is reused by its key. An SVG becomes a PNG.\n * `null` where it cannot be brought in, and the header then prints the name.\n */\nexport async function aiSiteImportLogo(\n input: { hostId: string; uid: string; domain: string; logo: NonNullable<AiSiteImportHome['brand']['logo']>; alt: string; headers?: Readonly<Record<string, string>> | null; signal?: AbortSignal },\n deps: { fetch?: Fetch; ingest: PluginMediaIngest | null; rasterize?: AiSiteImportRasterize },\n): Promise<AiSiteImportPhoto | null> {\n const ingest = deps.ingest\n const url = siteImportLogoUrl(input.logo.url)\n if (!ingest?.findImportedPhoto || !url) return null\n const sourceKey = siteImportImageKey(url)\n try {\n const held = await ingest.findImportedPhoto({ hostId: input.hostId, sourceKey })\n if (held) return { mediaId: held.mediaId, src: held.src, alt: input.alt, from: url, ...(held.width ? { width: held.width } : {}), ...(held.height ? { height: held.height } : {}) }\n const fetched = await (deps.fetch ?? fetchPublicTextDetailed)(url, {\n maxBytes: AI_SITE_IMPORT_LOGO_MAX_BYTES,\n accept: /^image\\//i,\n acceptHeader: 'image/svg+xml,image/png,image/webp,image/jpeg;q=0.9',\n userAgent: AGLYN_BOT_USER_AGENT,\n timeoutMs: LOOK_READ_MS,\n refuseOver: true,\n binary: true,\n ...(input.signal ? { signal: input.signal } : {}),\n ...(input.headers && sameHost(url, `https://${input.domain}/`) ? { headers: input.headers } : {}),\n })\n if (!fetched.ok || !fetched.data?.byteLength) return null\n const type = fetched.contentType.split(';')[0].trim().toLowerCase()\n let bytes: Uint8Array | null = fetched.data\n let contentType = type\n if (type === 'image/svg+xml' || input.logo.type === 'svg') {\n bytes = await (deps.rasterize ?? rasterizeHere)(fetched.data, AI_SITE_IMPORT_LOGO_HEIGHT).catch(() => null)\n contentType = 'image/png'\n } else if (!/^image\\/(?:png|jpeg|webp|gif)$/.test(type)) {\n return null\n }\n if (!bytes?.byteLength) return null\n const stored = await ingest.ingest({\n hostId: input.hostId,\n uid: input.uid,\n fileName: `logo.${contentType === 'image/jpeg' ? 'jpg' : contentType.split('/')[1]}`,\n contentType,\n bytes,\n ...(input.alt ? { alt: input.alt.slice(0, 250) } : {}),\n importedFrom: { key: sourceKey, url, domain: input.domain },\n })\n if (!stored.ok) return null\n return { mediaId: stored.mediaId, src: stored.src, alt: input.alt, from: url, ...(stored.width ? { width: stored.width } : {}), ...(stored.height ? { height: stored.height } : {}) }\n } catch (error) {\n console.warn('[site-import] the logo could not be brought in', { hostId: input.hostId, error })\n return null\n }\n}\n\n/** The look pass (AGL-4084): the theme, the logo and the listing, from the owner's own home page. No model. */\nexport async function aiRunSiteImportLook(input: AiSiteImportLookInput, deps: AiSiteImportLookDeps = {}): Promise<AiSiteImportLook> {\n const { firestore, hostId, home } = input\n let headers: Readonly<Record<string, string>> | null = null\n if (deps.headersFor) {\n try {\n headers = await deps.headersFor(input.domain)\n } catch (error) {\n console.warn('[site-import] the edge headers could not be read; reading the look without them', { domain: input.domain, error })\n }\n }\n const seed = aiSiteSeed(input.jobId)\n const page = await readSiteImportLookPage(home.url, { ...(deps.fetch ? { fetch: deps.fetch } : {}), headers, ...(input.signal ? { signal: input.signal } : {}) })\n // The source's own signals, its brand colors as it draws them solid.\n const signals = page ? aiSiteInspirationSignals(page) : null\n const site = signals && page ? { ...signals, colors: aiSiteImportBrandColors(page) } : signals\n const avoid = await (deps.others ?? aiWorkspaceSiteStyles)(firestore, { orgId: input.orgId, hostId })\n const style = aiSiteImportStyle(site, { seed, description: `${home.siteName} ${home.description}`, avoid })\n let theme: AiSiteImportLook['theme'] = 'unread'\n if (style) {\n const saved = await (deps.save ?? aiSaveSiteTheme)(firestore, { hostId, jobId: input.jobId, style })\n if (saved.write === 'missing') throw new Error(`site ${hostId} vanished while its look was brought in`)\n theme = saved.write === 'kept' ? 'kept' : 'applied'\n }\n const ingest = deps.ingest ?? null\n const logo = home.brand.logo\n ? await aiSiteImportLogo(\n { hostId, uid: input.uid, domain: input.domain, logo: home.brand.logo, alt: home.siteName || home.brand.text, headers, ...(input.signal ? { signal: input.signal } : {}) },\n { ingest, ...(deps.fetch ? { fetch: deps.fetch } : {}), ...(deps.rasterize ? { rasterize: deps.rasterize } : {}) },\n )\n : null\n // The site's listing in its own words, only over the one it was created with.\n let seo = false\n const host = await (deps.readHost ?? readHostDoc)(firestore, hostId)\n if (host) {\n const updates: Record<string, unknown> = aiSiteImportListingUpdates(host['seo'], host['displayName'], home)\n if (home.ogImage && shareOpen(host['seo'])) {\n const share = await bringInSiteImportPhotos(\n { hostId, uid: input.uid, domain: input.domain, images: [{ url: home.ogImage, alt: '' }], deadlineAt: Date.now() + LOOK_READ_MS, ...(input.signal ? { signal: input.signal } : {}) },\n { ingest, ...(deps.fetch ? { fetch: deps.fetch } : {}) },\n )\n const photo = share.assets[0]\n if (photo) {\n updates['seo.image'] = photo.src\n if (photo.width) updates['seo.imageWidth'] = photo.width\n if (photo.height) updates['seo.imageHeight'] = photo.height\n }\n }\n if (Object.keys(updates).length) {\n await (deps.writeHost ?? writeHostDoc)(firestore, hostId, updates)\n seo = true\n }\n }\n const drawn = style\n ? {\n ...(typeof style.headerAlign === 'string' ? { headerAlign: style.headerAlign } : {}),\n ...(typeof style.rhythm === 'string' ? { rhythm: style.rhythm } : {}),\n ...(typeof style.motion === 'string' ? { motion: style.motion } : {}),\n }\n : null\n return { at: input.at, theme, colors: site?.colors ?? [], logo, seo, seed: style ? style.seed : seed, style: drawn }\n}\n"],"names":["aiSiteListingSeeded","aiSiteKindFor","aiSiteSeed","aiSiteStyleFor","aiSiteIsStarterSrc","AGLYN_BOT_USER_AGENT","bringInSiteImportPhotos","siteImportImageKey","siteImportLogoUrl","aiSaveSiteTheme","aiWorkspaceSiteStyles","aiSiteInspirationLookPins","aiSiteInspirationSignals","AI_THEME_BRAND_COLORS_PER_SOURCE","AI_THEME_REFERENCE_MAX_BYTES","AI_THEME_REFERENCE_STYLESHEETS","AI_THEME_STYLESHEET_MAX_BYTES","AI_THEME_THEME_COLOR_WEIGHT","fetchPublicTextDetailed","rankBrandColors","stylesheetUrlsOf","AI_SITE_IMPORT_LOGO_HEIGHT","AI_SITE_IMPORT_LOGO_MAX_BYTES","LOOK_READ_MS","sameHost","a","b","URL","host","readSiteImportLookPage","url","input","fetchText","fetch","headers","Object","keys","length","read","address","maxBytes","accept","own","userAgent","timeoutMs","signal","catch","page","ok","stylesheets","sheet","text","slice","css","push","html","SOLID_HEX","SOLID_RGB","THEME_COLOR","hexOf","value","raw","trim","toLowerCase","test","aiSiteImportBrandColors","match","weights","Map","add","hex","weight","set","get","matchAll","alpha","undefined","endsWith","Number","channels","map","Math","min","toString","padStart","join","aiSiteImportStyle","site","kind","description","pins","v","sites","unread","seed","brand","answer","avoid","aiSiteImportListingUpdates","seo","displayName","home","seeded","current","updates","title","replace","shareOpen","image","readHostDoc","firestore","hostId","snapshot","collection","doc","exists","data","writeHostDoc","update","rasterizeHere","svg","height","images","origin","aiImageReadsOrigin","requestAiLogoRaster","rasterizeAiLogoSvg","aiSiteImportLogo","deps","ingest","logo","findImportedPhoto","sourceKey","fetched","held","mediaId","src","alt","from","width","acceptHeader","refuseOver","binary","domain","byteLength","type","contentType","split","bytes","rasterize","stored","uid","fileName","importedFrom","key","error","console","warn","aiRunSiteImportLook","headersFor","jobId","signals","colors","others","orgId","style","siteName","theme","saved","save","write","Error","readHost","ogImage","share","deadlineAt","Date","now","photo","assets","writeHost","drawn","headerAlign","rhythm","motion","at"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-import-look.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 * THE SITE BROUGHT IN KEEPS ITS OWN LOOK (AGL-4084).\n *\n * The first wave's look pass, before the header and footer, with no model:\n *\n * - **The theme.** The owner's own home page is read once more as AglynBot,\n * with the edge headers a site this platform serves gets whether or not a\n * caller hands them in (AGL-4108: production handed none, our own edge\n * challenged the read, and the 10/29 site kept the starter's indigo), and\n * the signals a guided start reads off the sites a person likes\n * (AGL-3975: colors, ground, type mood, corners, buttons, cards, header,\n * density, motion) pin the look. What the reader found of the source's\n * look (AGL-4106) pins it closer (AGL-4108): its buttons' fill is the\n * brand and its links' color the accent, its page's ground is the site's,\n * and its own font family is the site's wherever the theme font catalog\n * has it, else a pairing of its type mood. The look is drawn and saved\n * exactly as a guided start's (`aiSiteStyleFor`, `aiSaveSiteTheme`): a\n * built-in base plus the brand colors and fonts, every color held to the\n * theme's reading checks. A site whose owner already chose a theme keeps\n * it.\n * - **The logo.** The source header's logo, as the source draws it, into the\n * site's media library once: an SVG is turned into a PNG 192 pixels tall\n * (the library takes pictures, not drawings), a picture is kept as it is.\n * - **The listing.** The home page's own title, description and share\n * picture become the site's listing, only while the site still has the\n * listing it was created with: an owner's own words are never written\n * over.\n *\n * Nothing here fails a wave: a source whose look cannot be read keeps the\n * site's theme, and a logo that cannot be brought in leaves the header its\n * name.\n *=========================================*/\n\nimport type { PluginMediaIngest } from '@aglyn/aglyn/plugin-manager/plugin-media-ingest'\nimport { themeFontFacts, type ThemeFontFacts } from '@aglyn/aglyn/plugin-manager/plugin-theme-font-catalog'\nimport { aiSiteListingSeeded } from '../model/ai-site-entity'\nimport type { AiSiteImportHome, AiSiteImportLook, AiSiteImportPhoto, SiteImportLookFacts } from '../model/ai-site-import'\nimport { aiSiteKindFor } from '../model/ai-site-kinds'\nimport { aiHexOklch, aiSiteSeed, aiSiteStyleFor, type AiSiteSourceFont, type AiSiteStyle } from '../model/ai-site-look'\nimport { aiSiteIsStarterSrc } from '../model/ai-site-photos'\nimport { AGLYN_BOT_USER_AGENT, siteImportOrigins, type SiteImportCrawlSeams } from '../site-import/site-import-crawl'\nimport { NO_SITE_IMPORT_HEADERS, siteImportEdgeHeadersFor, type SiteImportHeadersFor } from '../site-import/site-import-edge'\nimport { bringInSiteImportPhotos, siteImportImageKey, siteImportLogoUrl } from '../site-import/site-import-images'\nimport { aiSaveSiteTheme, aiWorkspaceSiteStyles } from './ai-job-site-look'\nimport { aiSiteInspirationLookPins } from './ai-site-inspiration'\nimport { aiSiteInspirationSignals, type AiSiteInspirationSite } from './ai-site-inspiration-signals'\nimport {\n AI_THEME_BRAND_COLORS_PER_SOURCE,\n AI_THEME_REFERENCE_MAX_BYTES,\n AI_THEME_REFERENCE_STYLESHEETS,\n AI_THEME_STYLESHEET_MAX_BYTES,\n AI_THEME_THEME_COLOR_WEIGHT,\n fetchPublicTextDetailed,\n rankBrandColors,\n stylesheetUrlsOf,\n type AiReferencePage,\n type PublicFetchOptions,\n type PublicFetchResult,\n} from './ai-theme-brand-inputs'\n\n/** The height an SVG logo is drawn at as a picture: crisp at twice the header's 40. */\nexport const AI_SITE_IMPORT_LOGO_HEIGHT = 192\n\n/** The largest logo file read. */\nexport const AI_SITE_IMPORT_LOGO_MAX_BYTES = 1024 * 1024\n\n/** One read's own clock in the look pass. */\nconst LOOK_READ_MS = 8_000\n\ntype Fetch = (url: string, options: PublicFetchOptions) => Promise<PublicFetchResult>\n\n/**\n * The home page and its first stylesheets, read as AglynBot (AGL-4084).\n * `headersFor` is asked for every request and every redirect hop, as the\n * crawl asks it (`aiSiteImportLookHeaders`: the edge header only to the\n * site's own origins). `null` when the page cannot be read.\n */\nexport async function readSiteImportLookPage(\n url: string,\n input: { fetch?: Fetch; headersFor?: SiteImportHeadersFor | null; signal?: AbortSignal },\n): Promise<AiReferencePage | null> {\n const fetchText = input.fetch ?? fetchPublicTextDetailed\n const headersFor = input.headersFor ?? null\n const read = (address: string, maxBytes: number, accept: RegExp) =>\n fetchText(address, {\n maxBytes,\n accept,\n userAgent: AGLYN_BOT_USER_AGENT,\n timeoutMs: LOOK_READ_MS,\n ...(input.signal ? { signal: input.signal } : {}),\n ...(headersFor ? { headersFor } : {}),\n }).catch(() => null)\n const page = await read(url, AI_THEME_REFERENCE_MAX_BYTES, /text\\/html|application\\/xhtml\\+xml/i)\n if (!page?.ok) return null\n const stylesheets: string[] = []\n for (const sheet of stylesheetUrlsOf(page.text, page.url).slice(0, AI_THEME_REFERENCE_STYLESHEETS)) {\n const css = await read(sheet, AI_THEME_STYLESHEET_MAX_BYTES, /text\\/css/i)\n if (css?.ok) stylesheets.push(css.text)\n }\n return { url: page.url, html: page.text, stylesheets }\n}\n\nconst SOLID_HEX = /(?<!&)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?![0-9a-z])/gi\nconst SOLID_RGB = /rgba?\\(\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*(?:[,/]\\s*([\\d.]+%?)\\s*)?\\)/gi\nconst THEME_COLOR = /<meta\\b[^>]*\\bname\\s*=\\s*[\"']?theme-color\\b[^>]*\\bcontent\\s*=\\s*[\"']?\\s*(#[0-9a-f]{3,6})/gi\n\nconst hexOf = (value: string): string | null => {\n const raw = value.trim().toLowerCase()\n if (/^#[0-9a-f]{6}$/.test(raw)) return raw\n if (/^#[0-9a-f]{3}$/.test(raw)) return `#${raw[1]}${raw[1]}${raw[2]}${raw[2]}${raw[3]}${raw[3]}`\n return null\n}\n\n/**\n * A source's brand colors as it DRAWS them (AGL-4084): the colors its markup\n * and stylesheets use solid, most used first, the colorful before the greys.\n * A translucent color is a tint of one (a hover, a focus ring), never a use\n * of it, so it is not counted: aglyn.com's focus rings, a light blue at a\n * quarter strength, outnumbered the deep blue its buttons and links are\n * drawn in, which the theme reader would otherwise take as its brand.\n */\nexport function aiSiteImportBrandColors(page: Pick<AiReferencePage, 'html' | 'stylesheets'>): string[] {\n const weights = new Map<string, number>()\n const add = (hex: string | null, weight = 1) => {\n if (hex) weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n for (const match of page.html.matchAll(THEME_COLOR)) add(hexOf(match[1] ?? ''), AI_THEME_THEME_COLOR_WEIGHT)\n for (const text of [page.html, ...page.stylesheets]) {\n for (const match of text.matchAll(SOLID_HEX)) add(hexOf(match[0]))\n for (const match of text.matchAll(SOLID_RGB)) {\n const alpha = match[4]\n if (alpha !== undefined && (alpha.endsWith('%') ? Number(alpha.slice(0, -1)) < 100 : Number(alpha) < 1)) continue\n const channels = [match[1], match[2], match[3]].map((value) => Math.min(255, Number(value)))\n add(`#${channels.map((value) => value.toString(16).padStart(2, '0')).join('')}`)\n }\n }\n return rankBrandColors(weights, AI_THEME_BRAND_COLORS_PER_SOURCE)\n}\n\n/** How colorful a color must be to carry a brand: the inspiration reader's own floor. */\nconst COLORFUL_CHROMA = 0.04\n\nconst colorful = (hex: string | null | undefined): hex is string => !!hex && /^#[0-9a-f]{6}$/i.test(hex) && aiHexOklch(hex).C >= COLORFUL_CHROMA\n\n/**\n * The brand and accent a source's look facts give (AGL-4108): the fill of\n * its filled buttons as the brand, the color of its links as the accent\n * where it is a color of its own, and the reverse where its buttons are not\n * colorful. The theme draws the brand at the nearest shade of it that reads\n * (`aiSiteColors`): aglyn.com's light blue buttons, under white words at\n * 2.4:1, come out at the deeper blue of its own links.\n */\nexport function aiSiteImportBrandOf(facts: Pick<SiteImportLookFacts, 'buttonFill' | 'link'> | null | undefined): { brand: string; accent: string | null } | null {\n const fill = colorful(facts?.buttonFill) ? (facts?.buttonFill as string).toLowerCase() : null\n const link = colorful(facts?.link) ? (facts?.link as string).toLowerCase() : null\n const brand = fill ?? link\n if (!brand) return null\n return { brand, accent: link && link !== brand ? link : null }\n}\n\n/**\n * The look a source's signals pin (AGL-4084), drawn the way a guided start's\n * is: the kind of site its own description says, the import's seed, its\n * strongest color as the brand. What the reader found of its look\n * (`facts`, AGL-4108) pins it closer: its buttons' fill as the brand and its\n * links' color as the accent, its page's ground, and its own fonts where the\n * catalog has them (`fonts`). `null` where the source showed no color to\n * carry a brand, so the site keeps the theme it has.\n */\nexport function aiSiteImportStyle(\n site: AiSiteInspirationSite | null,\n input: {\n seed: number\n description: string\n avoid?: readonly AiSiteStyle[]\n facts?: SiteImportLookFacts | null\n fonts?: AiSiteStyle['sourceFonts']\n },\n): AiSiteStyle | null {\n const facts = input.facts ?? null\n const own = aiSiteImportBrandOf(facts)\n if (!site && !own) return null\n const kind = aiSiteKindFor(input.description)\n const pins = site ? aiSiteInspirationLookPins({ v: 1, sites: [site], unread: 0 }, { kind, seed: input.seed }) : {}\n if (own) {\n pins.brand = own.brand\n pins.hue = Math.round(aiHexOklch(own.brand).h) % 360\n if (own.accent) pins.accent = Math.round(aiHexOklch(own.accent).h) % 360\n }\n // Its buttons' corners round the theme's shape where its stylesheets did not say (AGL-4108).\n const radius = facts?.buttonRadius\n if (typeof radius === 'number' && Number.isFinite(radius) && radius >= 0) {\n pins.buttons ??= radius >= 24 ? 'pill' : radius <= 2 ? 'square' : 'rounded'\n pins.corners ??= radius <= 2 ? 'sharp' : radius <= 10 ? 'soft' : radius <= 20 ? 'round' : 'pill'\n }\n if (!pins.brand) return null\n const style = aiSiteStyleFor({ kind, answer: {}, seed: input.seed, brand: pins.brand, avoid: input.avoid ?? [], pins })\n const page = facts?.background && /^#[0-9a-f]{6}$/i.test(facts.background) ? facts.background.toLowerCase() : null\n return {\n ...style,\n ...(page ? { page } : {}),\n ...(input.fonts ? { sourceFonts: input.fonts } : {}),\n }\n}\n\n/** A family's facts, as the theme font catalog answers them. */\nexport type AiSiteImportFontFacts = (family: string) => Promise<ThemeFontFacts | undefined>\n\n/**\n * The source's own fonts (AGL-4108), each where the theme font catalog has\n * the family (Google Fonts', as the fonts plugin registers it), or the\n * source loads it from Google Fonts itself: its body's and its headings'.\n * `null` where its body's family is neither, and the pairing of the source's\n * type mood sets the site instead.\n */\nexport async function aiSiteImportSourceFonts(\n facts: Pick<SiteImportLookFacts, 'fonts'> | null | undefined,\n lookup: AiSiteImportFontFacts = themeFontFacts,\n): Promise<AiSiteStyle['sourceFonts']> {\n const fonts = facts?.fonts\n if (!fonts?.body) return null\n const loaded = new Set(fonts.google.map((family) => family.toLowerCase()))\n const resolve = async (family: string | null): Promise<AiSiteSourceFont | null> => {\n if (!family) return null\n const known = await lookup(family).catch(() => undefined)\n if (known) return { family: known.family, category: known.category, weights: [...known.weights] }\n return loaded.has(family.toLowerCase()) ? { family, category: 'sans-serif', weights: [] } : null\n }\n const body = await resolve(fonts.body)\n if (!body) return null\n const heading = fonts.headings && fonts.headings.toLowerCase() !== body.family.toLowerCase() ? await resolve(fonts.headings) : null\n return { body, heading }\n}\n\n/**\n * The site's listing in the home page's own words (AGL-4084), as field\n * paths: its title and description each only where the site still has the\n * one it was created with, so an owner's own listing is never written over.\n * `{}` with nothing to write.\n */\nexport function aiSiteImportListingUpdates(seo: unknown, displayName: unknown, home: Pick<AiSiteImportHome, 'title' | 'description'>): Record<string, string> {\n const seeded = aiSiteListingSeeded(seo, displayName)\n const current = (seo && typeof seo === 'object' ? seo : {}) as Record<string, unknown>\n const updates: Record<string, string> = {}\n const title = home.title.replace(/\\s+/g, ' ').trim()\n const description = home.description.replace(/\\s+/g, ' ').trim()\n if (title && seeded.title && title !== current['title']) updates['seo.title'] = title\n if (description && seeded.description && description !== current['description']) updates['seo.description'] = description\n return updates\n}\n\n/** Whether the site's share picture is still open: none, or the starter's (AGL-3988). */\nfunction shareOpen(seo: unknown): boolean {\n const image = (seo as { image?: unknown } | null | undefined)?.image\n if (image === undefined || image === null) return true\n return typeof image === 'string' && (!image.trim() || aiSiteIsStarterSrc(image))\n}\n\n/** Turns an SVG drawing into a PNG of a height; `null` where it cannot. */\nexport type AiSiteImportRasterize = (svg: Uint8Array, height: number) => Promise<Uint8Array | null>\n\nexport interface AiSiteImportLookDeps {\n fetch?: Fetch\n /** The edge headers for the source, asked with its domain; absent, the edge is asked as the crawl asks it (AGL-4108). */\n headersFor?: SiteImportCrawlSeams['headersFor']\n /** The edge's own answer, per address (`siteImportEdgeHeadersFor`), where no `headersFor` is given. */\n edgeHeadersFor?: typeof siteImportEdgeHeadersFor\n /** A family's facts in the theme font catalog (`themeFontFacts`). */\n fontFacts?: AiSiteImportFontFacts\n save?: typeof aiSaveSiteTheme\n others?: typeof aiWorkspaceSiteStyles\n ingest?: PluginMediaIngest | null\n rasterize?: AiSiteImportRasterize\n /** The site's document, as the listing reads it. */\n readHost?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<Record<string, unknown> | null>\n writeHost?: (firestore: FirebaseFirestore.Firestore, hostId: string, updates: Record<string, unknown>) => Promise<void>\n}\n\nexport interface AiSiteImportLookInput {\n firestore: FirebaseFirestore.Firestore\n hostId: string\n orgId: string\n /** The member the logo and the share picture are stored as. */\n uid: string\n /** The import job's id, which the look's seed is drawn from. */\n jobId: string\n domain: string\n home: AiSiteImportHome\n at: string\n signal?: AbortSignal\n}\n\nasync function readHostDoc(firestore: FirebaseFirestore.Firestore, hostId: string): Promise<Record<string, unknown> | null> {\n const snapshot = await firestore.collection('hosts').doc(hostId).get()\n return snapshot.exists ? ((snapshot.data() ?? {}) as Record<string, unknown>) : null\n}\n\nasync function writeHostDoc(firestore: FirebaseFirestore.Firestore, hostId: string, updates: Record<string, unknown>): Promise<void> {\n await firestore.collection('hosts').doc(hostId).update(updates)\n}\n\n/** Turns an SVG into a PNG in this process, where `sharp` is shipped; `null` where it is not. */\nasync function rasterizeHere(svg: Uint8Array, height: number): Promise<Uint8Array | null> {\n const images = await import('./ai-image-reads')\n const origin = images.aiImageReadsOrigin()\n if (origin) return images.requestAiLogoRaster(origin, svg, height)\n return images.rasterizeAiLogoSvg(svg, height)\n}\n\n/**\n * The source header's logo in the site's library (AGL-4084), once: the copy\n * a page already brought in is reused by its key. An SVG becomes a PNG.\n * `null` where it cannot be brought in, and the header then prints the name.\n */\nexport async function aiSiteImportLogo(\n input: { hostId: string; uid: string; domain: string; logo: NonNullable<AiSiteImportHome['brand']['logo']>; alt: string; headersFor?: SiteImportHeadersFor | null; signal?: AbortSignal },\n deps: { fetch?: Fetch; ingest: PluginMediaIngest | null; rasterize?: AiSiteImportRasterize },\n): Promise<AiSiteImportPhoto | null> {\n const ingest = deps.ingest\n const url = siteImportLogoUrl(input.logo.url)\n if (!ingest?.findImportedPhoto || !url) return null\n const sourceKey = siteImportImageKey(url)\n try {\n const held = await ingest.findImportedPhoto({ hostId: input.hostId, sourceKey })\n if (held) return { mediaId: held.mediaId, src: held.src, alt: input.alt, from: url, ...(held.width ? { width: held.width } : {}), ...(held.height ? { height: held.height } : {}) }\n const fetched = await (deps.fetch ?? fetchPublicTextDetailed)(url, {\n maxBytes: AI_SITE_IMPORT_LOGO_MAX_BYTES,\n accept: /^image\\//i,\n acceptHeader: 'image/svg+xml,image/png,image/webp,image/jpeg;q=0.9',\n userAgent: AGLYN_BOT_USER_AGENT,\n timeoutMs: LOOK_READ_MS,\n refuseOver: true,\n binary: true,\n ...(input.signal ? { signal: input.signal } : {}),\n ...(input.headersFor ? { headersFor: input.headersFor } : {}),\n })\n if (!fetched.ok || !fetched.data?.byteLength) return null\n const type = fetched.contentType.split(';')[0].trim().toLowerCase()\n let bytes: Uint8Array | null = fetched.data\n let contentType = type\n if (type === 'image/svg+xml' || input.logo.type === 'svg') {\n bytes = await (deps.rasterize ?? rasterizeHere)(fetched.data, AI_SITE_IMPORT_LOGO_HEIGHT).catch(() => null)\n contentType = 'image/png'\n } else if (!/^image\\/(?:png|jpeg|webp|gif)$/.test(type)) {\n return null\n }\n if (!bytes?.byteLength) return null\n const stored = await ingest.ingest({\n hostId: input.hostId,\n uid: input.uid,\n fileName: `logo.${contentType === 'image/jpeg' ? 'jpg' : contentType.split('/')[1]}`,\n contentType,\n bytes,\n ...(input.alt ? { alt: input.alt.slice(0, 250) } : {}),\n importedFrom: { key: sourceKey, url, domain: input.domain },\n })\n if (!stored.ok) return null\n return { mediaId: stored.mediaId, src: stored.src, alt: input.alt, from: url, ...(stored.width ? { width: stored.width } : {}), ...(stored.height ? { height: stored.height } : {}) }\n } catch (error) {\n console.warn('[site-import] the logo could not be brought in', { hostId: input.hostId, error })\n return null\n }\n}\n\n/** The look pass (AGL-4084): the theme, the logo and the listing, from the owner's own home page. No model. */\nexport async function aiRunSiteImportLook(input: AiSiteImportLookInput, deps: AiSiteImportLookDeps = {}): Promise<AiSiteImportLook> {\n const { firestore, hostId, home } = input\n const headersFor = await aiSiteImportLookHeaders(input.domain, deps)\n const seed = aiSiteSeed(input.jobId)\n const page = await readSiteImportLookPage(home.url, { ...(deps.fetch ? { fetch: deps.fetch } : {}), headersFor, ...(input.signal ? { signal: input.signal } : {}) })\n // The source's own signals, its brand colors as it draws them solid.\n const signals = page ? aiSiteInspirationSignals(page) : null\n const site = signals && page ? { ...signals, colors: aiSiteImportBrandColors(page) } : signals\n const avoid = await (deps.others ?? aiWorkspaceSiteStyles)(firestore, { orgId: input.orgId, hostId })\n // What the reader found of its look, its buttons, ground and fonts (AGL-4108).\n const facts = home.look ?? null\n const fonts = await aiSiteImportSourceFonts(facts, deps.fontFacts)\n const style = aiSiteImportStyle(site, { seed, description: `${home.siteName} ${home.description}`, avoid, facts, fonts })\n let theme: AiSiteImportLook['theme'] = 'unread'\n if (style) {\n const saved = await (deps.save ?? aiSaveSiteTheme)(firestore, { hostId, jobId: input.jobId, style })\n if (saved.write === 'missing') throw new Error(`site ${hostId} vanished while its look was brought in`)\n theme = saved.write === 'kept' ? 'kept' : 'applied'\n }\n const ingest = deps.ingest ?? null\n const logo = home.brand.logo\n ? await aiSiteImportLogo(\n { hostId, uid: input.uid, domain: input.domain, logo: home.brand.logo, alt: home.siteName || home.brand.text, headersFor, ...(input.signal ? { signal: input.signal } : {}) },\n { ingest, ...(deps.fetch ? { fetch: deps.fetch } : {}), ...(deps.rasterize ? { rasterize: deps.rasterize } : {}) },\n )\n : null\n // The site's listing in its own words, only over the one it was created with.\n let seo = false\n const host = await (deps.readHost ?? readHostDoc)(firestore, hostId)\n if (host) {\n const updates: Record<string, unknown> = aiSiteImportListingUpdates(host['seo'], host['displayName'], home)\n if (home.ogImage && shareOpen(host['seo'])) {\n const share = await bringInSiteImportPhotos(\n { hostId, uid: input.uid, domain: input.domain, images: [{ url: home.ogImage, alt: '' }], deadlineAt: Date.now() + LOOK_READ_MS, ...(input.signal ? { signal: input.signal } : {}) },\n { ingest, ...(deps.fetch ? { fetch: deps.fetch } : {}) },\n )\n const photo = share.assets[0]\n if (photo) {\n updates['seo.image'] = photo.src\n if (photo.width) updates['seo.imageWidth'] = photo.width\n if (photo.height) updates['seo.imageHeight'] = photo.height\n }\n }\n if (Object.keys(updates).length) {\n await (deps.writeHost ?? writeHostDoc)(firestore, hostId, updates)\n seo = true\n }\n }\n const drawn = style\n ? {\n ...(typeof style.headerAlign === 'string' ? { headerAlign: style.headerAlign } : {}),\n ...(typeof style.rhythm === 'string' ? { rhythm: style.rhythm } : {}),\n ...(typeof style.motion === 'string' ? { motion: style.motion } : {}),\n }\n : null\n const colors = [...new Set([...(facts ? [facts.buttonFill, facts.link].filter(colorful).map((hex) => hex.toLowerCase()) : []), ...(site?.colors ?? [])])]\n return { at: input.at, theme, colors, logo, seo, seed: style ? style.seed : seed, style: drawn }\n}\n\n/**\n * The headers the look's reads of the source go with, per address (AGL-4108),\n * exactly as the crawl's (`siteRequestHeaders`): the caller's where it hands\n * them in, else the edge's own, so a site this platform serves is read past\n * its challenge however the step was registered; and nothing for any address\n * off the site's two origins, a redirect hop's included. Never throws.\n *\n * The 10/29 look read went without the header twice over: no caller handed\n * one in, and the read passed headers the fetch does not take, which is why\n * this is a function the fetch asks for every hop (`headersFor`).\n */\nexport async function aiSiteImportLookHeaders(\n domain: string,\n deps: Pick<AiSiteImportLookDeps, 'headersFor' | 'edgeHeadersFor'>,\n): Promise<SiteImportHeadersFor> {\n const origins = siteImportOrigins(domain)\n let inner: SiteImportHeadersFor = NO_SITE_IMPORT_HEADERS\n try {\n if (deps.headersFor) {\n const headers = await deps.headersFor(domain)\n if (headers && Object.keys(headers).length) inner = () => headers\n } else {\n inner = await (deps.edgeHeadersFor ?? siteImportEdgeHeadersFor)(origins)\n }\n } catch (error) {\n console.warn('[site-import] the edge headers could not be read; reading the look without them', { domain, error })\n }\n return (url: URL) => (origins.includes(url.origin) ? inner(url) : {})\n}\n"],"names":["themeFontFacts","aiSiteListingSeeded","aiSiteKindFor","aiHexOklch","aiSiteSeed","aiSiteStyleFor","aiSiteIsStarterSrc","AGLYN_BOT_USER_AGENT","siteImportOrigins","NO_SITE_IMPORT_HEADERS","siteImportEdgeHeadersFor","bringInSiteImportPhotos","siteImportImageKey","siteImportLogoUrl","aiSaveSiteTheme","aiWorkspaceSiteStyles","aiSiteInspirationLookPins","aiSiteInspirationSignals","AI_THEME_BRAND_COLORS_PER_SOURCE","AI_THEME_REFERENCE_MAX_BYTES","AI_THEME_REFERENCE_STYLESHEETS","AI_THEME_STYLESHEET_MAX_BYTES","AI_THEME_THEME_COLOR_WEIGHT","fetchPublicTextDetailed","rankBrandColors","stylesheetUrlsOf","AI_SITE_IMPORT_LOGO_HEIGHT","AI_SITE_IMPORT_LOGO_MAX_BYTES","LOOK_READ_MS","readSiteImportLookPage","url","input","fetchText","fetch","headersFor","read","address","maxBytes","accept","userAgent","timeoutMs","signal","catch","page","ok","stylesheets","sheet","text","slice","css","push","html","SOLID_HEX","SOLID_RGB","THEME_COLOR","hexOf","value","raw","trim","toLowerCase","test","aiSiteImportBrandColors","match","weights","Map","add","hex","weight","set","get","matchAll","alpha","undefined","endsWith","Number","channels","map","Math","min","toString","padStart","join","COLORFUL_CHROMA","colorful","C","aiSiteImportBrandOf","facts","fill","buttonFill","link","brand","accent","aiSiteImportStyle","site","own","kind","description","pins","v","sites","unread","seed","hue","round","h","radius","buttonRadius","isFinite","buttons","corners","style","answer","avoid","background","fonts","sourceFonts","aiSiteImportSourceFonts","lookup","body","loaded","Set","google","family","resolve","known","category","has","heading","headings","aiSiteImportListingUpdates","seo","displayName","home","seeded","current","updates","title","replace","shareOpen","image","readHostDoc","firestore","hostId","snapshot","collection","doc","exists","data","writeHostDoc","update","rasterizeHere","svg","height","images","origin","aiImageReadsOrigin","requestAiLogoRaster","rasterizeAiLogoSvg","aiSiteImportLogo","deps","ingest","logo","findImportedPhoto","sourceKey","fetched","held","mediaId","src","alt","from","width","acceptHeader","refuseOver","binary","byteLength","type","contentType","split","bytes","rasterize","stored","uid","fileName","importedFrom","key","domain","error","console","warn","aiRunSiteImportLook","aiSiteImportLookHeaders","jobId","signals","colors","others","orgId","look","fontFacts","siteName","theme","saved","save","write","Error","host","readHost","ogImage","share","deadlineAt","Date","now","photo","assets","Object","keys","length","writeHost","drawn","headerAlign","rhythm","motion","filter","at","origins","inner","headers","edgeHeadersFor","includes"],"mappings":"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"}
|
|
@@ -22,7 +22,13 @@ export interface AiSiteImportPublishDeps {
|
|
|
22
22
|
/** Whether the site is in preview now. */
|
|
23
23
|
inPreview?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<boolean>;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* The pages of the import that will never be in (AGL-4107): left out, or
|
|
27
|
+
* settled without coming in — a wave's own failures included. A page of a
|
|
28
|
+
* wave still to come is not one: a link to it keeps its place and shows its
|
|
29
|
+
* words, which the site's pages show for a page that is not live, and goes
|
|
30
|
+
* there once that page's wave lands.
|
|
31
|
+
*/
|
|
26
32
|
export declare function aiSiteImportUnbuilt(state: AiSiteImportState, items: readonly AiJobItemLedger[]): string[];
|
|
27
33
|
/** The wave's pages that came in, as the outputs that publish them, in the wave's order. */
|
|
28
34
|
export declare function aiSiteImportWaveOutputs(state: AiSiteImportState, wave: AiSiteImportWave, items: readonly AiJobItemLedger[], outputs: readonly AiJobOutput[]): AiJobOutput[];
|
|
@@ -21,8 +21,10 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
21
21
|
* sees its notice, and its team sees the whole site. So when a wave closes,
|
|
22
22
|
* its pages go live INTO THE PREVIEW through the same writes a guided
|
|
23
23
|
* start's publish makes (`aiPublishGuidedSite`): each page takes its
|
|
24
|
-
* address, Home takes `/`,
|
|
25
|
-
* the cached pages go.
|
|
24
|
+
* address, Home takes `/`, a link to a page that will not come in goes
|
|
25
|
+
* nowhere with its words kept, and the cached pages go. A link to a page of a
|
|
26
|
+
* later wave keeps its place (AGL-4107): until that page is live a visitor
|
|
27
|
+
* sees its words, and once the page's wave lands it goes there. The team opens the site and sees the wave; the public
|
|
26
28
|
* still sees the notice until the person makes the site public.
|
|
27
29
|
*
|
|
28
30
|
* Only into a preview. A site that is not in preview keeps the wave's pages
|
|
@@ -39,12 +41,22 @@ async function readInPreview(firestore, hostId) {
|
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
43
|
const delivered = (row)=>(row == null ? void 0 : row.status) === 'succeeded' || (row == null ? void 0 : row.status) === 'degraded';
|
|
42
|
-
|
|
44
|
+
const open = (row)=>(row == null ? void 0 : row.status) === 'pending' || (row == null ? void 0 : row.status) === 'running';
|
|
45
|
+
/**
|
|
46
|
+
* The pages of the import that will never be in (AGL-4107): left out, or
|
|
47
|
+
* settled without coming in — a wave's own failures included. A page of a
|
|
48
|
+
* wave still to come is not one: a link to it keeps its place and shows its
|
|
49
|
+
* words, which the site's pages show for a page that is not live, and goes
|
|
50
|
+
* there once that page's wave lands.
|
|
51
|
+
*/ export function aiSiteImportUnbuilt(state, items) {
|
|
43
52
|
const rows = new Map(items.map((row)=>[
|
|
44
53
|
row.slot,
|
|
45
54
|
row
|
|
46
55
|
]));
|
|
47
|
-
return state.pages.filter((page)
|
|
56
|
+
return state.pages.filter((page)=>{
|
|
57
|
+
const row = rows.get(page.slot);
|
|
58
|
+
return page.refused || !delivered(row) && !open(row);
|
|
59
|
+
}).map((page)=>page.draftId);
|
|
48
60
|
}
|
|
49
61
|
/** The wave's pages that came in, as the outputs that publish them, in the wave's order. */ export function aiSiteImportWaveOutputs(state, wave, items, outputs) {
|
|
50
62
|
const rows = new Map(items.map((row)=>[
|
|
@@ -80,8 +92,10 @@ const delivered = (row)=>(row == null ? void 0 : row.status) === 'succeeded' ||
|
|
|
80
92
|
job: input.job,
|
|
81
93
|
outputs: pages,
|
|
82
94
|
now: input.now,
|
|
83
|
-
// The links to a page
|
|
95
|
+
// The links to a page that did not come in go nowhere, their words kept;
|
|
96
|
+
// a page still to come keeps its links (AGL-4107).
|
|
84
97
|
unbuiltScreenIds: aiSiteImportUnbuilt(input.state, input.items),
|
|
98
|
+
keepLinkWords: true,
|
|
85
99
|
siteOrder: input.state.pages.map((page)=>page.draftId)
|
|
86
100
|
}, (already == null ? void 0 : already.published.length) ? {
|
|
87
101
|
retry: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-import-publish.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 * EACH WAVE LANDS IN THE SITE'S PREVIEW (AGL-4084).\n *\n * A site brought in starts in preview (AGL-4086, `host.preview`): the public\n * sees its notice, and its team sees the whole site. So when a wave closes,\n * its pages go live INTO THE PREVIEW through the same writes a guided\n * start's publish makes (`aiPublishGuidedSite`): each page takes its\n * address, Home takes `/`,
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-import-publish.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 * EACH WAVE LANDS IN THE SITE'S PREVIEW (AGL-4084).\n *\n * A site brought in starts in preview (AGL-4086, `host.preview`): the public\n * sees its notice, and its team sees the whole site. So when a wave closes,\n * its pages go live INTO THE PREVIEW through the same writes a guided\n * start's publish makes (`aiPublishGuidedSite`): each page takes its\n * address, Home takes `/`, a link to a page that will not come in goes\n * nowhere with its words kept, and the cached pages go. A link to a page of a\n * later wave keeps its place (AGL-4107): until that page is live a visitor\n * sees its words, and once the page's wave lands it goes there. The team opens the site and sees the wave; the public\n * still sees the notice until the person makes the site public.\n *\n * Only into a preview. A site that is not in preview keeps the wave's pages\n * as drafts, as every import did before: the public never sees a page no\n * person has looked at. And `site_published` is not sent here: the site is\n * published when its switch makes it public (AGL-4086), not when a wave\n * lands.\n *=========================================*/\n\nimport { isHostInPreview } from '@aglyn/aglyn/app-utils/site-preview'\nimport type { AiJob, AiJobItemLedger, AiJobOutput, AiJobSitePublish } from '../model/ai-jobs.types'\nimport type { AiSiteImportState, AiSiteImportWave } from '../model/ai-site-import'\nimport { aiMergeSitePublish, aiPublishGuidedSite, type AiSitePublishDeps } from './ai-site-publish'\n\nexport interface AiSiteImportPublishDeps {\n publish?: typeof aiPublishGuidedSite\n /** Whether the site is in preview now. */\n inPreview?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<boolean>\n}\n\nasync function readInPreview(firestore: FirebaseFirestore.Firestore, hostId: string): Promise<boolean> {\n const host = await firestore.collection('hosts').doc(hostId).get()\n return host.exists && isHostInPreview({ preview: host.get('preview') })\n}\n\nconst delivered = (row: AiJobItemLedger | undefined) => row?.status === 'succeeded' || row?.status === 'degraded'\n\nconst open = (row: AiJobItemLedger | undefined) => row?.status === 'pending' || row?.status === 'running'\n\n/**\n * The pages of the import that will never be in (AGL-4107): left out, or\n * settled without coming in — a wave's own failures included. A page of a\n * wave still to come is not one: a link to it keeps its place and shows its\n * words, which the site's pages show for a page that is not live, and goes\n * there once that page's wave lands.\n */\nexport function aiSiteImportUnbuilt(state: AiSiteImportState, items: readonly AiJobItemLedger[]): string[] {\n const rows = new Map(items.map((row) => [row.slot, row]))\n return state.pages\n .filter((page) => {\n const row = rows.get(page.slot)\n return page.refused || (!delivered(row) && !open(row))\n })\n .map((page) => page.draftId)\n}\n\n/** The wave's pages that came in, as the outputs that publish them, in the wave's order. */\nexport function aiSiteImportWaveOutputs(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items: readonly AiJobItemLedger[],\n outputs: readonly AiJobOutput[],\n): AiJobOutput[] {\n const rows = new Map(items.map((row) => [row.slot, row]))\n return wave.slots.flatMap((slot) => {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page || !delivered(rows.get(slot))) return []\n const output = outputs.find((entry) => entry.resource === 'screen' && entry.id === page.draftId)\n return output ? [output] : []\n })\n}\n\n/**\n * A closed wave's pages, live in the site's preview (AGL-4084). `null` when\n * nothing went live: the site is not in preview, or none of the wave's pages\n * came in. The job's earlier publish is carried in the answer.\n */\nexport async function aiPublishImportedWave(\n firestore: FirebaseFirestore.Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'orgId' | 'hostId'> & Partial<Pick<AiJob, 'inputs' | 'sitePublish'>>\n state: AiSiteImportState\n wave: AiSiteImportWave\n items: readonly AiJobItemLedger[]\n outputs: readonly AiJobOutput[]\n now: Date\n },\n deps: AiSiteImportPublishDeps = {},\n): Promise<AiJobSitePublish | null> {\n const hostId = input.job.hostId\n if (!hostId) return null\n const pages = aiSiteImportWaveOutputs(input.state, input.wave, input.items, input.outputs)\n if (!pages.length) return null\n if (!(await (deps.inPreview ?? readInPreview)(firestore, hostId))) return null\n const publishDeps: AiSitePublishDeps = {\n // Published when its switch makes it public (AGL-4086), never when a wave lands.\n sendSitePublished: async () => undefined,\n }\n const already = input.job.sitePublish ?? null\n const result = await (deps.publish ?? aiPublishGuidedSite)(\n firestore,\n {\n job: input.job,\n outputs: pages,\n now: input.now,\n // The links to a page that did not come in go nowhere, their words kept;\n // a page still to come keeps its links (AGL-4107).\n unbuiltScreenIds: aiSiteImportUnbuilt(input.state, input.items),\n keepLinkWords: true,\n siteOrder: input.state.pages.map((page) => page.draftId),\n // A later wave joins what is live: it never takes the home address again.\n ...(already?.published.length ? { retry: true } : {}),\n },\n publishDeps,\n )\n return already ? aiMergeSitePublish(already, result) : result\n}\n"],"names":["isHostInPreview","aiMergeSitePublish","aiPublishGuidedSite","readInPreview","firestore","hostId","host","collection","doc","get","exists","preview","delivered","row","status","open","aiSiteImportUnbuilt","state","items","rows","Map","map","slot","pages","filter","page","refused","draftId","aiSiteImportWaveOutputs","wave","outputs","slots","flatMap","find","entry","output","resource","id","aiPublishImportedWave","input","deps","job","length","inPreview","publishDeps","sendSitePublished","undefined","already","sitePublish","result","publish","now","unbuiltScreenIds","keepLinkWords","siteOrder","published","retry"],"mappings":"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"}
|