@aglyn/plugins-ai 1.0.0 → 1.2.0

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 (46) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  3. package/src/lib/components/ai-site-start-card.component.js +40 -13
  4. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  5. package/src/lib/components/ai-site-start-steps.component.d.ts +17 -1
  6. package/src/lib/components/ai-site-start-steps.component.js +30 -7
  7. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  8. package/src/lib/components/assist-panel.component.js +8 -12
  9. package/src/lib/components/assist-panel.component.js.map +1 -1
  10. package/src/lib/jobs/ai-job-drafts.d.ts +3 -0
  11. package/src/lib/jobs/ai-job-drafts.js +5 -0
  12. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  13. package/src/lib/jobs/ai-job-layout-language.d.ts +10 -0
  14. package/src/lib/jobs/ai-job-layout-language.js +38 -2
  15. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  16. package/src/lib/jobs/ai-job-layout-step.js +3 -3
  17. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  18. package/src/lib/jobs/ai-site-import-compile.js +15 -7
  19. package/src/lib/jobs/ai-site-import-compile.js.map +1 -1
  20. package/src/lib/layout-language/ai-layout-compiler.js +39 -1
  21. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  22. package/src/lib/layout-language/ai-layout-frame.d.ts +6 -0
  23. package/src/lib/layout-language/ai-layout-frame.js +85 -30
  24. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  25. package/src/lib/layout-language/ai-layout-language.d.ts +5 -0
  26. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  27. package/src/lib/model/ai-site-import-fidelity.d.ts +175 -0
  28. package/src/lib/model/ai-site-import-fidelity.js +941 -0
  29. package/src/lib/model/ai-site-import-fidelity.js.map +1 -0
  30. package/src/lib/model/ai-site-start.d.ts +27 -0
  31. package/src/lib/model/ai-site-start.js +41 -0
  32. package/src/lib/model/ai-site-start.js.map +1 -1
  33. package/src/lib/model/assist-page-change.d.ts +26 -6
  34. package/src/lib/model/assist-page-change.js +32 -10
  35. package/src/lib/model/assist-page-change.js.map +1 -1
  36. package/src/lib/runtime/ai-node-tree.js +18 -2
  37. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  38. package/src/lib/server/assist-chat.d.ts +5 -0
  39. package/src/lib/server/assist-chat.js +11 -2
  40. package/src/lib/server/assist-chat.js.map +1 -1
  41. package/src/lib/server/assist-page-change.d.ts +12 -0
  42. package/src/lib/server/assist-page-change.js +23 -1
  43. package/src/lib/server/assist-page-change.js.map +1 -1
  44. package/src/lib/site-import/fixtures/site-import-fidelity-replay.d.ts +62 -0
  45. package/src/lib/site-import/fixtures/site-import-fidelity-replay.js +374 -0
  46. package/src/lib/site-import/fixtures/site-import-fidelity-replay.js.map +1 -0
