@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.
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
@@ -54,8 +54,10 @@ import { aiCreditRangeOrdered, aiCreditRangeText, AI_MEASURED_IMPORT_CREDITS } f
54
54
  /**
55
55
  * The most pages one import reads, whatever the plan allows: an unlimited
56
56
  * plan is still one crawl's worth, and the crawl's own time budget may stop
57
- * sooner (the inventory then says so).
58
- */ export const AI_SITE_IMPORT_MAX_PAGES = 200;
57
+ * sooner (the inventory then says so). Only pages count: a file a link
58
+ * points at is never fetched as one (AGL-4106), so aglyn.com's 209 pages fit
59
+ * with room where 136 press-kit files once took 136 of 200.
60
+ */ export const AI_SITE_IMPORT_MAX_PAGES = 400;
59
61
  /** The fewest pages under one address prefix that make it a collection. */ export const AI_SITE_IMPORT_COLLECTION_MIN = 3;
60
62
  /** The most photos brought in from one page. */ export const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8;
61
63
  /**
@@ -66,15 +68,19 @@ import { aiCreditRangeOrdered, aiCreditRangeText, AI_MEASURED_IMPORT_CREDITS } f
66
68
  /** The most sections one imported page is planned with: one for every second-level heading (AGL-4083). */ export const AI_SITE_IMPORT_MAX_SECTIONS = 24;
67
69
  /** The most blocks of one page a wave's read keeps. */ export const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2000;
68
70
  /** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */ export const AI_SITE_IMPORT_READ_MAX_CHARS = 120000;
71
+ /** The most characters one page's layout keeps on the job document (AGL-4106): its icons' drawings go first. */ export const AI_SITE_IMPORT_LAYOUT_MAX_CHARS = 48000;
69
72
  /**
70
- * The most characters of blocks the reads of one import hold at once: they
73
+ * The most characters of blocks (and their layouts, AGL-4106) the reads of one import hold at once: they
71
74
  * ride the job document, which Firestore caps at 1 MiB, beside the
72
75
  * inventory and the ledger. A wave's later page past it keeps what fits and
73
76
  * says it was cut.
74
77
  */ export const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400000;
75
78
  /**
76
- * A page with more words of its own than this is copied as it is, never
77
- * arranged (AGL-4083): a policy, an article, a long story.
79
+ * A collection's page longer than this is prose: a policy, an article, a
80
+ * long story (AGL-4083). A collection's page is copied as it is whatever its
81
+ * length, and since AGL-4106 a main page is arranged whatever its length:
82
+ * `/product` at 1,072 words is laid out as its source lays it out, not copied
83
+ * as one column.
78
84
  */ export const AI_SITE_IMPORT_PROSE_WORDS = 900;
