@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.
Files changed (149) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-site-import-job.component.js +5 -4
  3. package/src/lib/components/ai-site-import-job.component.js.map +1 -1
  4. package/src/lib/components/ai-site-import-start.component.js +3 -3
  5. package/src/lib/components/ai-site-import-start.component.js.map +1 -1
  6. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +129 -29
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
  10. package/src/lib/components/ai-site-start-steps.component.js +42 -25
  11. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  12. package/src/lib/components/ai-site-style-previews.d.ts +17 -0
  13. package/src/lib/components/ai-site-style-previews.js +26 -95
  14. package/src/lib/components/ai-site-style-previews.js.map +1 -1
  15. package/src/lib/declarations.server.js +22 -0
  16. package/src/lib/declarations.server.js.map +1 -1
  17. package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
  18. package/src/lib/jobs/ai-job-import-step.js +179 -49
  19. package/src/lib/jobs/ai-job-import-step.js.map +1 -1
  20. package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
  21. package/src/lib/jobs/ai-job-layout-language.js +156 -113
  22. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  23. package/src/lib/jobs/ai-job-layout-step.js +56 -17
  24. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  25. package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
  26. package/src/lib/jobs/ai-job-page-language.js +120 -66
  27. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  28. package/src/lib/jobs/ai-job-page-step.js +27 -7
  29. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  30. package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
  31. package/src/lib/jobs/ai-job-site-datasets.js +36 -16
  32. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  33. package/src/lib/jobs/ai-jobs.js +6 -2
  34. package/src/lib/jobs/ai-jobs.js.map +1 -1
  35. package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
  36. package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
  37. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  38. package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
  39. package/src/lib/jobs/ai-site-import-compile.js +1143 -0
  40. package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
  41. package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
  42. package/src/lib/jobs/ai-site-import-document.js +23 -489
  43. package/src/lib/jobs/ai-site-import-document.js.map +1 -1
  44. package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
  45. package/src/lib/jobs/ai-site-import-look.js +163 -52
  46. package/src/lib/jobs/ai-site-import-look.js.map +1 -1
  47. package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
  48. package/src/lib/jobs/ai-site-import-publish.js +19 -5
  49. package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
  50. package/src/lib/jobs/ai-site-late-photos.js +4 -2
  51. package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
  52. package/src/lib/jobs/ai-site-publish.d.ts +10 -1
  53. package/src/lib/jobs/ai-site-publish.js +8 -3
  54. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  55. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
  56. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
  57. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
  58. package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
  59. package/src/lib/layout-language/ai-layout-compiler.js +677 -33
  60. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  61. package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
  62. package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
  63. package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
  64. package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
  65. package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
  66. package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
  67. package/src/lib/layout-language/ai-layout-frame.js +121 -29
  68. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  69. package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
  70. package/src/lib/layout-language/ai-layout-language.js +85 -120
  71. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  72. package/src/lib/model/ai-credit-estimate.d.ts +11 -7
  73. package/src/lib/model/ai-credit-estimate.js +11 -7
  74. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  75. package/src/lib/model/ai-site-import.d.ts +307 -13
  76. package/src/lib/model/ai-site-import.js +227 -19
  77. package/src/lib/model/ai-site-import.js.map +1 -1
  78. package/src/lib/model/ai-site-look.d.ts +65 -1
  79. package/src/lib/model/ai-site-look.js +141 -23
  80. package/src/lib/model/ai-site-look.js.map +1 -1
  81. package/src/lib/model/ai-site-photos.d.ts +2 -0
  82. package/src/lib/model/ai-site-photos.js.map +1 -1
  83. package/src/lib/model/ai-site-start-steps.d.ts +4 -3
  84. package/src/lib/model/ai-site-start-steps.js +11 -3
  85. package/src/lib/model/ai-site-start-steps.js.map +1 -1
  86. package/src/lib/model/ai-site-start.d.ts +23 -2
  87. package/src/lib/model/ai-site-start.js +25 -3
  88. package/src/lib/model/ai-site-start.js.map +1 -1
  89. package/src/lib/providers/routing.d.ts +11 -0
  90. package/src/lib/providers/routing.js +23 -0
  91. package/src/lib/providers/routing.js.map +1 -1
  92. package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
  93. package/src/lib/runtime/ai-doctrine-validators.js +7 -2
  94. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  95. package/src/lib/runtime/ai-icon-library.d.ts +4 -0
  96. package/src/lib/runtime/ai-icon-library.js +4 -0
  97. package/src/lib/runtime/ai-icon-library.js.map +1 -1
  98. package/src/lib/runtime/ai-node-tree.js +5 -1
  99. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  100. package/src/lib/runtime/ai-palette.d.ts +2 -0
  101. package/src/lib/runtime/ai-palette.generated.js +19 -3
  102. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  103. package/src/lib/runtime/ai-palette.js +2 -1
  104. package/src/lib/runtime/ai-palette.js.map +1 -1
  105. package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
  106. package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
  107. package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
  108. package/src/lib/site-import/site-import-crawl.d.ts +18 -1
  109. package/src/lib/site-import/site-import-crawl.js +70 -10
  110. package/src/lib/site-import/site-import-crawl.js.map +1 -1
  111. package/src/lib/site-import/site-import-css.d.ts +110 -0
  112. package/src/lib/site-import/site-import-css.js +826 -0
  113. package/src/lib/site-import/site-import-css.js.map +1 -0
  114. package/src/lib/site-import/site-import-html.d.ts +116 -1
  115. package/src/lib/site-import/site-import-html.js +538 -46
  116. package/src/lib/site-import/site-import-html.js.map +1 -1
  117. package/src/lib/site-import/site-import-layout.d.ts +79 -0
  118. package/src/lib/site-import/site-import-layout.js +1608 -0
  119. package/src/lib/site-import/site-import-layout.js.map +1 -0
  120. package/src/lib/site-import/site-import-nav.d.ts +40 -0
  121. package/src/lib/site-import/site-import-nav.js +437 -0
  122. package/src/lib/site-import/site-import-nav.js.map +1 -0
  123. package/src/lib/site-import/site-import-page.d.ts +8 -1
  124. package/src/lib/site-import/site-import-page.js +24 -2
  125. package/src/lib/site-import/site-import-page.js.map +1 -1
  126. package/src/lib/assets/site-styles/beauty.webp +0 -0
  127. package/src/lib/assets/site-styles/blog.webp +0 -0
  128. package/src/lib/assets/site-styles/business.webp +0 -0
  129. package/src/lib/assets/site-styles/coming-soon.webp +0 -0
  130. package/src/lib/assets/site-styles/education.webp +0 -0
  131. package/src/lib/assets/site-styles/events.webp +0 -0
  132. package/src/lib/assets/site-styles/fitness.webp +0 -0
  133. package/src/lib/assets/site-styles/landing.webp +0 -0
  134. package/src/lib/assets/site-styles/music.webp +0 -0
  135. package/src/lib/assets/site-styles/nonprofit.webp +0 -0
  136. package/src/lib/assets/site-styles/personal.webp +0 -0
  137. package/src/lib/assets/site-styles/photography.webp +0 -0
  138. package/src/lib/assets/site-styles/portfolio.webp +0 -0
  139. package/src/lib/assets/site-styles/professional.webp +0 -0
  140. package/src/lib/assets/site-styles/realestate.webp +0 -0
  141. package/src/lib/assets/site-styles/restaurant.webp +0 -0
  142. package/src/lib/assets/site-styles/store.webp +0 -0
  143. package/src/lib/assets/site-styles/studio.webp +0 -0
  144. package/src/lib/assets/site-styles/trades.webp +0 -0
  145. package/src/lib/assets/site-styles/wellness.webp +0 -0
  146. package/src/lib/assets/site-styles/yoga.webp +0 -0
  147. package/src/lib/site-import/site-import-review.d.ts +0 -40
  148. package/src/lib/site-import/site-import-review.js +0 -149
  149. package/src/lib/site-import/site-import-review.js.map +0 -1