@@ -0,0 +1,941 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /*==========================================
17
+ * A PAGE BROUGHT IN, SCORED AGAINST ITS SOURCE (AGL-4109).
18
+ *
19
+ * On 10/29 an import of aglyn.com shipped with every section flattened to one
20
+ * column of lines and a header of page titles, and nothing measured it: the
21
+ * specs checked that words came through, never how the page was laid out.
22
+ * This scores a built page against what the reader read of its source
23
+ * (`SiteImportLayout`, AGL-4106), with no model and no browser, over the
24
+ * page's nodes as the compiler draws them or as Firestore stores them:
25
+ *
26
+ * - **The words**: every line of the source exactly once, as written, and
27
+ * none of the page's own. A score of words is 1 or the page is wrong.
28
+ * - **The structure**: its sections and their bands; its rows of columns
29
+ * side by side; its groups by kind, each item its own element with its
30
+ * parts (an FAQ's question on an accordion, a card's link on the card);
31
+ * its tables, tab strips, pictures, icons, buttons as the source draws
32
+ * them, headings and forms.
33
+ * - **The frame** (`siteImportFrameFidelity`): the header's top-level items
34
+ * in order, each menu's entries with their lines and icons, the buttons,
35
+ * the phone menu holding all of it, labels on one line; the footer's
36
+ * columns, links, profiles and facts.
37
+ *
38
+ * Every measure is anchored to the source's own words, never to how the
39
+ * compiler names its elements: each line of the source is found on the
40
+ * built page, and the structure is asked of the elements holding them (are
41
+ * a group's thirteen items thirteen elements, or one stack of lines?). So
42
+ * the same score reads a page compiled in a spec and a page stored on a live
43
+ * site. The score is the lowest ratio of the structure; words are held apart
44
+ * at exactly 1.
45
+ *=========================================*/ import { _ as _extends } from "@swc/helpers/_/_extends";
46
+ /** The lowest score of structure a page brought in ships at (AGL-4109); words are held at exactly 1. */ export const SITE_IMPORT_FIDELITY_THRESHOLD = 0.9;
47
+ /*------------------------------------------
48
+ * Words and lines
49
+ *-----------------------------------------*/ const clean = (text)=>typeof text === 'string' ? text.replace(/\s+/g, ' ').trim() : '';
50
+ const WORD = RegExp("[\\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}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "gu");
51
+ /** A line's words, in order. */ const wordsOf = (text)=>{
52
+ var _text_match;
53
+ return (_text_match = text.match(WORD)) != null ? _text_match : [];
54
+ };
55
+ /**
56
+ * A line's words as one string, for comparing two lines whatever their
57
+ * spacing and marks; a line of marks alone (a table's "✓", a "—") as it is.
58
+ */ const wordLine = (text)=>wordsOf(text).join(' ') || clean(text);
59
+ /** Whether a block is a line of the page: a link inside a sentence is in its sentence. */ function isLine(block) {
60
+ if (!block) return false;
61
+ if (block.kind === 'image') return !!block.url;
62
+ if (block.kind === 'link') return !block.inline && !!clean(block.text);
63
+ return !!clean(block.text);
64
+ }
65
+ /** A cell of the Table element's grid, its pipes unescaped. */ function tableCells(rows) {
66
+ return rows.split('\n').flatMap((line)=>line.replace(/^\s*\|/, '').replace(/\|\s*$/, '').split(/(?<!\\)\|/).map((cell)=>clean(cell.replace(/\\\|/g, '|')))).filter(Boolean);
67
+ }
68
+ /** The words a node shows a visitor: its text, its list lines, its tab labels, its table's cells. */ function shownTexts(node) {
69
+ var _node_props;
70
+ if (!node) return [];
71
+ const props = (_node_props = node.props) != null ? _node_props : {};
72
+ const out = [];
73
+ for (const key of [
74
+ 'children',
75
+ 'primary',
76
+ 'secondary'
77
+ ]){
78
+ const value = clean(props[key]);
79
+ if (value) out.push(value);
80
+ }
81
+ if (node.componentId === 'muiTabs' && typeof props['labels'] === 'string') out.push(...props['labels'].split('\n').map(clean).filter(Boolean));
82
+ if (node.componentId === 'dataTable' && typeof props['rows'] === 'string') out.push(...tableCells(props['rows']));
83
+ return out;
84
+ }
85
+ /** Where a node sends a visitor: a page, an address, a section of the page. */ function hasDestination(node) {
86
+ var _node_props, _ref;
87
+ var _node_interactions;
88
+ if (!node) return false;
89
+ const props = (_node_props = node.props) != null ? _node_props : {};
90
+ return !!clean(props['screenId']) || !!clean(props['href']) || !!clean(props['to']) || ((_ref = (_node_interactions = node.interactions) == null ? void 0 : _node_interactions.length) != null ? _ref : 0) > 0;
91
+ }
92
+ const BUTTON_VARIANT = {
93
+ primary: 'contained',
94
+ secondary: 'outlined',
95
+ quiet: 'text'
96
+ };
97
+ /** A section's band as the compiler draws it: a dark scheme, the paper ground, the brand fill, else plain. */ export function siteImportFidelityBandOf(node) {
98
+ var _node_props, _node_sx;
99
+ if ((node == null ? void 0 : (_node_props = node.props) == null ? void 0 : _node_props['colorScheme']) === 'dark') return 'dark';
100
+ const ground = node == null ? void 0 : (_node_sx = node.sx) == null ? void 0 : _node_sx['bgcolor'];
101
+ if (ground === 'background.paper') return 'soft';
102
+ if (ground === 'primary.main') return 'brand';
103
+ return 'plain';
104
+ }
105
+ /** The node a stored page or layout hangs from: the canvas root, else the one with no parent. */ export function siteImportFidelityTreeOf(nodes) {
106
+ var _ref, _ids_find;
107
+ const ids = Object.keys(nodes);
108
+ const rootId = nodes['_@_'] ? '_@_' : (_ref = (_ids_find = ids.find((id)=>{
109
+ var _nodes_id;
110
+ return !((_nodes_id = nodes[id]) == null ? void 0 : _nodes_id.parentId);
111
+ })) != null ? _ids_find : ids[0]) != null ? _ref : '_@_';
112
+ return {
113
+ rootId,
114
+ nodes
115
+ };
116
+ }
117
+ /** A built tree in document order: each node's span, its parent, and the words each shows. */ let Built = class Built {
118
+ node(id) {
119
+ return this.tree.nodes[id];
120
+ }
121
+ /** Whether `a` is `b` or holds it. */ holds(a, b) {
122
+ const start = this.enter.get(a);
123
+ const end = this.exit.get(a);
124
+ const point = this.enter.get(b);
125
+ return start !== undefined && end !== undefined && point !== undefined && point >= start && point <= end;
126
+ }
127
+ /** The nodes under one, itself included, in document order. */ under(id) {
128
+ const start = this.enter.get(id);
129
+ const end = this.exit.get(id);
130
+ return start === undefined || end === undefined ? [] : this.order.slice(start, end + 1);
131
+ }
132
+ children(id) {
133
+ var _ref;
134
+ var _this_tree_nodes_id;
135
+ return ((_ref = (_this_tree_nodes_id = this.tree.nodes[id]) == null ? void 0 : _this_tree_nodes_id.nodes) != null ? _ref : []).filter((child)=>this.enter.has(child));
136
+ }
137
+ /** The deepest node holding every one of `ids`. */ common(ids) {
138
+ if (!ids.length) return null;
139
+ for(let at = ids[0]; at; at = this.parent.get(at)){
140
+ if (ids.every((id)=>this.holds(at, id))) return at;
141
+ }
142
+ return null;
143
+ }
144
+ /** Whether any node under `id` (itself included) is one `test` takes. */ has(id, test) {
145
+ return this.under(id).some((at)=>{
146
+ const node = this.tree.nodes[at];
147
+ return !!node && test(node);
148
+ });
149
+ }
150
+ constructor(tree){
151
+ this.tree = tree;
152
+ this.order = [];
153
+ this.enter = new Map();
154
+ this.exit = new Map();
155
+ this.parent = new Map();
156
+ this.pieces = [];
157
+ const seen = new Set();
158
+ const visit = (id, parent)=>{
159
+ var _ref;
160
+ var _tree_nodes_id;
161
+ if (seen.has(id) || !tree.nodes[id]) return;
162
+ seen.add(id);
163
+ if (parent) this.parent.set(id, parent);
164
+ const at = this.order.length;
165
+ this.enter.set(id, at);
166
+ this.order.push(id);
167
+ for (const text of shownTexts(tree.nodes[id]))this.pieces.push({
168
+ node: id,
169
+ text,
170
+ key: wordLine(text),
171
+ at
172
+ });
173
+ for (const child of (_ref = (_tree_nodes_id = tree.nodes[id]) == null ? void 0 : _tree_nodes_id.nodes) != null ? _ref : [])visit(child, id);
174
+ this.exit.set(id, this.order.length - 1);
175
+ };
176
+ visit(tree.rootId, null);
177
+ }
178
+ };
179
+ const ratioKept = (source, built)=>({
180
+ source,
181
+ built,
182
+ ratio: source > 0 ? Math.min(1, built / source) : 1
183
+ });
184
+ const ratioBoth = (source, built)=>({
185
+ source,
186
+ built,
187
+ ratio: Math.max(source, built) > 0 ? Math.min(source, built) / Math.max(source, built) : 1
188
+ });
189
+ /*------------------------------------------
190
+ * The page
191
+ *-----------------------------------------*/ /**
192
+ * Each line of the source found on the built page (AGL-4109): the element
193
+ * showing its words, or the picture the library copy of it is. A line is
194
+ * looked for in its own section, nearest after the line before it; an
195
+ * element holds one line, or the run of lines a build joins into one (a
196
+ * list item's words, a quote's).
197
+ */ let Placement = class Placement {
198
+ /** The piece showing a line: its words exactly, else a part of them (a long paragraph split), else the run it was joined into. */ find(pieces, text, cursor, previous) {
199
+ const key = wordLine(text);
200
+ if (!key) return null;
201
+ const free = (piece)=>{
202
+ const owner = this.claimedBy.get(piece);
203
+ // A piece holds one line, or the lines a build joined: the one before this one.
204
+ return owner === undefined || owner === previous;
205
+ };
206
+ // The nearest to the line placed before it: a build keeps a section's lines in their order, near enough.
207
+ const pick = (list)=>list.reduce((best, piece)=>{
208
+ if (!best) return piece;
209
+ const gap = Math.abs(piece.at - cursor);
210
+ const bestGap = Math.abs(best.at - cursor);
211
+ return gap < bestGap || gap === bestGap && piece.at >= cursor && best.at < cursor ? piece : best;
212
+ }, null);
213
+ const exact = pieces.filter((piece)=>piece.key === key && this.claimedBy.get(piece) === undefined);
214
+ if (exact.length) return pick(exact);
215
+ const padded = ` ${key} `;
216
+ const part = pieces.filter((piece)=>free(piece) && piece.key && (` ${piece.key} `.includes(padded) || padded.includes(` ${piece.key} `)));
217
+ return pick(part);
218
+ }
219
+ /** The nodes a list of lines is shown by, and whether every one of them was found. */ nodesOf(refs) {
220
+ var _this_source_photos;
221
+ const nodes = [];
222
+ let whole = true;
223
+ let measured = false;
224
+ const pictures = !!((_this_source_photos = this.source.photos) == null ? void 0 : _this_source_photos.length);
225
+ for (const ref of refs){
226
+ const block = this.source.blocks[ref];
227
+ if (!isLine(block)) continue;
228
+ if (block.kind === 'image' && !pictures) continue;
229
+ measured = true;
230
+ const node = this.of.get(ref);
231
+ if (node) nodes.push(node);
232
+ else whole = false;
233
+ }
234
+ return {
235
+ nodes,
236
+ whole,
237
+ measured
238
+ };
239
+ }
240
+ constructor(built, source, sectionRoots){
241
+ var _source_photos;
242
+ this.built = built;
243
+ this.source = source;
244
+ /** Line → the node showing it. */ this.of = new Map();
245
+ this.claimedBy = new Map();
246
+ this.imageClaimed = new Set();
247
+ const photoOf = new Map();
248
+ for (const photo of (_source_photos = source.photos) != null ? _source_photos : [])if (photo.from) photoOf.set(photo.from, photo.src);
249
+ source.layout.sections.forEach((section, index)=>{
250
+ var _sectionRoots_index;
251
+ const root = (_sectionRoots_index = sectionRoots[index]) != null ? _sectionRoots_index : built.tree.rootId;
252
+ const scope = new Set(built.under(root));
253
+ const pieces = built.pieces.filter((piece)=>scope.has(piece.node));
254
+ const images = built.under(root).filter((id)=>{
255
+ var _built_node;
256
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'image';
257
+ });
258
+ let cursor = -1;
259
+ let previous = null;
260
+ for (const ref of section.lines){
261
+ const block = source.blocks[ref];
262
+ if (!isLine(block)) continue;
263
+ if (block.kind === 'image') {
264
+ const src = photoOf.get(block.url);
265
+ const found = src ? images.find((id)=>{
266
+ var _built_node_props, _built_node;
267
+ return !this.imageClaimed.has(id) && ((_built_node = built.node(id)) == null ? void 0 : (_built_node_props = _built_node.props) == null ? void 0 : _built_node_props['src']) === src;
268
+ }) : undefined;
269
+ if (found) {
270
+ this.imageClaimed.add(found);
271
+ this.of.set(ref, found);
272
+ }
273
+ previous = ref;
274
+ continue;
275
+ }
276
+ const piece = this.find(pieces, clean(block.text), cursor, previous);
277
+ if (piece) {
278
+ this.claimedBy.set(piece, ref);
279
+ this.of.set(ref, piece.node);
280
+ cursor = piece.at;
281
+ }
282
+ previous = ref;
283
+ }
284
+ });
285
+ }
286
+ };
287
+ /**
288
+ * Units found on the page, each as the element of its own (AGL-4109): the
289
+ * highest element holding its lines and none of the other units' lines. A
290
+ * unit whose lines are not held apart from another's (thirteen cards' titles
291
+ * in one stack under their thirteen lines) has no element of its own.
292
+ */ function unitsOn(built, placement, parts, stop) {
293
+ const found = parts.map((refs)=>placement.nodesOf(refs));
294
+ return found.map((part, index)=>{
295
+ const others = found.flatMap((other, at)=>at === index ? [] : other.nodes);
296
+ const anchor = built.common(part.nodes);
297
+ if (!anchor || others.some((node)=>built.holds(anchor, node))) return _extends({}, part, {
298
+ element: null
299
+ });
300
+ let element = anchor;
301
+ for(let up = built.parent.get(element); up && up !== stop && !others.some((node)=>built.holds(up, node)); up = built.parent.get(up)){
302
+ var _built_node;
303
+ element = up;
304
+ if (((_built_node = built.node(up)) == null ? void 0 : _built_node.componentId) === 'section') break;
305
+ }
306
+ return _extends({}, part, {
307
+ element
308
+ });
309
+ });
310
+ }
311
+ /** Whether a node lays its children side by side: a Grid container, or a Stack set in a row. */ function setsSideBySide(node) {
312
+ var _node_props;
313
+ if (!node) return false;
314
+ const props = (_node_props = node.props) != null ? _node_props : {};
315
+ if (node.componentId === 'muiGrid') return props['container'] === true || props['container'] === 'true';
316
+ if (node.componentId === 'muiStack') return props['direction'] === 'row' || typeof props['direction'] === 'string' && /\b(?:sm|md|lg):row\b/.test(props['direction']);
317
+ return false;
318
+ }
319
+ /**
320
+ * Whether a row of the source is a byline: one cell a lone picture, the other
321
+ * a few words (a writer's portrait beside their name and date), which the
322
+ * build draws as the portrait, small, over its words (AGL-4107), not as two
323
+ * columns across the page.
324
+ */ function isByline(source, cells) {
325
+ var _ref;
326
+ var _cells_;
327
+ if (cells.length !== 2) return false;
328
+ const at = cells.findIndex((cell)=>{
329
+ var _cell_nodes_;
330
+ return cell.nodes.length === 1 && ((_cell_nodes_ = cell.nodes[0]) == null ? void 0 : _cell_nodes_.kind) === 'picture';
331
+ });
332
+ if (at === -1) return false;
333
+ const words = (_ref = (_cells_ = cells[1 - at]) == null ? void 0 : _cells_.nodes) != null ? _ref : [];
334
+ if (!words.every((node)=>node.kind === 'line')) return false;
335
+ const count = words.reduce((total, node)=>{
336
+ const block = node.kind === 'line' ? source.blocks[node.line] : undefined;
337
+ return total + (block && block.kind !== 'image' ? wordsOf(clean(block.text)).length : 0);
338
+ }, 0);
339
+ return count > 0 && count <= 12;
340
+ }
341
+ /** Every node of a section's layout, rows, cells and tab panels opened, in order. */ function layoutNodes(nodes) {
342
+ const out = [];
343
+ const visit = (list)=>{
344
+ for (const node of list){
345
+ out.push(node);
346
+ if (node.kind === 'row') for (const cell of node.cells)visit(cell.nodes);
347
+ if (node.kind === 'tabs') for (const tab of node.tabs)visit(tab.panel);
348
+ }
349
+ };
350
+ visit(nodes);
351
+ return out;
352
+ }
353
+ /** Every line a list of layout nodes places, rows, groups, tables and tabs opened. */ function linesOf(nodes) {
354
+ const out = [];
355
+ for (const node of layoutNodes(nodes)){
356
+ if (node.kind === 'line' || node.kind === 'picture') out.push(node.line);
357
+ else if (node.kind === 'group') for (const item of node.items)out.push(...item.lines);
358
+ else if (node.kind === 'table') for (const row of node.rows)for (const cell of row.cells)out.push(...cell.lines);
359
+ else if (node.kind === 'tabs') {
360
+ for (const tab of node.tabs)if (tab.label !== null) out.push(tab.label);
361
+ }
362
+ }
363
+ return out;
364
+ }
365
+ /**
366
+ * A built page scored against its source (AGL-4109): the words, each once;
367
+ * the sections and their bands; the rows of columns; the groups by kind, each
368
+ * item an element of its own with its parts; the tables, tabs, pictures,
369
+ * icons, buttons, headings and forms. The score is the lowest ratio of the
370
+ * structure.
371
+ */ export function siteImportFidelity(source, tree) {
372
+ const built = new Built(tree);
373
+ const sections = source.layout.sections;
374
+ const drawnSections = built.children(tree.rootId);
375
+ // The build draws each of the source's sections as one, in order; a page whose count differs is searched whole.
376
+ const aligned = drawnSections.length === sections.length;
377
+ const roots = sections.map((_, index)=>{
378
+ var _drawnSections_index;
379
+ return aligned ? (_drawnSections_index = drawnSections[index]) != null ? _drawnSections_index : null : null;
380
+ });
381
+ const placement = new Placement(built, source, roots);
382
+ const words = wordsScore(source, built);
383
+ const read = sections.map((section)=>section.band);
384
+ const drawn = drawnSections.map((id)=>siteImportFidelityBandOf(built.node(id)));
385
+ const bandsKept = read.filter((band, index)=>drawn[index] === band).length;
386
+ const rows = {
387
+ source: 0,
388
+ built: 0
389
+ };
390
+ const groups = {};
391
+ const tables = {
392
+ source: 0,
393
+ built: 0
394
+ };
395
+ const tabs = {
396
+ source: 0,
397
+ built: 0
398
+ };
399
+ const buttons = {
400
+ source: 0,
401
+ built: 0,
402
+ unlinked: 0
403
+ };
404
+ const icons = {
405
+ source: 0,
406
+ built: 0,
407
+ lone: 0
408
+ };
409
+ sections.forEach((section, index)=>{
410
+ var _roots_index;
411
+ const root = (_roots_index = roots[index]) != null ? _roots_index : null;
412
+ for (const node of layoutNodes(section.nodes)){
413
+ switch(node.kind){
414
+ case 'icon':
415
+ icons.lone += 1;
416
+ break;
417
+ case 'line':
418
+ {
419
+ var _drawnAs_props;
420
+ if (!node.button) break;
421
+ const block = source.blocks[node.line];
422
+ // A button that goes nowhere a page can send a visitor (a film's play button its script runs) is kept as its words.
423
+ if ((block == null ? void 0 : block.kind) !== 'link' || !/^(?:https?:|mailto:|tel:)/i.test(block.href)) {
424
+ buttons.unlinked += 1;
425
+ break;
426
+ }
427
+ buttons.source += 1;
428
+ const id = placement.of.get(node.line);
429
+ const drawnAs = id ? built.node(id) : undefined;
430
+ if ((drawnAs == null ? void 0 : drawnAs.componentId) === 'muiButton' && ((_drawnAs_props = drawnAs.props) == null ? void 0 : _drawnAs_props['variant']) === BUTTON_VARIANT[node.button] && hasDestination(drawnAs)) buttons.built += 1;
431
+ break;
432
+ }
433
+ case 'row':
434
+ {
435
+ const cells = node.cells.filter((cell)=>linesOf(cell.nodes).length);
436
+ if (cells.length < 2 || isByline(source, cells)) break;
437
+ rows.source += 1;
438
+ const units = unitsOn(built, placement, cells.map((cell)=>linesOf(cell.nodes)), root);
439
+ const parents = new Set(units.map((unit)=>unit.element ? built.parent.get(unit.element) : null));
440
+ const parent = parents.size === 1 ? [
441
+ ...parents
442
+ ][0] : null;
443
+ if (units.every((unit)=>unit.whole && unit.element) && parent && setsSideBySide(built.node(parent))) rows.built += 1;
444
+ break;
445
+ }
446
+ case 'group':
447
+ {
448
+ var _groups, _node_group, _;
449
+ const units = unitsOn(built, placement, node.items.map((item)=>item.lines), root);
450
+ // Each item's own icon and its bullets' marks, in the element that is the item.
451
+ node.items.forEach((item, at)=>{
452
+ var _ref;
453
+ var _item_list, _units_at;
454
+ const wanted = (item.icon !== undefined ? 1 : 0) + (item.listIcon !== undefined ? (_ref = (_item_list = item.list) == null ? void 0 : _item_list.length) != null ? _ref : 0 : 0);
455
+ if (!wanted) return;
456
+ icons.source += wanted;
457
+ const element = (_units_at = units[at]) == null ? void 0 : _units_at.element;
458
+ if (element) icons.built += Math.min(wanted, built.under(element).filter((id)=>{
459
+ var _built_node;
460
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'icon';
461
+ }).length);
462
+ });
463
+ // Lines that only share a shape are drawn as their lines: no group to keep.
464
+ if (node.group === 'text') break;
465
+ const entry = (_ = (_groups = groups)[_node_group = node.group]) != null ? _ : _groups[_node_group] = {
466
+ groups: 0,
467
+ groupsKept: 0,
468
+ items: 0,
469
+ itemsKept: 0
470
+ };
471
+ let kept = 0;
472
+ let measured = 0;
473
+ node.items.forEach((item, at)=>{
474
+ const unit = units[at];
475
+ if (!unit.measured) return;
476
+ measured += 1;
477
+ if (itemKept(built, node.group, item, unit)) kept += 1;
478
+ });
479
+ entry.groups += 1;
480
+ entry.items += measured;
481
+ entry.itemsKept += kept;
482
+ if (measured > 0 && kept === measured) entry.groupsKept += 1;
483
+ break;
484
+ }
485
+ case 'table':
486
+ {
487
+ tables.source += 1;
488
+ const cells = node.rows.flatMap((row)=>row.cells.map((cell)=>wordLine(cell.lines.map((ref)=>{
489
+ var _source_blocks_ref;
490
+ return clean((_source_blocks_ref = source.blocks[ref]) == null ? void 0 : _source_blocks_ref.text);
491
+ }).join(' '))).filter(Boolean));
492
+ // The theme's Table element, every cell of the source's in it.
493
+ const kept = built.under(root != null ? root : tree.rootId).some((id)=>{
494
+ var _drawnAs_props;
495
+ const drawnAs = built.node(id);
496
+ if ((drawnAs == null ? void 0 : drawnAs.componentId) !== 'dataTable' || typeof ((_drawnAs_props = drawnAs.props) == null ? void 0 : _drawnAs_props['rows']) !== 'string') return false;
497
+ const shown = new Set(tableCells(drawnAs.props['rows']).map(wordLine));
498
+ return cells.every((cell)=>shown.has(cell));
499
+ });
500
+ if (kept) tables.built += 1;
501
+ break;
502
+ }
503
+ case 'tabs':
504
+ {
505
+ var _built_node;
506
+ tabs.source += 1;
507
+ const labels = node.tabs.flatMap((tab)=>tab.label !== null ? [
508
+ tab.label
509
+ ] : []);
510
+ const found = placement.nodesOf(labels);
511
+ if (!found.whole || !found.nodes.length) break;
512
+ // The Tabs element names every tab itself; a strip draws each label as an element of its own, side by side.
513
+ const one = new Set(found.nodes).size === 1;
514
+ const strip = built.common(found.nodes);
515
+ if (one ? ((_built_node = built.node(found.nodes[0])) == null ? void 0 : _built_node.componentId) === 'muiTabs' : !!strip && setsSideBySide(built.node(strip))) tabs.built += 1;
516
+ break;
517
+ }
518
+ default:
519
+ break;
520
+ }
521
+ }
522
+ });
523
+ const pictures = picturesScore(source, built, placement);
524
+ const headings = headingsScore(source, built, placement);
525
+ const forms = ratioBoth(sections.reduce((total, section)=>total + layoutNodes(section.nodes).filter((node)=>node.kind === 'form').length, 0), built.order.filter((id)=>{
526
+ var _built_node;
527
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'form';
528
+ }).length);
529
+ const report = {
530
+ words,
531
+ sections: ratioBoth(sections.length, drawnSections.length),
532
+ bands: _extends({}, ratioKept(read.length, bandsKept), {
533
+ read,
534
+ drawn
535
+ }),
536
+ rows: ratioKept(rows.source, rows.built),
537
+ groups: Object.fromEntries(Object.entries(groups).map(([kind, entry])=>[
538
+ kind,
539
+ {
540
+ groups: ratioKept(entry.groups, entry.groupsKept),
541
+ items: ratioKept(entry.items, entry.itemsKept)
542
+ }
543
+ ])),
544
+ tables: ratioKept(tables.source, tables.built),
545
+ tabs: ratioKept(tabs.source, tabs.built),
546
+ pictures,
547
+ icons: _extends({}, ratioKept(icons.source, icons.built), {
548
+ lone: icons.lone
549
+ }),
550
+ buttons: _extends({}, ratioKept(buttons.source, buttons.built), {
551
+ unlinked: buttons.unlinked
552
+ }),
553
+ headings,
554
+ forms
555
+ };
556
+ return _extends({}, lowestOf(pageMeasures(report)), report);
557
+ }
558
+ /** Whether an item of a group is kept: its own element, every line of it found, and the parts its kind and source say it has. */ function itemKept(built, kind, item, unit) {
559
+ if (!unit.whole || !unit.element) return false;
560
+ const element = unit.element;
561
+ // A question opens its answer: the theme's accordion.
562
+ if (kind === 'faq' && !built.has(element, (node)=>node.componentId === 'muiAccordion')) return false;
563
+ // An item the source made a link as a whole still goes there.
564
+ if (item.to && !built.has(element, hasDestination)) return false;
565
+ // A picture of the item's own, where the library's copy is not known to the score.
566
+ if (item.image !== undefined && !built.has(element, (node)=>node.componentId === 'image')) return false;
567
+ return true;
568
+ }
569
+ /** The words: the source's lines against what the page shows, each word as often as it is said. */ function wordsScore(source, built) {
570
+ const tally = (lines)=>{
571
+ var _counts_get;
572
+ const counts = new Map();
573
+ for (const word of lines.flatMap(wordsOf))counts.set(word, ((_counts_get = counts.get(word)) != null ? _counts_get : 0) + 1);
574
+ return counts;
575
+ };
576
+ const said = tally(source.blocks.flatMap((block)=>isLine(block) && block.kind !== 'image' ? [
577
+ clean(block.text)
578
+ ] : []));
579
+ const shown = tally(built.pieces.map((piece)=>piece.text));
580
+ let sourceWords = 0;
581
+ let builtWords = 0;
582
+ let both = 0;
583
+ const missing = [];
584
+ const extra = [];
585
+ for (const [word, count] of said){
586
+ var _shown_get;
587
+ sourceWords += count;
588
+ const on = (_shown_get = shown.get(word)) != null ? _shown_get : 0;
589
+ both += Math.min(count, on);
590
+ if (on < count) missing.push(word);
591
+ }
592
+ for (const [word, count] of shown){
593
+ var _said_get;
594
+ builtWords += count;
595
+ if (count > ((_said_get = said.get(word)) != null ? _said_get : 0)) extra.push(word);
596
+ }
597
+ const most = Math.max(sourceWords, builtWords);
598
+ return {
599
+ source: sourceWords,
600
+ built: builtWords,
601
+ missing: sourceWords - both,
602
+ extra: builtWords - both,
603
+ ratio: most > 0 ? both / most : 1,
604
+ examples: {
605
+ missing: missing.slice(0, 8),
606
+ extra: extra.slice(0, 8)
607
+ }
608
+ };
609
+ }
610
+ /** The pictures: placed where the library's copies are known, else counted. */ function picturesScore(source, built, placement) {
611
+ var _source_photos;
612
+ const refs = source.layout.sections.flatMap((section)=>section.lines).filter((ref)=>{
613
+ var _source_blocks_ref;
614
+ return ((_source_blocks_ref = source.blocks[ref]) == null ? void 0 : _source_blocks_ref.kind) === 'image' && isLine(source.blocks[ref]);
615
+ });
616
+ const drawn = built.order.filter((id)=>{
617
+ var _built_node;
618
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'image';
619
+ }).length;
620
+ if (!((_source_photos = source.photos) == null ? void 0 : _source_photos.length)) return ratioBoth(refs.length, drawn);
621
+ return ratioKept(refs.length, refs.filter((ref)=>placement.of.has(ref)).length);
622
+ }
623
+ /** The headings: each of the source's drawn as a heading (an `h1`–`h6`, or the question of an accordion). */ function headingsScore(source, built, placement) {
624
+ const refs = source.layout.sections.flatMap((section)=>section.lines).filter((ref)=>{
625
+ var _source_blocks_ref;
626
+ return ((_source_blocks_ref = source.blocks[ref]) == null ? void 0 : _source_blocks_ref.kind) === 'heading';
627
+ });
628
+ const kept = refs.filter((ref)=>{
629
+ var _ref;
630
+ var _node_props;
631
+ const id = placement.of.get(ref);
632
+ const node = id ? built.node(id) : undefined;
633
+ return !!node && (/^h[1-6]$/.test(String((_ref = (_node_props = node.props) == null ? void 0 : _node_props['component']) != null ? _ref : '')) || node.componentId === 'muiAccordionSummary');
634
+ });
635
+ return ratioKept(refs.length, kept.length);
636
+ }
637
+ /** Every structural measure of a page by name, groups by kind. */ function pageMeasures(report) {
638
+ return [
639
+ [
640
+ 'sections',
641
+ report.sections
642
+ ],
643
+ [
644
+ 'bands',
645
+ report.bands
646
+ ],
647
+ [
648
+ 'rows',
649
+ report.rows
650
+ ],
651
+ ...Object.entries(report.groups).map(([kind, entry])=>[
652
+ `${kind} items`,
653
+ entry.items
654
+ ]),
655
+ [
656
+ 'tables',
657
+ report.tables
658
+ ],
659
+ [
660
+ 'tabs',
661
+ report.tabs
662
+ ],
663
+ [
664
+ 'pictures',
665
+ report.pictures
666
+ ],
667
+ [
668
+ 'icons',
669
+ report.icons
670
+ ],
671
+ [
672
+ 'buttons',
673
+ report.buttons
674
+ ],
675
+ [
676
+ 'headings',
677
+ report.headings
678
+ ],
679
+ [
680
+ 'forms',
681
+ report.forms
682
+ ]
683
+ ];
684
+ }
685
+ /** The score: the lowest ratio, and the measure it came from. */ function lowestOf(measures) {
686
+ let score = 1;
687
+ let lowest = null;
688
+ for (const [name, count] of measures){
689
+ if (count.ratio < score) {
690
+ score = count.ratio;
691
+ lowest = name;
692
+ }
693
+ }
694
+ return {
695
+ score,
696
+ lowest
697
+ };
698
+ }
699
+ /** A node's label as a menu, a link or a button carries it. */ function labelOf(node) {
700
+ var _node_props, _node_props1, _node_props2;
701
+ if (!node) return '';
702
+ return clean((_node_props = node.props) == null ? void 0 : _node_props['label']) || clean((_node_props1 = node.props) == null ? void 0 : _node_props1['children']) || clean((_node_props2 = node.props) == null ? void 0 : _node_props2['ariaLabel']);
703
+ }
704
+ const LINK_COMPONENTS = new Set([
705
+ 'muiScreenLink',
706
+ 'muiButton',
707
+ 'muiLinkBox',
708
+ 'muiLink'
709
+ ]);
710
+ /**
711
+ * A built header and footer scored against the source's (AGL-4109): the
712
+ * header's top-level items in order, each menu's entries with the lines and
713
+ * icons under them, its buttons, the phone menu holding all of it, the row's
714
+ * labels on one line; the footer's columns, links, profiles and facts. The
715
+ * score is the lowest ratio.
716
+ */ export function siteImportFrameFidelity(source, tree) {
717
+ var _all_find, _all_find1, _all_find2, _inHeader_find, _source_nav, _ref, _ref1, _ref2, _ref3, _source_footer, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10;
718
+ var _built_node_sx, _built_node;
719
+ const built = new Built(tree);
720
+ const all = built.order;
721
+ const is = (componentId)=>(id)=>{
722
+ var _built_node;
723
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === componentId;
724
+ };
725
+ const drawer = (_all_find = all.find(is('muiDrawer'))) != null ? _all_find : null;
726
+ const inDrawer = (id)=>!!drawer && built.holds(drawer, id);
727
+ const header = (_all_find1 = all.find((id)=>{
728
+ var _built_node, _built_node_props, _built_node1;
729
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'muiAppBar' || ((_built_node1 = built.node(id)) == null ? void 0 : (_built_node_props = _built_node1.props) == null ? void 0 : _built_node_props['component']) === 'header';
730
+ })) != null ? _all_find1 : null;
731
+ const footer = (_all_find2 = all.find((id)=>{
732
+ var _built_node_props, _built_node, _built_node_props1, _built_node1;
733
+ return ((_built_node = built.node(id)) == null ? void 0 : (_built_node_props = _built_node.props) == null ? void 0 : _built_node_props['element']) === 'footer' || ((_built_node1 = built.node(id)) == null ? void 0 : (_built_node_props1 = _built_node1.props) == null ? void 0 : _built_node_props1['component']) === 'footer';
734
+ })) != null ? _all_find2 : null;
735
+ const inHeader = header ? built.under(header).filter((id)=>!inDrawer(id)) : [];
736
+ const row = (_inHeader_find = inHeader.find((id)=>{
737
+ var _built_node_props, _built_node;
738
+ return ((_built_node = built.node(id)) == null ? void 0 : (_built_node_props = _built_node.props) == null ? void 0 : _built_node_props['component']) === 'nav';
739
+ })) != null ? _inHeader_find : null;
740
+ const nav = (_source_nav = source.nav) != null ? _source_nav : null;
741
+ const groups = (_ref = nav == null ? void 0 : nav.items) != null ? _ref : [];
742
+ // The row: its items by name, in order.
743
+ const rowItems = row ? built.children(row).map((id)=>labelOf(built.node(id))) : [];
744
+ const wanted = groups.map((item)=>clean(item.label));
745
+ const left = [
746
+ ...rowItems
747
+ ];
748
+ let matched = 0;
749
+ for (const label of wanted){
750
+ const at = left.indexOf(label);
751
+ if (at !== -1) {
752
+ matched += 1;
753
+ left.splice(at, 1);
754
+ }
755
+ }
756
+ const inOrder = rowItems.filter((label)=>wanted.includes(label)).join('\n') === wanted.filter((label)=>rowItems.includes(label)).join('\n');
757
+ // Each menu: drawn as one, under its name, with its entries, their lines and icons.
758
+ const menus = {
759
+ source: 0,
760
+ built: 0
761
+ };
762
+ const entries = {
763
+ source: 0,
764
+ built: 0
765
+ };
766
+ const details = {
767
+ source: 0,
768
+ built: 0
769
+ };
770
+ for (const group of groups){
771
+ if (!group.items.length) continue;
772
+ menus.source += 1;
773
+ entries.source += group.items.length;
774
+ const menu = row ? built.under(row).find((id)=>{
775
+ var _built_node, _built_node1, _built_node_props, _built_node2;
776
+ return (((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'muiMegaMenu' || ((_built_node1 = built.node(id)) == null ? void 0 : _built_node1.componentId) === 'muiNavMenu') && clean((_built_node2 = built.node(id)) == null ? void 0 : (_built_node_props = _built_node2.props) == null ? void 0 : _built_node_props['label']) === clean(group.label);
777
+ }) : undefined;
778
+ for (const entry of group.items){
779
+ if (entry.text) details.source += 1;
780
+ if (entry.iconPath) details.source += 1;
781
+ }
782
+ if (!menu) continue;
783
+ menus.built += 1;
784
+ const inside = built.under(menu);
785
+ const links = inside.filter((id)=>{
786
+ var _ref;
787
+ var _built_node;
788
+ return LINK_COMPONENTS.has((_ref = (_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) != null ? _ref : '');
789
+ });
790
+ const texts = new Set(inside.flatMap((id)=>shownTexts(built.node(id))));
791
+ for (const entry of group.items){
792
+ const link = links.find((id)=>labelOf(built.node(id)) === clean(entry.label) || shownTexts(built.node(id)).includes(clean(entry.label)) || built.under(id).some((at)=>shownTexts(built.node(at)).includes(clean(entry.label))));
793
+ if (!link) continue;
794
+ entries.built += 1;
795
+ if (entry.text && texts.has(clean(entry.text))) details.built += 1;
796
+ if (entry.iconPath && built.has(link, (node)=>node.componentId === 'icon')) details.built += 1;
797
+ }
798
+ }
799
+ // The buttons, as the source draws them.
800
+ const ctas = {
801
+ source: 0,
802
+ built: 0
803
+ };
804
+ for (const cta of (_ref1 = nav == null ? void 0 : nav.ctas) != null ? _ref1 : []){
805
+ var _node_props;
806
+ ctas.source += 1;
807
+ const button = inHeader.find((id)=>{
808
+ var _built_node, _built_node_props, _built_node1;
809
+ return ((_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) === 'muiButton' && clean((_built_node1 = built.node(id)) == null ? void 0 : (_built_node_props = _built_node1.props) == null ? void 0 : _built_node_props['children']) === clean(cta.label);
810
+ });
811
+ const node = button ? built.node(button) : undefined;
812
+ if (node && ((_node_props = node.props) == null ? void 0 : _node_props['variant']) === BUTTON_VARIANT[cta.style] && (!cta.href || hasDestination(node))) ctas.built += 1;
813
+ }
814
+ // The phone menu: every name, entry, link and button of the row.
815
+ const drawerTexts = new Set(drawer ? built.under(drawer).flatMap((id)=>shownTexts(built.node(id))) : []);
816
+ const drawerWanted = [
817
+ ...groups.flatMap((group)=>[
818
+ clean(group.label),
819
+ ...group.items.map((entry)=>clean(entry.label))
820
+ ]),
821
+ ...((_ref2 = nav == null ? void 0 : nav.ctas) != null ? _ref2 : []).map((cta)=>clean(cta.label))
822
+ ].filter(Boolean);
823
+ const toggle = inHeader.some(is('muiDrawerToggle'));
824
+ const hasRow = groups.length > 0 || ((_ref3 = nav == null ? void 0 : nav.ctas.length) != null ? _ref3 : 0) > 0;
825
+ // The footer.
826
+ const foot = (_source_footer = source.footer) != null ? _source_footer : null;
827
+ const footIds = footer ? built.under(footer) : [];
828
+ const footTexts = new Set(footIds.flatMap((id)=>shownTexts(built.node(id))));
829
+ const footLinks = footIds.filter((id)=>{
830
+ var _ref;
831
+ var _built_node;
832
+ return LINK_COMPONENTS.has((_ref = (_built_node = built.node(id)) == null ? void 0 : _built_node.componentId) != null ? _ref : '');
833
+ });
834
+ const footLinkLabels = footLinks.map((id)=>labelOf(built.node(id)));
835
+ const columnLinks = ((_ref4 = foot == null ? void 0 : foot.columns) != null ? _ref4 : []).flatMap((column)=>column.links);
836
+ const linksWanted = [
837
+ ...columnLinks,
838
+ ...(_ref5 = foot == null ? void 0 : foot.links) != null ? _ref5 : []
839
+ ];
840
+ const linkLeft = [
841
+ ...footLinkLabels
842
+ ];
843
+ let linksKept = 0;
844
+ for (const link of linksWanted){
845
+ const at = linkLeft.indexOf(clean(link.label));
846
+ if (at !== -1) {
847
+ linksKept += 1;
848
+ linkLeft.splice(at, 1);
849
+ }
850
+ }
851
+ const profiles = footIds.filter(is('socialLinks')).flatMap((id)=>{
852
+ var _ref;
853
+ var _built_node;
854
+ return Object.entries((_ref = (_built_node = built.node(id)) == null ? void 0 : _built_node.props) != null ? _ref : {}).filter(([, value])=>typeof value === 'string' && /^https?:/.test(value));
855
+ });
856
+ const hrefs = new Set(footLinks.map((id)=>{
857
+ var _built_node_props, _built_node;
858
+ return clean((_built_node = built.node(id)) == null ? void 0 : (_built_node_props = _built_node.props) == null ? void 0 : _built_node_props['href']);
859
+ }));
860
+ const socialKept = ((_ref6 = foot == null ? void 0 : foot.social) != null ? _ref6 : []).filter((profile)=>profiles.some(([network, href])=>network === profile.network || href === profile.href) || hrefs.has(clean(profile.href))).length;
861
+ const facts = [
862
+ ...(_ref7 = foot == null ? void 0 : foot.text) != null ? _ref7 : [],
863
+ foot == null ? void 0 : foot.address,
864
+ foot == null ? void 0 : foot.phone,
865
+ foot == null ? void 0 : foot.email,
866
+ foot == null ? void 0 : foot.copyright
867
+ ].map(clean).filter(Boolean);
868
+ const report = {
869
+ // Over the larger of the two: a row of the site's page titles in place of the source's menus scores low.
870
+ items: {
871
+ source: wanted.length,
872
+ built: matched,
873
+ ratio: Math.max(wanted.length, rowItems.length) > 0 ? matched / Math.max(wanted.length, rowItems.length) : 1
874
+ },
875
+ inOrder,
876
+ menus: ratioKept(menus.source, menus.built),
877
+ entries: ratioKept(entries.source, entries.built),
878
+ details: ratioKept(details.source, details.built),
879
+ ctas: ratioKept(ctas.source, ctas.built),
880
+ drawer: ratioKept(drawerWanted.length, drawerWanted.filter((label)=>drawerTexts.has(label)).length),
881
+ phone: ratioKept(hasRow ? 1 : 0, hasRow && toggle && !!drawer ? 1 : 0),
882
+ nowrap: ratioKept(row ? 1 : 0, row && ((_built_node = built.node(row)) == null ? void 0 : (_built_node_sx = _built_node.sx) == null ? void 0 : _built_node_sx['whiteSpace']) === 'nowrap' ? 1 : 0),
883
+ columns: ratioKept((_ref8 = foot == null ? void 0 : foot.columns.length) != null ? _ref8 : 0, ((_ref9 = foot == null ? void 0 : foot.columns) != null ? _ref9 : []).filter((column)=>footTexts.has(clean(column.heading))).length),
884
+ links: ratioKept(linksWanted.length, linksKept),
885
+ social: ratioKept((_ref10 = foot == null ? void 0 : foot.social.length) != null ? _ref10 : 0, socialKept),
886
+ facts: ratioKept(facts.length, facts.filter((fact)=>footTexts.has(fact)).length)
887
+ };
888
+ const measures = [
889
+ [
890
+ 'items',
891
+ report.items
892
+ ],
893
+ [
894
+ 'menus',
895
+ report.menus
896
+ ],
897
+ [
898
+ 'entries',
899
+ report.entries
900
+ ],
901
+ [
902
+ 'details',
903
+ report.details
904
+ ],
905
+ [
906
+ 'ctas',
907
+ report.ctas
908
+ ],
909
+ [
910
+ 'drawer',
911
+ report.drawer
912
+ ],
913
+ [
914
+ 'phone',
915
+ report.phone
916
+ ],
917
+ [
918
+ 'nowrap',
919
+ report.nowrap
920
+ ],
921
+ [
922
+ 'columns',
923
+ report.columns
924
+ ],
925
+ [
926
+ 'links',
927
+ report.links
928
+ ],
929
+ [
930
+ 'social',
931
+ report.social
932
+ ],
933
+ [
934
+ 'facts',
935
+ report.facts
936
+ ]
937
+ ];
938
+ return _extends({}, lowestOf(measures), report);
939
+ }
940
+
941
+ //# sourceMappingURL=ai-site-import-fidelity.js.map