79
85
  /** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */ export const AI_SITE_IMPORT_BLOCKED_STATUSES = new Set([
80
86
  401,
@@ -97,7 +103,8 @@ export const AI_SITE_IMPORT_LEFT_OUT_REASONS = [
97
103
  'blocked',
98
104
  'too-large',
99
105
  'off-site',
100
- 'not-reached'
106
+ 'not-reached',
107
+ 'file'
101
108
  ];
102
109
  /** The reasons that mean the page was there and AglynBot could not read it: the "could not be read" of what was found. */ export const AI_SITE_IMPORT_UNREAD_REASONS = [
103
110
  'unreadable',
@@ -105,6 +112,166 @@ export const AI_SITE_IMPORT_LEFT_OUT_REASONS = [
105
112
  'too-large',
106
113
  'not-reached'
107
114
  ];
115
+ export const SITE_IMPORT_GROUP_KINDS = [
116
+ 'cards',
117
+ 'steps',
118
+ 'stats',
119
+ 'faq',
120
+ 'quotes',
121
+ 'logos',
122
+ 'pricing',
123
+ 'list',
124
+ 'text'
125
+ ];
126
+ /** Every line a layout's nodes place, in order. */ export function siteImportLayoutLines(nodes) {
127
+ const out = [];
128
+ const visit = (list)=>{
129
+ for (const node of list){
130
+ switch(node.kind){
131
+ case 'line':
132
+ case 'picture':
133
+ out.push(node.line);
134
+ break;
135
+ case 'row':
136
+ for (const cell of node.cells)visit(cell.nodes);
137
+ break;
138
+ case 'group':
139
+ for (const item of node.items)out.push(...item.lines);
140
+ break;
141
+ case 'table':
142
+ for (const row of node.rows)for (const cell of row.cells)out.push(...cell.lines);
143
+ break;
144
+ case 'tabs':
145
+ for (const tab of node.tabs){
146
+ if (tab.label !== null) out.push(tab.label);
147
+ visit(tab.panel);
148
+ }
149
+ break;
150
+ default:
151
+ break;
152
+ }
153
+ }
154
+ };
155
+ visit(nodes);
156
+ return out;
157
+ }
158
+ /** How many characters a layout weighs on the job document. */ export function siteImportLayoutChars(layout) {
159
+ return layout ? JSON.stringify(layout).length : 0;
160
+ }
161
+ /**
162
+ * A layout over only the first `count` lines (a read the wave's budget cut
163
+ * short): references past the cut are dropped, and what is left empty goes.
164
+ */ export function siteImportLayoutWithin(layout, count) {
165
+ const keep = (ref)=>ref >= 0 && ref < count;
166
+ const sections = layout.sections.map((section)=>_extends({}, section, {
167
+ nodes: siteImportFilterNodes(section.nodes, keep),
168
+ lines: section.lines.filter(keep)
169
+ })).filter((section)=>section.lines.length);
170
+ return _extends({}, layout, {
171
+ sections
172
+ });
173
+ }
174
+ /** Nodes with only the lines `keep` keeps; what is left empty goes. */ export function siteImportFilterNodes(list, keep) {
175
+ const nodes = (list)=>list.flatMap((node)=>{
176
+ switch(node.kind){
177
+ case 'line':
178
+ case 'picture':
179
+ return keep(node.line) ? [
180
+ node
181
+ ] : [];
182
+ case 'icon':
183
+ case 'form':
184
+ return [
185
+ node
186
+ ];
187
+ case 'row':
188
+ {
189
+ const cells = node.cells.map((cell)=>({
190
+ nodes: nodes(cell.nodes)
191
+ }));
192
+ return cells.some((cell)=>cell.nodes.length) ? [
193
+ _extends({}, node, {
194
+ cells
195
+ })
196
+ ] : [];
197
+ }
198
+ case 'group':
199
+ {
200
+ const items = node.items.map((item)=>trimItem(item, keep)).filter((item)=>item.lines.length || item.icon !== undefined);
201
+ return items.length ? [
202
+ _extends({}, node, {
203
+ items
204
+ })
205
+ ] : [];
206
+ }
207
+ case 'table':
208
+ {
209
+ const rows = node.rows.map((row)=>({
210
+ cells: row.cells.map((cell)=>({
211
+ lines: cell.lines.filter(keep)
212
+ }))
213
+ })).filter((row)=>row.cells.some((cell)=>cell.lines.length));
214
+ return rows.length ? [
215
+ _extends({}, node, {
216
+ rows
217
+ })
218
+ ] : [];
219
+ }
220
+ case 'tabs':
221
+ {
222
+ const tabs = node.tabs.map((tab)=>_extends({}, tab, {
223
+ label: tab.label !== null && keep(tab.label) ? tab.label : null,
224
+ panel: nodes(tab.panel)
225
+ }));
226
+ return tabs.some((tab)=>tab.label !== null || tab.panel.length) ? [
227
+ _extends({}, node, {
228
+ tabs
229
+ })
230
+ ] : [];
231
+ }
232
+ default:
233
+ return [];
234
+ }
235
+ });
236
+ return nodes(list);
237
+ }
238
+ function trimItem(item, keep) {
239
+ const out = _extends({}, item, {
240
+ lines: item.lines.filter(keep)
241
+ });
242
+ for (const key of [
243
+ 'title',
244
+ 'figure',
245
+ 'image'
246
+ ])if (out[key] !== undefined && !keep(out[key])) delete out[key];
247
+ for (const key of [
248
+ 'text',
249
+ 'list',
250
+ 'link'
251
+ ]){
252
+ var _out_key;
253
+ const list = (_out_key = out[key]) == null ? void 0 : _out_key.filter(keep);
254
+ if (list == null ? void 0 : list.length) out[key] = list;
255
+ else delete out[key];
256
+ }
257
+ return out;
258
+ }
259
+ /** A layout with its icons' drawings dropped, for a page that must be lighter. */ export function siteImportLayoutWithoutDrawings(layout) {
260
+ return _extends({}, layout, {
261
+ icons: layout.icons.map((icon)=>icon.svg ? {
262
+ viewBox: icon.viewBox
263
+ } : icon)
264
+ });
265
+ }
266
+ /**
267
+ * Whether the site's header and footer are built from the source's own menu
268
+ * (AGL-4108), with no model: the home page's menu was read. Then the frame
269
+ * costs nothing and is quoted at nothing.
270
+ */ export function aiSiteImportFrameIsRead(state) {
271
+ var _state_home;
272
+ const nav = state == null ? void 0 : (_state_home = state.home) == null ? void 0 : _state_home.nav;
273
+ return !!nav && (nav.items.length > 0 || nav.ctas.length > 0);
274
+ }
108
275
  const pathOfUrl = (url)=>{
109
276
  try {
110
277
  return new URL(url).pathname.replace(/\/+$/, '') || '/';
@@ -395,7 +562,7 @@ const segmentsOf = (path)=>String(path != null ? path : '').split('/').filter(Bo
395
562
  * The site's inventory cut into waves, every page left out said with its
396
563
  * reason. Pure: the step reads the site, this decides what comes in.
397
564
  */ export function aiSiteImportInventory(input) {
398
- var _input_wavePages, _leftOutCount_unreadable, _input_verdict, _input_reviewId;
565
+ var _input_wavePages, _leftOutCount_unreadable, _input_verdict;
399
566
  var _waves_;
400
567
  const wavePages = Math.max(1, Math.floor((_input_wavePages = input.wavePages) != null ? _input_wavePages : AI_SITE_IMPORT_WAVE_PAGES));
401
568
  const limit = input.pageLimit === null || !Number.isFinite(input.pageLimit) ? null : Math.max(0, Math.floor(input.pageLimit));
@@ -427,7 +594,6 @@ const segmentsOf = (path)=>String(path != null ? path : '').split('/').filter(Bo
427
594
  // Home first: a site read without its home page plans nothing at all.
428
595
  const verdict = (_input_verdict = input.verdict) != null ? _input_verdict : inventoryVerdict(input);
429
596
  if (aiSiteImportReadStops(verdict)) {
430
- var _input_reviewId1;
431
597
  return {
432
598
  domain: input.domain,
433
599
  siteName: clipLabel(input.siteName) || input.domain,
@@ -443,7 +609,6 @@ const segmentsOf = (path)=>String(path != null ? path : '').split('/').filter(Bo
443
609
  leftOut,
444
610
  leftOutCount,
445
611
  frame: null,
446
- reviewId: (_input_reviewId1 = input.reviewId) != null ? _input_reviewId1 : null,
447
612
  verdict
448
613
  };
449
614
  }
@@ -529,7 +694,6 @@ const segmentsOf = (path)=>String(path != null ? path : '').split('/').filter(Bo
529
694
  frame: ((_waves_ = waves[0]) == null ? void 0 : _waves_.frame) ? {
530
695
  draftId: input.mintId()
531
696
  } : null,
532
- reviewId: (_input_reviewId = input.reviewId) != null ? _input_reviewId : null,
533
697
  verdict
534
698
  };
535
699
  }
@@ -665,8 +829,21 @@ const withWave = (state, index, patch)=>_extends({}, state, {
665
829
  }
666
830
  const readChars = (read)=>{
667
831
  var _ref;
668
- return ((_ref = read == null ? void 0 : read.blocks) != null ? _ref : []).reduce((total, block)=>total + aiSiteImportBlockChars(block), 0);
832
+ return ((_ref = read == null ? void 0 : read.blocks) != null ? _ref : []).reduce((total, block)=>total + aiSiteImportBlockChars(block), 0) + siteImportLayoutChars(read == null ? void 0 : read.layout);
669
833
  };
834
+ /**
835
+ * A page's layout in the room the wave has left after its blocks (AGL-4106):
836
+ * cut to the blocks kept, its drawings dropped when it would not fit with
837
+ * them, and left off when it would not fit at all (the page is then laid out
838
+ * from its lines alone).
839
+ */ function layoutWithin(layout, blocks, left) {
840
+ if (!layout) return null;
841
+ const room = Math.min(left, AI_SITE_IMPORT_LAYOUT_MAX_CHARS);
842
+ const cut = siteImportLayoutWithin(layout, blocks);
843
+ if (siteImportLayoutChars(cut) <= room) return cut;
844
+ const light = siteImportLayoutWithoutDrawings(cut);
845
+ return siteImportLayoutChars(light) <= room ? light : null;
846
+ }
670
847
  /**
671
848
  * More of a running wave's pages, read now. Their blocks share
672
849
  * `AI_SITE_IMPORT_WAVE_READ_MAX_CHARS` with every read still held: a page
@@ -693,6 +870,13 @@ const readChars = (read)=>{
693
870
  }, capped.cut || read.cut ? {
694
871
  cut: true
695
872
  } : {});
873
+ if (read.layout) {
874
+ const layout = layoutWithin(read.layout, capped.blocks.length, Math.max(0, AI_SITE_IMPORT_WAVE_READ_MAX_CHARS - held));
875
+ held += siteImportLayoutChars(layout);
876
+ kept = _extends({}, kept, {
877
+ layout
878
+ });
879
+ }
696
880
  }
697
881
  next = withPage(next, slot, {
698
882
  read: kept,
@@ -713,7 +897,8 @@ const readChars = (read)=>{
713
897
  return withPage(state, slot, {
714
898
  read: _extends({}, page.read, {
715
899
  text: '',
716
- blocks: []
900
+ blocks: [],
901
+ layout: null
717
902
  })
718
903
  });
719
904
  }
@@ -730,6 +915,26 @@ const readChars = (read)=>{
730
915
  for (const slot of (_ref = (_state_waves_find = state.waves.find((wave)=>wave.index === index)) == null ? void 0 : _state_waves_find.slots) != null ? _ref : [])next = aiSiteImportPageSettled(next, slot);
731
916
  return next;
732
917
  }
918
+ /**
919
+ * The state of an import a person canceled (AGL-4107): every wave settled. A
920
+ * wave that ran, or was waiting to, is stopped; one already in, for review or
921
+ * done, is in. On 10/29 a canceled import still showed its second wave as
922
+ * running, with nothing running it.
923
+ */ export function aiSiteImportCanceled(state, at) {
924
+ return _extends({}, state, {
925
+ autoContinue: false,
926
+ waves: state.waves.map((wave)=>{
927
+ if (wave.status === 'running' || wave.status === 'waiting') return _extends({}, wave, {
928
+ status: 'stopped',
929
+ finishedAt: wave.status === 'running' ? at : wave.finishedAt
930
+ });
931
+ if (wave.status === 'review') return _extends({}, wave, {
932
+ status: 'done'
933
+ });
934
+ return wave;
935
+ })
936
+ });
937
+ }
733
938
  /** The state as the console reads it: every page's read text left off. */ export function aiSiteImportWire(state) {
734
939
  if (!state) return null;
735
940
  return _extends({}, state, {
@@ -738,7 +943,8 @@ const readChars = (read)=>{
738
943
  text: '',
739
944
  images: [],
740
945
  blocks: [],
741
- forms: []
946
+ forms: [],
947
+ layout: null
742
948
  })
743
949
  }) : page)
744
950
  });
@@ -747,11 +953,11 @@ const readChars = (read)=>{
747
953
  * Credits
748
954
  *-----------------------------------------*/ /**
749
955
  * How a page is built (AGL-4083), decided by code: every page of a collection
750
- * (a blog, the legal pages, news) and any page longer than
751
- * `AI_SITE_IMPORT_PROSE_WORDS` is copied as it is; the main pages are
752
- * arranged by Aglyn AI, their words kept as written.
956
+ * (a blog, the legal pages, news) is copied as it is; every main page is
957
+ * arranged by Aglyn AI whatever its length (AGL-4106), its words kept as
958
+ * written. Both are laid out from the source's own layout.
753
959
  */ export function aiSiteImportPageBuild(page) {
754
- return page.group !== 'main' || page.words > AI_SITE_IMPORT_PROSE_WORDS ? 'copied' : 'arranged';
960
+ return page.group !== 'main' ? 'copied' : 'arranged';
755
961
  }
756
962
  const whole = (value)=>Math.max(0, Math.floor(Number.isFinite(value) ? value : 0));
757
963
  /**
@@ -773,11 +979,12 @@ const whole = (value)=>Math.max(0, Math.floor(Number.isFinite(value) ? value : 0
773
979
  }
774
980
  /** What a wave still owes, by how its pages are built, and whether its header and footer are owed. */ export function aiSiteImportWaveOwed(state, wave, items) {
775
981
  const owed = (slot)=>items ? aiSiteImportItemOpen(items, slot) : true;
982
+ // A header and footer drawn from the source's own menu ask no model (AGL-4108), so are owed nothing.
776
983
  const counts = {
777
984
  pages: 0,
778
985
  arranged: 0,
779
986
  copied: 0,
780
- frame: wave.frame && !!state.frame && owed(AI_SITE_IMPORT_FRAME_SLOT)
987
+ frame: wave.frame && !!state.frame && !aiSiteImportFrameIsRead(state) && owed(AI_SITE_IMPORT_FRAME_SLOT)
781
988
  };
782
989
  for (const slot of wave.slots){
783
990
  const page = state.pages.find((entry)=>entry.slot === slot);
@@ -834,7 +1041,8 @@ const whole = (value)=>Math.max(0, Math.floor(Number.isFinite(value) ? value : 0
834
1041
  blocked: 'The site’s bot protection or firewall turned AglynBot away. Allow AglynBot in your site’s bot or firewall settings, then bring the site in again.',
835
1042
  'too-large': 'Larger than a page can be to bring in.',
836
1043
  'off-site': 'It sends visitors to another website.',
837
- 'not-reached': 'Not reached: the read stopped before it got to this page.'
1044
+ 'not-reached': 'Not reached: the read stopped before it got to this page.',
1045
+ file: 'A file, not a page.'
838
1046
  };
839
1047
  /** "3 pages" / "1 page". */ export function aiSiteImportPagesText(count) {
840
1048
  return `${count.toLocaleString('en-US')} ${count === 1 ? 'page' : 'pages'}`;