@@ -0,0 +1,437 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ /*==========================================
18
+ * THE SOURCE'S MENU AND FOOTER, AS IT BUILDS THEM (AGL-4106).
19
+ *
20
+ * The import's header used to be the wave's page titles, seven of them, cut
21
+ * to 28 characters, with no menus: the source's own menu was read as a flat
22
+ * list of links and used only for their order. This reads it as the source
23
+ * builds it, with no model:
24
+ *
25
+ * - **The menu** (`siteImportNavTree`). Each top-level item of the header's
26
+ * `<nav>` row: its link, and the panel that opens under it (a container of
27
+ * two or more links), each entry with its name, the line under it in a mega
28
+ * menu, its address, its icon and the heading of the column it sits in.
29
+ * The header's buttons outside the row ("Sign in", "Start free") with their
30
+ * addresses and how they are drawn; whether the source shows a menu button
31
+ * on phones; the header bar's background.
32
+ * - **The footer** (`siteImportFooterColumns`). Each column: its heading and
33
+ * its links. Its profiles on other sites, its other links, its lines of
34
+ * text, its address, phone and email (from its structured data where the
35
+ * footer prints none), and its © line word for word.
36
+ *
37
+ * Every label is the source's, as written. Untrusted data: it is drawn by the
38
+ * frame compiler as words, and fenced wherever a prompt reads it.
39
+ *=========================================*/ import { AI_SITE_IMPORT_LABEL_MAX_CHARS } from "../model/ai-site-import.js";
40
+ import { siteImportBackgroundOf, siteImportCssOf, siteImportHex, siteImportStylesOf } from "./site-import-css.js";
41
+ import { siteImportButtonStyleOf } from "./site-import-layout.js";
42
+ /** The most top-level items, entries per menu and footer links kept. */ export const SITE_IMPORT_NAV_LIMITS = {
43
+ items: 12,
44
+ entries: 40,
45
+ footerLinks: 160
46
+ };
47
+ const PHONE_MENU = /\b(?:menu|navigation|nav)\b/i;
48
+ const PHONE_MENU_CLASS = /hamburger|burger|menu-?toggle|nav-?toggle|mobile-?menu|navbar-toggler|menu-?button|header-menu-toggle|drawer-?toggle/i;
49
+ const SOCIAL_NETWORKS = [
50
+ [
51
+ /(?:^|\.)(?:twitter\.com|x\.com)$/i,
52
+ 'x'
53
+ ],
54
+ [
55
+ /(?:^|\.)facebook\.com$/i,
56
+ 'facebook'
57
+ ],
58
+ [
59
+ /(?:^|\.)instagram\.com$/i,
60
+ 'instagram'
61
+ ],
62
+ [
63
+ /(?:^|\.)linkedin\.com$/i,
64
+ 'linkedin'
65
+ ],
66
+ [
67
+ /(?:^|\.)youtube\.com$/i,
68
+ 'youtube'
69
+ ],
70
+ [
71
+ /(?:^|\.)tiktok\.com$/i,
72
+ 'tiktok'
73
+ ],
74
+ [
75
+ /(?:^|\.)github\.com$/i,
76
+ 'github'
77
+ ],
78
+ [
79
+ /(?:^|\.)pinterest\.com$/i,
80
+ 'pinterest'
81
+ ],
82
+ [
83
+ /(?:^|\.)threads\.net$/i,
84
+ 'threads'
85
+ ],
86
+ [
87
+ /(?:^|\.)yelp\.com$/i,
88
+ 'yelp'
89
+ ],
90
+ [
91
+ /(?:^|\.)tripadvisor\.com$/i,
92
+ 'tripadvisor'
93
+ ],
94
+ [
95
+ /(?:^|\.)nextdoor\.com$/i,
96
+ 'nextdoor'
97
+ ],
98
+ [
99
+ /(?:^|\.)vimeo\.com$/i,
100
+ 'vimeo'
101
+ ],
102
+ [
103
+ /(?:^|\.)bsky\.app$/i,
104
+ 'bluesky'
105
+ ],
106
+ [
107
+ /(?:^|\.)mastodon\.social$/i,
108
+ 'mastodon'
109
+ ],
110
+ [
111
+ /(?:^|\.)discord\.(?:gg|com)$/i,
112
+ 'discord'
113
+ ],
114
+ [
115
+ /(?:^|\.)whatsapp\.com$|(?:^|\.)wa\.me$/i,
116
+ 'whatsapp'
117
+ ]
118
+ ];
119
+ /** The network a profile address is on, or `null` for an address that is no profile. */ export function siteImportSocialNetwork(href) {
120
+ try {
121
+ var _ref;
122
+ var _SOCIAL_NETWORKS_find;
123
+ const host = new URL(href).hostname;
124
+ return (_ref = (_SOCIAL_NETWORKS_find = SOCIAL_NETWORKS.find(([pattern])=>pattern.test(host))) == null ? void 0 : _SOCIAL_NETWORKS_find[1]) != null ? _ref : null;
125
+ } catch (unused) {
126
+ return null;
127
+ }
128
+ }
129
+ const clean = (text, max = 300)=>{
130
+ const line = String(text != null ? text : '').replace(/\s+/g, ' ').trim();
131
+ return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;
132
+ };
133
+ let FrameReader = class FrameReader {
134
+ anchors(element) {
135
+ const held = this.anchorMemo.get(element);
136
+ if (held) return held;
137
+ const node = this.elements[element];
138
+ if (!node) return [];
139
+ const own = this.anchorAt.get(element);
140
+ const list = own ? [
141
+ own
142
+ ] : [];
143
+ for (const child of node.children)list.push(...this.anchors(child));
144
+ this.anchorMemo.set(element, list);
145
+ return list;
146
+ }
147
+ contains(ancestor, element) {
148
+ var _ref;
149
+ var _this_elements_at;
150
+ for(let at = element; at >= 0; at = (_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.parent) != null ? _ref : -1)if (at === ancestor) return true;
151
+ return false;
152
+ }
153
+ /** The words a menu holds outside its links, by element: a column's heading, a button's name. */ texts(element) {
154
+ var _this_page_structure_texts;
155
+ const texts = (_this_page_structure_texts = this.page.structure.texts) != null ? _this_page_structure_texts : [];
156
+ return texts.filter((entry)=>this.contains(element, entry.element));
157
+ }
158
+ first(test) {
159
+ return this.elements.find(test);
160
+ }
161
+ background(element) {
162
+ for(let at = element, depth = 0; at >= 0 && depth < 4; depth += 1){
163
+ var _ref;
164
+ var _this_elements_at;
165
+ const color = siteImportBackgroundOf(this.styles.of(at)).color;
166
+ if (color && color.a >= 0.5) return siteImportHex(color);
167
+ const children = ((_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.children) != null ? _ref : []).filter((child)=>{
168
+ var _this_elements_child;
169
+ return this.anchors(child).length || ((_this_elements_child = this.elements[child]) == null ? void 0 : _this_elements_child.children.length);
170
+ });
171
+ at = children.length === 1 ? children[0] : -1;
172
+ }
173
+ return null;
174
+ }
175
+ buttonStyle(element) {
176
+ var _ref;
177
+ var _this_elements_at;
178
+ const node = this.elements[element];
179
+ if (!node) return null;
180
+ let under = null;
181
+ for(let at = node.parent; at >= 0; at = (_ref = (_this_elements_at = this.elements[at]) == null ? void 0 : _this_elements_at.parent) != null ? _ref : -1){
182
+ const color = siteImportBackgroundOf(this.styles.of(at)).color;
183
+ if (color && color.a >= 0.5) {
184
+ under = siteImportHex(color);
185
+ break;
186
+ }
187
+ }
188
+ return siteImportButtonStyleOf(node, this.styles.of(element), under != null ? under : '#ffffff');
189
+ }
190
+ constructor(page, styles){
191
+ this.page = page;
192
+ this.styles = styles;
193
+ /** Each element's anchors, its subtree's, in order. */ this.anchorMemo = new Map();
194
+ this.anchorAt = new Map();
195
+ this.elements = page.structure.elements;
196
+ for (const anchor of page.structure.anchors)this.anchorAt.set(anchor.element, anchor);
197
+ }
198
+ };
199
+ function frameReader(page, stylesheets) {
200
+ const css = siteImportCssOf([
201
+ ...page.structure.styles,
202
+ ...stylesheets
203
+ ]);
204
+ return new FrameReader(page, siteImportStylesOf(page.structure, css));
205
+ }
206
+ /**
207
+ * The source's header menu (AGL-4106), as its home page builds it; `null`
208
+ * for a page with no header menu the reader can find.
209
+ */ export function siteImportNavTree(page, stylesheets = []) {
210
+ var _ref, _reader_first, _ref1, _reader_first1, _ref2;
211
+ var _reader_elements_row;
212
+ if (!page.structure) return null;
213
+ const reader = frameReader(page, stylesheets);
214
+ const header = (_ref = (_reader_first = reader.first((node)=>node.tag === 'header' && node.region === 'header')) != null ? _reader_first : reader.first((node)=>node.attrs.role === 'banner')) != null ? _ref : reader.first((node)=>node.tag === 'nav' && node.region !== 'footer');
215
+ if (!header) return null;
216
+ const nav = (_ref1 = (_reader_first1 = reader.first((node)=>node.tag === 'nav' && reader.contains(header.id, node.id) && reader.anchors(node.id).length >= 2)) != null ? _reader_first1 : reader.first((node)=>node.tag === 'nav' && node.region !== 'footer' && reader.anchors(node.id).length >= 2)) != null ? _ref1 : header;
217
+ // The row: down through wrappers to the element whose children are the items.
218
+ let row = nav.id;
219
+ for(let guard = 0; guard < 32; guard += 1){
220
+ var _ref3;
221
+ var _reader_elements_row1;
222
+ const children = ((_ref3 = (_reader_elements_row1 = reader.elements[row]) == null ? void 0 : _reader_elements_row1.children) != null ? _ref3 : []).filter((child)=>reader.anchors(child).length || reader.texts(child).length);
223
+ if (children.length !== 1) break;
224
+ row = children[0];
225
+ }
226
+ const brand = (anchor)=>{
227
+ try {
228
+ var _anchor_href;
229
+ return new URL((_anchor_href = anchor.href) != null ? _anchor_href : '').pathname === '/' && (!!anchor.image || !anchor.label);
230
+ } catch (unused) {
231
+ return false;
232
+ }
233
+ };
234
+ const items = [];
235
+ const ctas = [];
236
+ const inRow = new Set();
237
+ for (const child of (_ref2 = (_reader_elements_row = reader.elements[row]) == null ? void 0 : _reader_elements_row.children) != null ? _ref2 : []){
238
+ var _ref4, _ref5, _ref6;
239
+ const anchors = reader.anchors(child).filter((anchor)=>!brand(anchor) && (anchor.label || anchor.href));
240
+ const texts = reader.texts(child);
241
+ if (!anchors.length && !texts.length) continue;
242
+ anchors.forEach((anchor)=>inRow.add(anchor));
243
+ // A top-level link drawn as a button is a call to action, as the source draws it.
244
+ if (anchors.length === 1 && !texts.length) {
245
+ const anchor = anchors[0];
246
+ const style = reader.buttonStyle(anchor.element);
247
+ if (style && style !== 'quiet') {
248
+ ctas.push({
249
+ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),
250
+ href: anchor.href,
251
+ style
252
+ });
253
+ continue;
254
+ }
255
+ }
256
+ // The item's name: its first link, or the words of the button that opens its menu.
257
+ const trigger = anchors[0];
258
+ const opener = texts.find((entry)=>!trigger || entry.element < trigger.element);
259
+ const label = clean((_ref4 = (_ref5 = opener == null ? void 0 : opener.text) != null ? _ref5 : trigger == null ? void 0 : trigger.label) != null ? _ref4 : '', AI_SITE_IMPORT_LABEL_MAX_CHARS * 2);
260
+ if (!label) continue;
261
+ const entries = (opener ? anchors : anchors.slice(1)).slice(0, SITE_IMPORT_NAV_LIMITS.entries);
262
+ items.push({
263
+ label,
264
+ href: opener ? null : (_ref6 = trigger == null ? void 0 : trigger.href) != null ? _ref6 : null,
265
+ items: entries.filter((anchor)=>anchor.href).map((anchor)=>navEntry(reader, anchor, child))
266
+ });
267
+ if (items.length >= SITE_IMPORT_NAV_LIMITS.items) break;
268
+ }
269
+ // The header's buttons outside the menu row.
270
+ for (const anchor of reader.anchors(header.id)){
271
+ if (inRow.has(anchor) || brand(anchor) || !anchor.label || reader.contains(row, anchor.element)) continue;
272
+ const style = reader.buttonStyle(anchor.element);
273
+ if (!style) continue;
274
+ if (!ctas.some((cta)=>cta.label === anchor.label && cta.href === anchor.href)) ctas.push({
275
+ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),
276
+ href: anchor.href,
277
+ style
278
+ });
279
+ }
280
+ const phoneMenu = reader.elements.some((node)=>{
281
+ var _node_attrs_class, _node_attrs_label, _node_attrs_class1, _node_attrs_controls;
282
+ if (node.region !== 'header' && !reader.contains(header.id, node.id)) return false;
283
+ if (node.tag !== 'button' && node.attrs.role !== 'button' && node.tag !== 'summary' && node.tag !== 'a') return false;
284
+ if (node.tag === 'a' && !PHONE_MENU_CLASS.test((_node_attrs_class = node.attrs.class) != null ? _node_attrs_class : '')) return false;
285
+ return PHONE_MENU.test((_node_attrs_label = node.attrs.label) != null ? _node_attrs_label : '') || PHONE_MENU_CLASS.test((_node_attrs_class1 = node.attrs.class) != null ? _node_attrs_class1 : '') || /menu|nav|drawer/i.test((_node_attrs_controls = node.attrs.controls) != null ? _node_attrs_controls : '');
286
+ });
287
+ if (!items.length && !ctas.length) return null;
288
+ return {
289
+ items,
290
+ ctas,
291
+ phoneMenu,
292
+ background: reader.background(header.id)
293
+ };
294
+ }
295
+ /** One entry of a menu, with the heading of the column it sits in. */ function navEntry(reader, anchor, item) {
296
+ const icon = anchor.icon !== undefined ? reader.page.structure.icons[anchor.icon] : undefined;
297
+ const column = columnHeading(reader, anchor.element, item);
298
+ return _extends({
299
+ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2)
300
+ }, anchor.text ? {
301
+ text: clean(anchor.text)
302
+ } : {}, {
303
+ href: anchor.href
304
+ }, (icon == null ? void 0 : icon.path) ? {
305
+ iconPath: icon.path,
306
+ viewBox: icon.viewBox
307
+ } : {}, column ? {
308
+ column
309
+ } : {});
310
+ }
311
+ /**
312
+ * The words that head the column an entry sits in: the first words, outside
313
+ * any link, of the nearest container around the entry that has words before
314
+ * its first link ("By team" over Agencies, Startups, Creators).
315
+ */ function columnHeading(reader, element, item) {
316
+ var _ref, _ref1;
317
+ var _reader_elements_element, _reader_elements_at;
318
+ const own = reader.texts(item).filter((entry)=>!reader.anchors(item).some((anchor)=>reader.contains(anchor.element, entry.element)));
319
+ for(let at = (_ref = (_reader_elements_element = reader.elements[element]) == null ? void 0 : _reader_elements_element.parent) != null ? _ref : -1; at >= 0 && at !== item; at = (_ref1 = (_reader_elements_at = reader.elements[at]) == null ? void 0 : _reader_elements_at.parent) != null ? _ref1 : -1){
320
+ const first = own.find((entry)=>reader.contains(at, entry.element));
321
+ if (!first) continue;
322
+ const firstLink = reader.anchors(at)[0];
323
+ if (firstLink && first.element > firstLink.element) return null;
324
+ return first.text.length <= AI_SITE_IMPORT_LABEL_MAX_CHARS ? clean(first.text, AI_SITE_IMPORT_LABEL_MAX_CHARS) : null;
325
+ }
326
+ return null;
327
+ }
328
+ const EMAIL = /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/;
329
+ const PHONE = /(?:\+?\d[\d\s().-]{7,}\d)/;
330
+ const ADDRESS = RegExp("\\d+\\s+[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\d .'-]+(?:street|st\\.?|avenue|ave\\.?|road|rd\\.?|drive|dr\\.?|lane|ln\\.?|boulevard|blvd\\.?|way|court|ct\\.?|place|pl\\.?|suite|ste\\.?|highway|hwy\\.?|parkway|pkwy)\\b", "iu");
331
+ /**
332
+ * The source's footer (AGL-4106), as its home page builds it; `null` for a
333
+ * page with no footer.
334
+ */ export function siteImportFooterColumns(page, stylesheets = []) {
335
+ if (!page.structure) return null;
336
+ const reader = frameReader(page, stylesheets);
337
+ const footers = reader.elements.filter((node)=>(node.tag === 'footer' || node.attrs.role === 'contentinfo') && node.region === 'footer');
338
+ const footer = footers.find((node)=>!footers.some((other)=>other !== node && reader.contains(other.id, node.id)));
339
+ if (!footer) return null;
340
+ const { blocks, structure } = page;
341
+ const leaves = [];
342
+ structure.blockElement.forEach((element, index)=>{
343
+ const block = blocks[index];
344
+ if (!block || block.region !== 'footer' || block.kind === 'image' || block.kind === 'link') return;
345
+ // A line that is only its link's words is that link.
346
+ const next = blocks[index + 1];
347
+ if ((next == null ? void 0 : next.kind) === 'link' && next.inline && next.text === block.text) return;
348
+ if (reader.contains(footer.id, element)) leaves.push({
349
+ element,
350
+ text: clean(block.text, 1000),
351
+ href: null
352
+ });
353
+ });
354
+ for (const entry of reader.texts(footer.id))leaves.push({
355
+ element: entry.element,
356
+ text: clean(entry.text, 1000),
357
+ href: null
358
+ });
359
+ for (const anchor of reader.anchors(footer.id)){
360
+ var _reader_elements_anchor_element;
361
+ // An icon link is named by its label: a profile's network.
362
+ const label = anchor.label || ((_reader_elements_anchor_element = reader.elements[anchor.element]) == null ? void 0 : _reader_elements_anchor_element.attrs.label) || '';
363
+ if (anchor.href && label) leaves.push({
364
+ element: anchor.element,
365
+ text: clean(label),
366
+ href: anchor.href
367
+ });
368
+ }
369
+ leaves.sort((a, b)=>a.element - b.element);
370
+ const within = (element)=>leaves.filter((leaf)=>reader.contains(element, leaf.element));
371
+ // A column: a heading, then its links, nothing else.
372
+ const columns = [];
373
+ const inColumns = new Set();
374
+ for (const node of reader.elements){
375
+ if (!reader.contains(footer.id, node.id) || node.id === footer.id) continue;
376
+ const own = within(node.id);
377
+ const [heading, ...rest] = own;
378
+ if (!heading || heading.href || heading.text.length > AI_SITE_IMPORT_LABEL_MAX_CHARS || rest.length < 2 || rest.some((leaf)=>!leaf.href)) continue;
379
+ if (own.some((leaf)=>inColumns.has(leaf))) continue;
380
+ own.forEach((leaf)=>inColumns.add(leaf));
381
+ columns.push({
382
+ heading: heading.text,
383
+ links: rest.slice(0, SITE_IMPORT_NAV_LIMITS.footerLinks).map((leaf)=>({
384
+ label: leaf.text,
385
+ href: leaf.href
386
+ }))
387
+ });
388
+ }
389
+ const social = [];
390
+ const links = [];
391
+ const text = [];
392
+ let copyright = null;
393
+ let address = null;
394
+ let phone = null;
395
+ let email = null;
396
+ for (const leaf of leaves){
397
+ if (inColumns.has(leaf)) continue;
398
+ if (leaf.href) {
399
+ const network = siteImportSocialNetwork(leaf.href);
400
+ if (network) {
401
+ if (!social.some((entry)=>entry.href === leaf.href)) social.push({
402
+ label: leaf.text,
403
+ href: leaf.href,
404
+ network
405
+ });
406
+ } else if (leaf.href.startsWith('mailto:')) email != null ? email : email = leaf.href.slice('mailto:'.length);
407
+ else if (leaf.href.startsWith('tel:')) phone != null ? phone : phone = leaf.text;
408
+ else if (!links.some((entry)=>entry.href === leaf.href && entry.label === leaf.text)) links.push({
409
+ label: leaf.text,
410
+ href: leaf.href
411
+ });
412
+ continue;
413
+ }
414
+ if (!copyright && /©|\(c\)\s*\d{4}|\bcopyright\b/i.test(leaf.text)) {
415
+ copyright = leaf.text;
416
+ continue;
417
+ }
418
+ if (!email && EMAIL.test(leaf.text) && leaf.text.length <= 120) email = EMAIL.exec(leaf.text)[0];
419
+ else if (!phone && PHONE.test(leaf.text) && leaf.text.length <= 40) phone = leaf.text;
420
+ else if (!address && ADDRESS.test(leaf.text) && leaf.text.length <= 200) address = leaf.text;
421
+ else if (!text.includes(leaf.text)) text.push(leaf.text);
422
+ }
423
+ // What the footer does not print, the site's own structured data says.
424
+ const organization = page.organization;
425
+ return {
426
+ columns,
427
+ social,
428
+ links,
429
+ text,
430
+ address: address != null ? address : (organization == null ? void 0 : organization.address) || null,
431
+ phone: phone != null ? phone : (organization == null ? void 0 : organization.telephone) || null,
432
+ email: email != null ? email : (organization == null ? void 0 : organization.email) || null,
433
+ copyright
434
+ };
435
+ }
436
+
437
+ //# sourceMappingURL=site-import-nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/site-import/site-import-nav.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 SOURCE'S MENU AND FOOTER, AS IT BUILDS THEM (AGL-4106).\n *\n * The import's header used to be the wave's page titles, seven of them, cut\n * to 28 characters, with no menus: the source's own menu was read as a flat\n * list of links and used only for their order. This reads it as the source\n * builds it, with no model:\n *\n * - **The menu** (`siteImportNavTree`). Each top-level item of the header's\n * `<nav>` row: its link, and the panel that opens under it (a container of\n * two or more links), each entry with its name, the line under it in a mega\n * menu, its address, its icon and the heading of the column it sits in.\n * The header's buttons outside the row (\"Sign in\", \"Start free\") with their\n * addresses and how they are drawn; whether the source shows a menu button\n * on phones; the header bar's background.\n * - **The footer** (`siteImportFooterColumns`). Each column: its heading and\n * its links. Its profiles on other sites, its other links, its lines of\n * text, its address, phone and email (from its structured data where the\n * footer prints none), and its © line word for word.\n *\n * Every label is the source's, as written. Untrusted data: it is drawn by the\n * frame compiler as words, and fenced wherever a prompt reads it.\n *=========================================*/\n\nimport {\n AI_SITE_IMPORT_LABEL_MAX_CHARS,\n type SiteImportButtonStyle,\n type SiteImportFooter,\n type SiteImportFooterLink,\n type SiteImportNavCta,\n type SiteImportNavEntry,\n type SiteImportNavGroup,\n type SiteImportNavTree,\n} from '../model/ai-site-import'\nimport { siteImportBackgroundOf, siteImportCssOf, siteImportHex, siteImportStylesOf, type SiteImportStyles } from './site-import-css'\nimport type { SiteImportAnchor, SiteImportElement, SiteImportExtract, SiteImportStructure } from './site-import-html'\nimport { siteImportButtonStyleOf } from './site-import-layout'\n\n/** A page as the menu reader takes it: its structure, its blocks and its structured data. */\nexport type SiteImportFrameSource = Pick<SiteImportExtract, 'blocks' | 'organization'> & { structure: SiteImportStructure }\n\n/** The most top-level items, entries per menu and footer links kept. */\nexport const SITE_IMPORT_NAV_LIMITS = { items: 12, entries: 40, footerLinks: 160 } as const\n\nconst PHONE_MENU = /\\b(?:menu|navigation|nav)\\b/i\nconst PHONE_MENU_CLASS = /hamburger|burger|menu-?toggle|nav-?toggle|mobile-?menu|navbar-toggler|menu-?button|header-menu-toggle|drawer-?toggle/i\n\nconst SOCIAL_NETWORKS: ReadonlyArray<[RegExp, string]> = [\n [/(?:^|\\.)(?:twitter\\.com|x\\.com)$/i, 'x'],\n [/(?:^|\\.)facebook\\.com$/i, 'facebook'],\n [/(?:^|\\.)instagram\\.com$/i, 'instagram'],\n [/(?:^|\\.)linkedin\\.com$/i, 'linkedin'],\n [/(?:^|\\.)youtube\\.com$/i, 'youtube'],\n [/(?:^|\\.)tiktok\\.com$/i, 'tiktok'],\n [/(?:^|\\.)github\\.com$/i, 'github'],\n [/(?:^|\\.)pinterest\\.com$/i, 'pinterest'],\n [/(?:^|\\.)threads\\.net$/i, 'threads'],\n [/(?:^|\\.)yelp\\.com$/i, 'yelp'],\n [/(?:^|\\.)tripadvisor\\.com$/i, 'tripadvisor'],\n [/(?:^|\\.)nextdoor\\.com$/i, 'nextdoor'],\n [/(?:^|\\.)vimeo\\.com$/i, 'vimeo'],\n [/(?:^|\\.)bsky\\.app$/i, 'bluesky'],\n [/(?:^|\\.)mastodon\\.social$/i, 'mastodon'],\n [/(?:^|\\.)discord\\.(?:gg|com)$/i, 'discord'],\n [/(?:^|\\.)whatsapp\\.com$|(?:^|\\.)wa\\.me$/i, 'whatsapp'],\n]\n\n/** The network a profile address is on, or `null` for an address that is no profile. */\nexport function siteImportSocialNetwork(href: string): string | null {\n try {\n const host = new URL(href).hostname\n return SOCIAL_NETWORKS.find(([pattern]) => pattern.test(host))?.[1] ?? null\n } catch {\n return null\n }\n}\n\nconst clean = (text: string, max = 300) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line\n}\n\nclass FrameReader {\n readonly elements: readonly SiteImportElement[]\n /** Each element's anchors, its subtree's, in order. */\n private readonly anchorMemo = new Map<number, SiteImportAnchor[]>()\n private readonly anchorAt = new Map<number, SiteImportAnchor>()\n\n constructor(\n readonly page: SiteImportFrameSource,\n readonly styles: SiteImportStyles,\n ) {\n this.elements = page.structure.elements\n for (const anchor of page.structure.anchors) this.anchorAt.set(anchor.element, anchor)\n }\n\n anchors(element: number): SiteImportAnchor[] {\n const held = this.anchorMemo.get(element)\n if (held) return held\n const node = this.elements[element]\n if (!node) return []\n const own = this.anchorAt.get(element)\n const list = own ? [own] : []\n for (const child of node.children) list.push(...this.anchors(child))\n this.anchorMemo.set(element, list)\n return list\n }\n\n contains(ancestor: number, element: number): boolean {\n for (let at = element; at >= 0; at = this.elements[at]?.parent ?? -1) if (at === ancestor) return true\n return false\n }\n\n /** The words a menu holds outside its links, by element: a column's heading, a button's name. */\n texts(element: number): Array<{ element: number; text: string }> {\n const texts = this.page.structure.texts ?? []\n return texts.filter((entry) => this.contains(element, entry.element))\n }\n\n first(test: (node: SiteImportElement) => boolean): SiteImportElement | undefined {\n return this.elements.find(test)\n }\n\n background(element: number): string | null {\n for (let at = element, depth = 0; at >= 0 && depth < 4; depth += 1) {\n const color = siteImportBackgroundOf(this.styles.of(at)).color\n if (color && color.a >= 0.5) return siteImportHex(color)\n const children: number[] = (this.elements[at]?.children ?? []).filter((child) => this.anchors(child).length || this.elements[child]?.children.length)\n at = children.length === 1 ? (children[0] as number) : -1\n }\n return null\n }\n\n buttonStyle(element: number): SiteImportButtonStyle | null {\n const node = this.elements[element]\n if (!node) return null\n let under: string | null = null\n for (let at = node.parent; at >= 0; at = this.elements[at]?.parent ?? -1) {\n const color = siteImportBackgroundOf(this.styles.of(at)).color\n if (color && color.a >= 0.5) {\n under = siteImportHex(color)\n break\n }\n }\n return siteImportButtonStyleOf(node, this.styles.of(element), under ?? '#ffffff')\n }\n}\n\nfunction frameReader(page: SiteImportFrameSource, stylesheets: readonly string[]): FrameReader {\n const css = siteImportCssOf([...page.structure.styles, ...stylesheets])\n return new FrameReader(page, siteImportStylesOf(page.structure, css))\n}\n\n/**\n * The source's header menu (AGL-4106), as its home page builds it; `null`\n * for a page with no header menu the reader can find.\n */\nexport function siteImportNavTree(page: SiteImportFrameSource, stylesheets: readonly string[] = []): SiteImportNavTree | null {\n if (!page.structure) return null\n const reader = frameReader(page, stylesheets)\n const header =\n reader.first((node) => node.tag === 'header' && node.region === 'header') ??\n reader.first((node) => node.attrs.role === 'banner') ??\n reader.first((node) => node.tag === 'nav' && node.region !== 'footer')\n if (!header) return null\n const nav =\n reader.first((node) => node.tag === 'nav' && reader.contains(header.id, node.id) && reader.anchors(node.id).length >= 2) ??\n reader.first((node) => node.tag === 'nav' && node.region !== 'footer' && reader.anchors(node.id).length >= 2) ??\n header\n // The row: down through wrappers to the element whose children are the items.\n let row = nav.id\n for (let guard = 0; guard < 32; guard += 1) {\n const children = (reader.elements[row]?.children ?? []).filter((child) => reader.anchors(child).length || reader.texts(child).length)\n if (children.length !== 1) break\n row = children[0] as number\n }\n const brand = (anchor: SiteImportAnchor) => {\n try {\n return new URL(anchor.href ?? '').pathname === '/' && (!!anchor.image || !anchor.label)\n } catch {\n return false\n }\n }\n const items: SiteImportNavGroup[] = []\n const ctas: SiteImportNavCta[] = []\n const inRow = new Set<SiteImportAnchor>()\n for (const child of reader.elements[row]?.children ?? []) {\n const anchors = reader.anchors(child).filter((anchor) => !brand(anchor) && (anchor.label || anchor.href))\n const texts = reader.texts(child)\n if (!anchors.length && !texts.length) continue\n anchors.forEach((anchor) => inRow.add(anchor))\n // A top-level link drawn as a button is a call to action, as the source draws it.\n if (anchors.length === 1 && !texts.length) {\n const anchor = anchors[0] as SiteImportAnchor\n const style = reader.buttonStyle(anchor.element)\n if (style && style !== 'quiet') {\n ctas.push({ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2), href: anchor.href, style })\n continue\n }\n }\n // The item's name: its first link, or the words of the button that opens its menu.\n const trigger = anchors[0]\n const opener = texts.find((entry) => !trigger || entry.element < trigger.element)\n const label = clean(opener?.text ?? trigger?.label ?? '', AI_SITE_IMPORT_LABEL_MAX_CHARS * 2)\n if (!label) continue\n const entries = (opener ? anchors : anchors.slice(1)).slice(0, SITE_IMPORT_NAV_LIMITS.entries)\n items.push({\n label,\n href: opener ? null : (trigger?.href ?? null),\n items: entries.filter((anchor) => anchor.href).map((anchor) => navEntry(reader, anchor, child)),\n })\n if (items.length >= SITE_IMPORT_NAV_LIMITS.items) break\n }\n // The header's buttons outside the menu row.\n for (const anchor of reader.anchors(header.id)) {\n if (inRow.has(anchor) || brand(anchor) || !anchor.label || reader.contains(row, anchor.element)) continue\n const style = reader.buttonStyle(anchor.element)\n if (!style) continue\n if (!ctas.some((cta) => cta.label === anchor.label && cta.href === anchor.href)) ctas.push({ label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2), href: anchor.href, style })\n }\n const phoneMenu = reader.elements.some((node) => {\n if (node.region !== 'header' && !reader.contains(header.id, node.id)) return false\n if (node.tag !== 'button' && node.attrs.role !== 'button' && node.tag !== 'summary' && node.tag !== 'a') return false\n if (node.tag === 'a' && !PHONE_MENU_CLASS.test(node.attrs.class ?? '')) return false\n return PHONE_MENU.test(node.attrs.label ?? '') || PHONE_MENU_CLASS.test(node.attrs.class ?? '') || /menu|nav|drawer/i.test(node.attrs.controls ?? '')\n })\n if (!items.length && !ctas.length) return null\n return { items, ctas, phoneMenu, background: reader.background(header.id) }\n}\n\n/** One entry of a menu, with the heading of the column it sits in. */\nfunction navEntry(reader: FrameReader, anchor: SiteImportAnchor, item: number): SiteImportNavEntry {\n const icon = anchor.icon !== undefined ? reader.page.structure.icons[anchor.icon] : undefined\n const column = columnHeading(reader, anchor.element, item)\n return {\n label: clean(anchor.label, AI_SITE_IMPORT_LABEL_MAX_CHARS * 2),\n ...(anchor.text ? { text: clean(anchor.text) } : {}),\n href: anchor.href as string,\n ...(icon?.path ? { iconPath: icon.path, viewBox: icon.viewBox } : {}),\n ...(column ? { column } : {}),\n }\n}\n\n/**\n * The words that head the column an entry sits in: the first words, outside\n * any link, of the nearest container around the entry that has words before\n * its first link (\"By team\" over Agencies, Startups, Creators).\n */\nfunction columnHeading(reader: FrameReader, element: number, item: number): string | null {\n const own = reader.texts(item).filter((entry) => !reader.anchors(item).some((anchor) => reader.contains(anchor.element, entry.element)))\n for (let at = reader.elements[element]?.parent ?? -1; at >= 0 && at !== item; at = reader.elements[at]?.parent ?? -1) {\n const first = own.find((entry) => reader.contains(at, entry.element))\n if (!first) continue\n const firstLink = reader.anchors(at)[0]\n if (firstLink && first.element > firstLink.element) return null\n return first.text.length <= AI_SITE_IMPORT_LABEL_MAX_CHARS ? clean(first.text, AI_SITE_IMPORT_LABEL_MAX_CHARS) : null\n }\n return null\n}\n\nconst EMAIL = /[\\w.+-]+@[\\w-]+(?:\\.[\\w-]+)+/\nconst PHONE = /(?:\\+?\\d[\\d\\s().-]{7,}\\d)/\nconst ADDRESS = /\\d+\\s+[\\p{L}\\d .'-]+(?:street|st\\.?|avenue|ave\\.?|road|rd\\.?|drive|dr\\.?|lane|ln\\.?|boulevard|blvd\\.?|way|court|ct\\.?|place|pl\\.?|suite|ste\\.?|highway|hwy\\.?|parkway|pkwy)\\b/iu\n\n/**\n * The source's footer (AGL-4106), as its home page builds it; `null` for a\n * page with no footer.\n */\nexport function siteImportFooterColumns(page: SiteImportFrameSource, stylesheets: readonly string[] = []): SiteImportFooter | null {\n if (!page.structure) return null\n const reader = frameReader(page, stylesheets)\n const footers = reader.elements.filter((node) => (node.tag === 'footer' || node.attrs.role === 'contentinfo') && node.region === 'footer')\n const footer = footers.find((node) => !footers.some((other) => other !== node && reader.contains(other.id, node.id)))\n if (!footer) return null\n const { blocks, structure } = page\n // The footer's words in order: its text blocks, its menu's words, and its links.\n type Leaf = { element: number; text: string; href: string | null }\n const leaves: Leaf[] = []\n structure.blockElement.forEach((element, index) => {\n const block = blocks[index]\n if (!block || block.region !== 'footer' || block.kind === 'image' || block.kind === 'link') return\n // A line that is only its link's words is that link.\n const next = blocks[index + 1]\n if (next?.kind === 'link' && next.inline && next.text === block.text) return\n if (reader.contains(footer.id, element)) leaves.push({ element, text: clean(block.text, 1_000), href: null })\n })\n for (const entry of reader.texts(footer.id)) leaves.push({ element: entry.element, text: clean(entry.text, 1_000), href: null })\n for (const anchor of reader.anchors(footer.id)) {\n // An icon link is named by its label: a profile's network.\n const label = anchor.label || reader.elements[anchor.element]?.attrs.label || ''\n if (anchor.href && label) leaves.push({ element: anchor.element, text: clean(label), href: anchor.href })\n }\n leaves.sort((a, b) => a.element - b.element)\n const within = (element: number) => leaves.filter((leaf) => reader.contains(element, leaf.element))\n // A column: a heading, then its links, nothing else.\n const columns: SiteImportFooter['columns'] = []\n const inColumns = new Set<Leaf>()\n for (const node of reader.elements) {\n if (!reader.contains(footer.id, node.id) || node.id === footer.id) continue\n const own = within(node.id)\n const [heading, ...rest] = own\n if (!heading || heading.href || heading.text.length > AI_SITE_IMPORT_LABEL_MAX_CHARS || rest.length < 2 || rest.some((leaf) => !leaf.href)) continue\n if (own.some((leaf) => inColumns.has(leaf))) continue\n own.forEach((leaf) => inColumns.add(leaf))\n columns.push({ heading: heading.text, links: rest.slice(0, SITE_IMPORT_NAV_LIMITS.footerLinks).map((leaf) => ({ label: leaf.text, href: leaf.href as string })) })\n }\n const social: SiteImportFooter['social'] = []\n const links: SiteImportFooterLink[] = []\n const text: string[] = []\n let copyright: string | null = null\n let address: string | null = null\n let phone: string | null = null\n let email: string | null = null\n for (const leaf of leaves) {\n if (inColumns.has(leaf)) continue\n if (leaf.href) {\n const network = siteImportSocialNetwork(leaf.href)\n if (network) {\n if (!social.some((entry) => entry.href === leaf.href)) social.push({ label: leaf.text, href: leaf.href, network })\n } else if (leaf.href.startsWith('mailto:')) email ??= leaf.href.slice('mailto:'.length)\n else if (leaf.href.startsWith('tel:')) phone ??= leaf.text\n else if (!links.some((entry) => entry.href === leaf.href && entry.label === leaf.text)) links.push({ label: leaf.text, href: leaf.href })\n continue\n }\n if (!copyright && /©|\\(c\\)\\s*\\d{4}|\\bcopyright\\b/i.test(leaf.text)) {\n copyright = leaf.text\n continue\n }\n if (!email && EMAIL.test(leaf.text) && leaf.text.length <= 120) email = (EMAIL.exec(leaf.text) as RegExpExecArray)[0]\n else if (!phone && PHONE.test(leaf.text) && leaf.text.length <= 40) phone = leaf.text\n else if (!address && ADDRESS.test(leaf.text) && leaf.text.length <= 200) address = leaf.text\n else if (!text.includes(leaf.text)) text.push(leaf.text)\n }\n // What the footer does not print, the site's own structured data says.\n const organization = page.organization\n return {\n columns,\n social,\n links,\n text,\n address: address ?? (organization?.address || null),\n phone: phone ?? (organization?.telephone || null),\n email: email ?? (organization?.email || null),\n copyright,\n }\n}\n"],"names":["AI_SITE_IMPORT_LABEL_MAX_CHARS","siteImportBackgroundOf","siteImportCssOf","siteImportHex","siteImportStylesOf","siteImportButtonStyleOf","SITE_IMPORT_NAV_LIMITS","items","entries","footerLinks","PHONE_MENU","PHONE_MENU_CLASS","SOCIAL_NETWORKS","siteImportSocialNetwork","href","host","URL","hostname","find","pattern","test","clean","text","max","line","String","replace","trim","length","slice","trimEnd","FrameReader","anchors","element","held","anchorMemo","get","node","elements","own","anchorAt","list","child","children","push","set","contains","ancestor","at","parent","texts","page","structure","filter","entry","first","background","depth","color","styles","of","a","buttonStyle","under","Map","anchor","frameReader","stylesheets","css","siteImportNavTree","reader","header","tag","region","attrs","role","nav","id","row","guard","brand","pathname","image","label","ctas","inRow","Set","opener","forEach","add","style","trigger","map","navEntry","has","some","cta","phoneMenu","class","controls","item","icon","undefined","icons","column","columnHeading","path","iconPath","viewBox","firstLink","EMAIL","PHONE","ADDRESS","siteImportFooterColumns","footers","footer","other","blocks","leaves","blockElement","index","block","kind","next","inline","sort","b","within","leaf","columns","inColumns","heading","rest","links","social","copyright","address","phone","email","network","startsWith","exec","includes","organization","telephone"],"mappings":"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"}
@@ -44,8 +44,15 @@ export declare function siteImportPageImages(page: Pick<SiteImportPage, 'blocks'
44
44
  export declare function siteImportPageSections(page: Pick<SiteImportPage, 'title' | 'blocks'>): string[];
45
45
  /** The page's text in order, as a prompt shows it inside its fence. */
46
46
  export declare function siteImportPageText(page: Pick<SiteImportPage, 'title' | 'description' | 'blocks'>): string;
47
- /** The page as a wave reads it: its main region's blocks whole, within the read's budget. */
47
+ /**
48
+ * The page as a wave reads it: its main region's blocks whole, within the
49
+ * read's budget, and how the source lays them out (AGL-4106) over the blocks
50
+ * kept, its icons' drawings dropped first when it runs past its own budget.
51
+ */
48
52
  export declare function siteImportPageRead(page: SiteImportPage, at: string): AiSiteImportPageRead;
53
+ /** The most characters of a source's title and description a read keeps: far past what any listing shows. */
54
+ export declare const SITE_IMPORT_META_TITLE_MAX_CHARS = 300;
55
+ export declare const SITE_IMPORT_META_DESCRIPTION_MAX_CHARS = 1000;
49
56
  /**
50
57
  * How the source's header draws its brand: its logo alone (aglyn.com), its
51
58
  * logo with its name beside it, or its name alone. `text` is the name as the
@@ -36,7 +36,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
36
36
  * its own facts (`siteImportSiteFacts`), for the header and footer the import
37
37
  * builds.
38
38
  *=========================================*/ import { neutralizeUntrustedText } from "@aglyn/shared-util-tools/untrusted-text";
39
- import { AI_SITE_IMPORT_BLOCKED_STATUSES, AI_SITE_IMPORT_IMAGES_PER_PAGE, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_MAX_SECTIONS, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS, AI_SITE_IMPORT_READ_MAX_CHARS, aiSiteImportCapBlocks } from "../model/ai-site-import.js";
39
+ import { AI_SITE_IMPORT_BLOCKED_STATUSES, AI_SITE_IMPORT_IMAGES_PER_PAGE, AI_SITE_IMPORT_LABEL_MAX_CHARS, AI_SITE_IMPORT_MAX_SECTIONS, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS, AI_SITE_IMPORT_READ_MAX_CHARS, aiSiteImportCapBlocks, AI_SITE_IMPORT_LAYOUT_MAX_CHARS, siteImportLayoutChars, siteImportLayoutWithin, siteImportLayoutWithoutDrawings } from "../model/ai-site-import.js";
40
40
  import { siteImportLogoCandidates } from "./site-import-images.js";
41
41
  /** A line of untrusted text safe to show outside a fence: neutralized, one line, cut short. */ export function siteImportLabel(text, max = AI_SITE_IMPORT_LABEL_MAX_CHARS) {
42
42
  const line = neutralizeUntrustedText(text).replace(/\s+/g, ' ').trim();
@@ -135,9 +135,16 @@ const wordsIn = (text)=>{
135
135
  const text = lines.join('\n');
136
136
  return text.length > AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS ? text.slice(0, AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS) : text;
137
137
  }
138
- /** The page as a wave reads it: its main region's blocks whole, within the read's budget. */ export function siteImportPageRead(page, at) {
138
+ /**
139
+ * The page as a wave reads it: its main region's blocks whole, within the
140
+ * read's budget, and how the source lays them out (AGL-4106) over the blocks
141
+ * kept, its icons' drawings dropped first when it runs past its own budget.
142
+ */ export function siteImportPageRead(page, at) {
139
143
  const main = page.blocks.filter((block)=>block.region === 'main');
140
144
  const capped = aiSiteImportCapBlocks(main, AI_SITE_IMPORT_READ_MAX_CHARS);
145
+ let layout = page.layout ? siteImportLayoutWithin(page.layout, capped.blocks.length) : null;
146
+ if (layout && siteImportLayoutChars(layout) > AI_SITE_IMPORT_LAYOUT_MAX_CHARS) layout = siteImportLayoutWithoutDrawings(layout);
147
+ if (layout && siteImportLayoutChars(layout) > AI_SITE_IMPORT_LAYOUT_MAX_CHARS) layout = null;
141
148
  return _extends({
142
149
  at,
143
150
  title: siteImportPageTitle(page),
@@ -153,8 +160,21 @@ const wordsIn = (text)=>{
153
160
  blocks: main
154
161
  }),
155
162
  forms: page.forms.kept
163
+ }, layout ? {
164
+ layout
165
+ } : {}, {
166
+ // The source's own title and description, for the page's listing (AGL-4107).
167
+ meta: {
168
+ title: siteImportMetaText(page.title, SITE_IMPORT_META_TITLE_MAX_CHARS),
169
+ description: siteImportMetaText(page.description, SITE_IMPORT_META_DESCRIPTION_MAX_CHARS)
170
+ }
156
171
  });
157
172
  }
173
+ /** The most characters of a source's title and description a read keeps: far past what any listing shows. */ export const SITE_IMPORT_META_TITLE_MAX_CHARS = 300;
174
+ export const SITE_IMPORT_META_DESCRIPTION_MAX_CHARS = 1000;
175
+ /** A title or a description as the source writes it, one line, neutralized; a longer one is kept to its ceiling. */ function siteImportMetaText(text, max) {
176
+ return neutralizeUntrustedText(String(text != null ? text : '')).replace(/\s+/g, ' ').trim().slice(0, max);
177
+ }
158
178
  /** The site's own name: its head's, its structured data's, else its home page's title before a separator. */ export function siteImportSiteName(home) {
159
179
  var _home_title;
160
180
  var _home_organization;
@@ -252,6 +272,8 @@ const SOCIAL_HOSTS = /(?:^|\.)(?:twitter\.com|x\.com|facebook\.com|instagram\.co
252
272
  return status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(status) ? 'blocked' : 'unreadable';
253
273
  case 'not-reached':
254
274
  return 'not-reached';
275
+ case 'not-html':
276
+ return 'file';
255
277
  default:
256
278
  return 'unreadable';
257
279
  }