@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,826 @@
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
+ * HOW A PAGE IS STYLED, AS A WIDE SCREEN SHOWS IT (AGL-4106).
18
+ *
19
+ * Bring my site reads a page's layout, not only its lines, and most of a
20
+ * layout lives in the stylesheet: which band is grey and which is dark, which
21
+ * container lays its children out in three columns, which link is drawn as a
22
+ * filled button. This is a small cascade, enough for that and no more:
23
+ *
24
+ * - **The sheets.** The page's own `<style>` sheets and the ones it links,
25
+ * read in order. `@media` rules apply as a 1280-pixel screen in light mode
26
+ * sees them, so a grid's desktop columns win over its phone ones;
27
+ * `@layer`, `@supports` and `@container` are read through; keyframes, font
28
+ * faces (their families noted) and page rules are skipped.
29
+ * - **Selectors.** Tags, classes (escaped ones too, as utility frameworks
30
+ * write them), ids and attributes, joined by any combinator. A rule that
31
+ * only applies on hover, focus or to a generated part (`::before`) is a
32
+ * state the page is not in and is not read.
33
+ * - **The cascade.** Specificity, then order, `!important` over both, and the
34
+ * element's own `style` over every rule; color, font and alignment inherit,
35
+ * and so do custom properties, which `var()` reads.
36
+ *
37
+ * Only the properties the layout reader asks about are kept. Nothing here
38
+ * runs anything a page wrote: values are text compared to text.
39
+ *=========================================*/ /** The width a page is read at: a laptop's, where a desktop layout shows. */ export const SITE_IMPORT_CSS_VIEWPORT = {
40
+ width: 1280,
41
+ height: 800
42
+ };
43
+ /** The properties the layout reader reads; every other declaration is dropped as the sheet is parsed. */ const KEPT_PROPERTIES = new Set([
44
+ 'background',
45
+ 'background-color',
46
+ 'background-image',
47
+ 'color',
48
+ 'border',
49
+ 'border-width',
50
+ 'border-style',
51
+ 'border-color',
52
+ 'border-top',
53
+ 'border-right',
54
+ 'border-bottom',
55
+ 'border-left',
56
+ 'border-top-width',
57
+ 'border-bottom-width',
58
+ 'border-left-width',
59
+ 'border-right-width',
60
+ 'border-top-style',
61
+ 'border-bottom-style',
62
+ 'border-top-color',
63
+ 'border-bottom-color',
64
+ 'border-radius',
65
+ 'box-shadow',
66
+ 'display',
67
+ 'grid-template-columns',
68
+ 'grid-column',
69
+ 'flex',
70
+ 'flex-direction',
71
+ 'flex-wrap',
72
+ 'flex-grow',
73
+ 'flex-basis',
74
+ 'width',
75
+ 'max-width',
76
+ 'font-family',
77
+ 'font-size',
78
+ 'font-weight',
79
+ 'text-transform',
80
+ 'letter-spacing',
81
+ 'text-align',
82
+ 'position',
83
+ 'visibility',
84
+ 'padding',
85
+ 'padding-top',
86
+ 'padding-bottom',
87
+ 'padding-left',
88
+ 'padding-right',
89
+ 'object-fit',
90
+ 'justify-content',
91
+ 'align-items'
92
+ ]);
93
+ /** What an element takes from its parent when it says nothing itself. */ const INHERITED = new Set([
94
+ 'color',
95
+ 'font-family',
96
+ 'font-size',
97
+ 'font-weight',
98
+ 'text-transform',
99
+ 'letter-spacing',
100
+ 'text-align',
101
+ 'visibility'
102
+ ]);
103
+ /*------------------------------------------
104
+ * Parsing
105
+ *-----------------------------------------*/ function unescapeIdent(text) {
106
+ return text.replace(/\\([0-9a-fA-F]{1,6}\s?|.)/g, (_, escaped)=>{
107
+ if (/^[0-9a-fA-F]{1,6}\s?$/.test(escaped)) {
108
+ const code = parseInt(escaped.trim(), 16);
109
+ return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '';
110
+ }
111
+ return escaped;
112
+ });
113
+ }
114
+ const IDENT = String.raw`(?:\\[0-9a-fA-F]{1,6}\s?|\\.|[\w-]|[^\x00-\x7f])+`;
115
+ const SIMPLE = new RegExp(String.raw`^(?:(\*|${IDENT})|#(${IDENT})|\.(${IDENT})|\[\s*([\w-]+)\s*(?:([~|^$*]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\]\s]+))\s*(?:[is]\s*)?)?\]|(::?)(${IDENT})(\((?:[^()]|\([^()]*\))*\))?)`);
116
+ /** The pseudo-classes a page at rest matches as if they were not there. */ const AT_REST = new Set([
117
+ 'link',
118
+ 'any-link'
119
+ ]);
120
+ /** One compound selector, or `null` when it depends on a state or a generated part. */ function parseCompound(text) {
121
+ const compound = {
122
+ tag: null,
123
+ id: null,
124
+ classes: [],
125
+ attrs: [],
126
+ root: false
127
+ };
128
+ let rest = text;
129
+ while(rest){
130
+ var _match_, _ref, _ref1, _match_1;
131
+ const match = SIMPLE.exec(rest);
132
+ if (!match || !match[0]) return null;
133
+ rest = rest.slice(match[0].length);
134
+ if (match[1]) compound.tag = match[1] === '*' ? null : unescapeIdent(match[1]).toLowerCase();
135
+ else if (match[2]) compound.id = unescapeIdent(match[2]);
136
+ else if (match[3]) compound.classes.push(unescapeIdent(match[3]));
137
+ else if (match[4]) compound.attrs.push({
138
+ name: match[4].toLowerCase(),
139
+ op: (_match_ = match[5]) != null ? _match_ : null,
140
+ value: (_ref = (_ref1 = (_match_1 = match[6]) != null ? _match_1 : match[7]) != null ? _ref1 : match[8]) != null ? _ref : ''
141
+ });
142
+ else if (match[9]) {
143
+ var _match_2;
144
+ const name = unescapeIdent((_match_2 = match[10]) != null ? _match_2 : '').toLowerCase();
145
+ if (match[9] === '::') return null;
146
+ if (name === 'root') compound.root = true;
147
+ else if (!AT_REST.has(name)) return null;
148
+ }
149
+ }
150
+ return compound;
151
+ }
152
+ /** A complex selector, or `null` when any part of it cannot be read at rest. */ function parseSelector(text) {
153
+ const compounds = [];
154
+ const combinators = [];
155
+ const parts = text.trim().replace(/\s*([>+~])\s*/g, ' $1 ').split(/\s+/);
156
+ let pending = null;
157
+ for (const part of parts){
158
+ if (!part) continue;
159
+ if (part === '>' || part === '+' || part === '~') {
160
+ pending = part;
161
+ continue;
162
+ }
163
+ const compound = parseCompound(part);
164
+ if (!compound) return null;
165
+ if (compounds.length) combinators.push(pending != null ? pending : ' ');
166
+ pending = null;
167
+ compounds.push(compound);
168
+ }
169
+ if (!compounds.length || pending) return null;
170
+ let ids = 0;
171
+ let classes = 0;
172
+ let tags = 0;
173
+ for (const compound of compounds){
174
+ if (compound.id) ids += 1;
175
+ classes += compound.classes.length + compound.attrs.length + (compound.root ? 1 : 0);
176
+ if (compound.tag) tags += 1;
177
+ }
178
+ return {
179
+ compounds,
180
+ combinators,
181
+ specificity: ids * 1000000 + classes * 1000 + tags
182
+ };
183
+ }
184
+ /** Splits at top-level commas: none inside brackets or parentheses. */ function splitList(text) {
185
+ const out = [];
186
+ let depth = 0;
187
+ let start = 0;
188
+ for(let at = 0; at < text.length; at += 1){
189
+ const char = text[at];
190
+ if (char === '(' || char === '[') depth += 1;
191
+ else if (char === ')' || char === ']') depth = Math.max(0, depth - 1);
192
+ else if (char === ',' && depth === 0) {
193
+ out.push(text.slice(start, at));
194
+ start = at + 1;
195
+ }
196
+ }
197
+ out.push(text.slice(start));
198
+ return out.map((part)=>part.trim()).filter(Boolean);
199
+ }
200
+ /** A length in px at the read width; `em`/`rem` at 16. */ function lengthPx(value) {
201
+ const match = /^(-?\d*\.?\d+)\s*(px|em|rem)?$/i.exec(value.trim());
202
+ if (!match) return null;
203
+ const amount = Number(match[1]);
204
+ return match[2] && match[2].toLowerCase() !== 'px' ? amount * 16 : amount;
205
+ }
206
+ /** Whether one media query holds for a light-mode screen of the read width. */ function mediaQueryHolds(query) {
207
+ let text = query.trim().toLowerCase();
208
+ let negate = false;
209
+ if (text.startsWith('not ')) {
210
+ negate = true;
211
+ text = text.slice(4);
212
+ }
213
+ text = text.replace(/^only\s+/, '');
214
+ let holds = true;
215
+ for (const part of text.split(/\s+and\s+/)){
216
+ const term = part.trim();
217
+ if (!term) continue;
218
+ if (!term.startsWith('(')) {
219
+ if (!/^(?:all|screen)$/.test(term)) holds = false;
220
+ continue;
221
+ }
222
+ const inner = term.replace(/^\(|\)$/g, '').trim();
223
+ const feature = /^([\w-]+)\s*:\s*(.+)$/.exec(inner);
224
+ const { width, height } = SITE_IMPORT_CSS_VIEWPORT;
225
+ if (feature) {
226
+ const [, name, value] = feature;
227
+ const px = lengthPx(value);
228
+ if (name === 'min-width') holds && (holds = px !== null && width >= px);
229
+ else if (name === 'max-width') holds && (holds = px !== null && width <= px);
230
+ else if (name === 'min-height') holds && (holds = px !== null && height >= px);
231
+ else if (name === 'max-height') holds && (holds = px !== null && height <= px);
232
+ else if (name === 'prefers-color-scheme') holds && (holds = value.trim() === 'light');
233
+ else if (name === 'prefers-reduced-motion') holds && (holds = value.trim() === 'no-preference');
234
+ else if (name === 'orientation') holds && (holds = value.trim() === 'landscape');
235
+ else if (name === 'hover' || name === 'any-hover') holds && (holds = value.trim() === 'hover');
236
+ else if (name === 'pointer' || name === 'any-pointer') holds && (holds = value.trim() === 'fine');
237
+ else if (name === 'prefers-contrast' || name === 'forced-colors') holds && (holds = /^(?:no-preference|none)$/.test(value.trim()));
238
+ continue;
239
+ }
240
+ // Range syntax: (width >= 900px), (600px <= width < 1200px).
241
+ const range = /^(?:(-?[\d.]+\w*)\s*(<=|<|>=|>)\s*)?(width|height)\s*(?:(<=|<|>=|>)\s*(-?[\d.]+\w*))?$/.exec(inner);
242
+ if (range) {
243
+ const size = range[3] === 'width' ? width : height;
244
+ const test = (a, op, b)=>op === '<' ? a < b : op === '<=' ? a <= b : op === '>' ? a > b : a >= b;
245
+ if (range[1] && range[2]) {
246
+ const px = lengthPx(range[1]);
247
+ holds && (holds = px !== null && test(px, range[2], size));
248
+ }
249
+ if (range[4] && range[5]) {
250
+ const px = lengthPx(range[5]);
251
+ holds && (holds = px !== null && test(size, range[4], px));
252
+ }
253
+ }
254
+ }
255
+ return negate ? !holds : holds;
256
+ }
257
+ function mediaHolds(prelude) {
258
+ const list = splitList(prelude.replace(/^@media\s*/i, ''));
259
+ return !list.length || list.some(mediaQueryHolds);
260
+ }
261
+ function declarationsOf(body) {
262
+ const decls = [];
263
+ // Values may hold semicolons only inside strings or url(), which the kept properties never need.
264
+ for (const part of body.split(';')){
265
+ const colon = part.indexOf(':');
266
+ if (colon <= 0) continue;
267
+ const written = part.slice(0, colon).trim();
268
+ // A custom property's name is case-sensitive, as `var()` reads it.
269
+ const property = written.startsWith('--') ? written : written.toLowerCase();
270
+ if (!property.startsWith('--') && !KEPT_PROPERTIES.has(property)) continue;
271
+ let value = part.slice(colon + 1).trim();
272
+ const important = /!\s*important\s*$/i.test(value);
273
+ if (important) value = value.replace(/!\s*important\s*$/i, '').trim();
274
+ if (value) decls.push([
275
+ property,
276
+ value.slice(0, 2000),
277
+ important
278
+ ]);
279
+ }
280
+ return decls;
281
+ }
282
+ /** The index key of a selector: its rightmost compound's id, first class, tag, else `*`. */ function keyOf(selector) {
283
+ const last = selector.compounds[selector.compounds.length - 1];
284
+ if (last.id) return `#${last.id}`;
285
+ if (last.classes.length) return `.${last.classes[0]}`;
286
+ if (last.root) return ':root';
287
+ if (last.tag) return last.tag;
288
+ return '*';
289
+ }
290
+ /**
291
+ * The page's sheets parsed for a wide screen. Malformed CSS is read as far as
292
+ * it makes sense, as a browser would; nothing throws.
293
+ */ export function siteImportCssOf(sheets) {
294
+ const css = {
295
+ rules: [],
296
+ index: new Map(),
297
+ fontFaces: []
298
+ };
299
+ let order = 0;
300
+ const addRule = (prelude, body)=>{
301
+ const decls = declarationsOf(body);
302
+ if (!decls.length) return;
303
+ for (const text of splitList(prelude)){
304
+ const selector = parseSelector(text);
305
+ if (!selector) continue;
306
+ const rule = {
307
+ selector,
308
+ order: order++,
309
+ decls
310
+ };
311
+ css.rules.push(rule);
312
+ const key = keyOf(selector);
313
+ const list = css.index.get(key);
314
+ if (list) list.push(rule);
315
+ else css.index.set(key, [
316
+ rule
317
+ ]);
318
+ }
319
+ };
320
+ for (const sheet of sheets){
321
+ const text = String(sheet != null ? sheet : '').replace(/\/\*[\s\S]*?\*\//g, ' ');
322
+ /** For each open group: whether its rules apply. */ const groups = [];
323
+ let at = 0;
324
+ let start = 0;
325
+ while(at < text.length){
326
+ const char = text[at];
327
+ if (char === '"' || char === "'") {
328
+ const end = text.indexOf(char, at + 1);
329
+ at = end < 0 ? text.length : end + 1;
330
+ continue;
331
+ }
332
+ if (char === ';') {
333
+ // A statement at-rule (@import, @charset) or stray text.
334
+ start = at + 1;
335
+ at += 1;
336
+ continue;
337
+ }
338
+ if (char === '}') {
339
+ groups.pop();
340
+ start = at + 1;
341
+ at += 1;
342
+ continue;
343
+ }
344
+ if (char !== '{') {
345
+ at += 1;
346
+ continue;
347
+ }
348
+ const prelude = text.slice(start, at).trim();
349
+ const applies = groups.every(Boolean);
350
+ if (/^@(?:media)\b/i.test(prelude)) {
351
+ groups.push(applies && mediaHolds(prelude));
352
+ start = at = at + 1;
353
+ continue;
354
+ }
355
+ if (/^@(?:supports|layer|container|document|scope|starting-style)\b/i.test(prelude)) {
356
+ groups.push(applies && !/^@starting-style/i.test(prelude));
357
+ start = at = at + 1;
358
+ continue;
359
+ }
360
+ // A block to read whole: a style rule, a font face, or an at-rule to skip.
361
+ let depth = 1;
362
+ let end = at + 1;
363
+ while(end < text.length && depth > 0){
364
+ if (text[end] === '{') depth += 1;
365
+ else if (text[end] === '}') depth -= 1;
366
+ end += 1;
367
+ }
368
+ const body = text.slice(at + 1, Math.max(at + 1, end - 1));
369
+ if (/^@font-face\b/i.test(prelude)) {
370
+ var _exec_, _exec;
371
+ const family = (_exec = /font-family\s*:\s*(["']?)([^;"']+)\1/i.exec(body)) == null ? void 0 : (_exec_ = _exec[2]) == null ? void 0 : _exec_.trim();
372
+ if (family && !css.fontFaces.includes(family)) css.fontFaces.push(family);
373
+ } else if (!prelude.startsWith('@') && applies && prelude) {
374
+ // A rule nested inside a rule (`&:hover`) is a state: only the outer declarations are read.
375
+ addRule(prelude, body.replace(/[^;{}]*\{[^{}]*\}/g, ''));
376
+ }
377
+ start = at = end;
378
+ }
379
+ }
380
+ return css;
381
+ }
382
+ /*------------------------------------------
383
+ * Matching and the cascade
384
+ *-----------------------------------------*/ function classesOf(element) {
385
+ var _element_attrs_class;
386
+ return ((_element_attrs_class = element.attrs.class) != null ? _element_attrs_class : '').split(/\s+/).filter(Boolean);
387
+ }
388
+ function attributeOf(element, name) {
389
+ switch(name){
390
+ case 'class':
391
+ return element.attrs.class;
392
+ case 'id':
393
+ return element.attrs.id;
394
+ case 'role':
395
+ return element.attrs.role;
396
+ case 'aria-label':
397
+ return element.attrs.label;
398
+ case 'href':
399
+ return element.attrs.href;
400
+ case 'type':
401
+ return element.attrs.type;
402
+ case 'style':
403
+ return element.attrs.style;
404
+ case 'hidden':
405
+ case 'aria-hidden':
406
+ return element.attrs.hidden ? 'true' : undefined;
407
+ case 'data-section-theme':
408
+ case 'data-theme':
409
+ return element.attrs.theme;
410
+ default:
411
+ return undefined;
412
+ }
413
+ }
414
+ function compoundMatches(compound, element, rootId) {
415
+ if (compound.root && element.id !== rootId) return false;
416
+ if (compound.tag && compound.tag !== element.tag) return false;
417
+ if (compound.id && compound.id !== element.attrs.id) return false;
418
+ if (compound.classes.length) {
419
+ const own = classesOf(element);
420
+ if (!compound.classes.every((name)=>own.includes(name))) return false;
421
+ }
422
+ for (const attr of compound.attrs){
423
+ const value = attributeOf(element, attr.name);
424
+ if (value === undefined) return false;
425
+ if (!attr.op) continue;
426
+ const wanted = attr.value;
427
+ const ok = attr.op === '=' ? value === wanted : attr.op === '~=' ? value.split(/\s+/).includes(wanted) : attr.op === '^=' ? value.startsWith(wanted) : attr.op === '$=' ? value.endsWith(wanted) : attr.op === '*=' ? value.includes(wanted) : value === wanted || value.startsWith(`${wanted}-`);
428
+ if (!ok) return false;
429
+ }
430
+ return true;
431
+ }
432
+ /** Whether a selector matches an element, its combinators walked from the right. */ function selectorMatches(selector, element, elements, root) {
433
+ var _ref;
434
+ const rootId = (_ref = root == null ? void 0 : root.id) != null ? _ref : -2;
435
+ /** The element above `at`: its parent, or the document's root above the body. */ const above = (at)=>at.parent >= 0 ? elements[at.parent] : at === root ? undefined : root != null ? root : undefined;
436
+ const walk = (index, at)=>{
437
+ if (!compoundMatches(selector.compounds[index], at, rootId)) return false;
438
+ if (index === 0) return true;
439
+ const combinator = selector.combinators[index - 1];
440
+ if (combinator === '>') {
441
+ const parent = above(at);
442
+ return !!parent && walk(index - 1, parent);
443
+ }
444
+ if (combinator === '+' || combinator === '~') {
445
+ var _ref;
446
+ var _elements_at_parent;
447
+ const siblings = (_ref = (_elements_at_parent = elements[at.parent]) == null ? void 0 : _elements_at_parent.children) != null ? _ref : [];
448
+ const position = siblings.indexOf(at.id);
449
+ for(let before = position - 1; before >= 0; before -= 1){
450
+ const sibling = elements[siblings[before]];
451
+ if (!sibling) continue;
452
+ if (walk(index - 1, sibling)) return true;
453
+ if (combinator === '+') return false;
454
+ }
455
+ return false;
456
+ }
457
+ for(let parent = above(at); parent; parent = above(parent)){
458
+ if (walk(index - 1, parent)) return true;
459
+ }
460
+ return false;
461
+ };
462
+ return walk(selector.compounds.length - 1, element);
463
+ }
464
+ const EMPTY_STYLE = new Map();
465
+ /** Replaces each `var(--x, fallback)` with what the custom properties hold, to a few levels. */ function withVariables(value, custom) {
466
+ let out = value;
467
+ for(let pass = 0; pass < 4 && out.includes('var('); pass += 1){
468
+ out = out.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*((?:[^()]|\([^()]*\))*))?\)/g, (_, name, fallback)=>{
469
+ var _ref, _custom_get;
470
+ return (_ref = (_custom_get = custom.get(name)) != null ? _custom_get : fallback == null ? void 0 : fallback.trim()) != null ? _ref : '';
471
+ });
472
+ }
473
+ return out;
474
+ }
475
+ /** The kept declarations an inline `style` attribute writes. */ function inlineDeclarations(style) {
476
+ return style ? declarationsOf(style) : [];
477
+ }
478
+ /**
479
+ * Elements' styles over a page's structure and its sheets. Styles are
480
+ * resolved on demand, parent first, each element once.
481
+ */ export function siteImportStylesOf(structure, css) {
482
+ const { elements } = structure;
483
+ const resolved = new Map();
484
+ // The page's root is above the body: `:root` and `html` rules are its.
485
+ const rootStyle = (()=>{
486
+ var _structure_root, _css_index_get, _css_index_get1, _css_index_get2, _css_index_get3, _css_index_get4;
487
+ const style = new Map();
488
+ const custom = new Map();
489
+ const fake = {
490
+ id: -2,
491
+ tag: 'html',
492
+ parent: -1,
493
+ children: [],
494
+ region: 'main',
495
+ nav: false,
496
+ attrs: (_structure_root = structure.root) != null ? _structure_root : {},
497
+ blocks: []
498
+ };
499
+ const candidates = [
500
+ ...(_css_index_get = css.index.get(':root')) != null ? _css_index_get : [],
501
+ ...(_css_index_get1 = css.index.get('html')) != null ? _css_index_get1 : [],
502
+ ...(_css_index_get2 = css.index.get('*')) != null ? _css_index_get2 : []
503
+ ];
504
+ for (const name of classesOf(fake))candidates.push(...(_css_index_get3 = css.index.get(`.${name}`)) != null ? _css_index_get3 : []);
505
+ if (fake.attrs.id) candidates.push(...(_css_index_get4 = css.index.get(`#${fake.attrs.id}`)) != null ? _css_index_get4 : []);
506
+ apply(candidates.filter((rule)=>selectorMatches(rule.selector, fake, [], fake)), inlineDeclarations(fake.attrs.style), style, custom);
507
+ return {
508
+ style,
509
+ custom,
510
+ element: fake
511
+ };
512
+ })();
513
+ function apply(rules, inline, style, custom) {
514
+ const unique = [
515
+ ...new Set(rules)
516
+ ].sort((a, b)=>a.selector.specificity - b.selector.specificity || a.order - b.order);
517
+ const important = [];
518
+ const put = (property, value)=>{
519
+ if (property.startsWith('--')) custom.set(property, value);
520
+ else style.set(property, value);
521
+ };
522
+ for (const rule of unique){
523
+ for (const [property, value, flag] of rule.decls){
524
+ if (flag) important.push([
525
+ property,
526
+ value
527
+ ]);
528
+ else put(property, value);
529
+ }
530
+ }
531
+ for (const [property, value, flag] of inline){
532
+ if (flag) important.push([
533
+ property,
534
+ value
535
+ ]);
536
+ else put(property, value);
537
+ }
538
+ for (const [property, value] of important)put(property, value);
539
+ }
540
+ function resolve(id) {
541
+ var _css_index_get, _css_index_get1, _css_index_get2, _css_index_get3;
542
+ const held = resolved.get(id);
543
+ if (held) return held;
544
+ const element = elements[id];
545
+ if (!element) return rootStyle;
546
+ const parent = element.parent >= 0 ? resolve(element.parent) : rootStyle;
547
+ const candidates = [
548
+ ...(_css_index_get = css.index.get(element.tag)) != null ? _css_index_get : [],
549
+ ...(_css_index_get1 = css.index.get('*')) != null ? _css_index_get1 : []
550
+ ];
551
+ for (const name of classesOf(element))candidates.push(...(_css_index_get2 = css.index.get(`.${name}`)) != null ? _css_index_get2 : []);
552
+ if (element.attrs.id) candidates.push(...(_css_index_get3 = css.index.get(`#${element.attrs.id}`)) != null ? _css_index_get3 : []);
553
+ const matched = candidates.filter((rule)=>selectorMatches(rule.selector, element, elements, rootStyle.element));
554
+ const own = new Map();
555
+ const custom = new Map(parent.custom);
556
+ apply(matched, inlineDeclarations(element.attrs.style), own, custom);
557
+ const style = new Map();
558
+ for (const property of INHERITED){
559
+ const value = parent.style.get(property);
560
+ if (value !== undefined) style.set(property, value);
561
+ }
562
+ for (const [property, value] of own){
563
+ const lowered = value.trim().toLowerCase();
564
+ if (lowered === 'inherit') {
565
+ const inherited = parent.style.get(property);
566
+ if (inherited !== undefined) style.set(property, inherited);
567
+ continue;
568
+ }
569
+ if (lowered === 'initial' || lowered === 'unset' || lowered === 'revert') {
570
+ if (!INHERITED.has(property) || lowered === 'initial') style.delete(property);
571
+ continue;
572
+ }
573
+ style.set(property, property === 'font-family' ? withVariables(value, custom) : withVariables(value, custom).toLowerCase());
574
+ }
575
+ const entry = {
576
+ style,
577
+ custom
578
+ };
579
+ resolved.set(id, entry);
580
+ return entry;
581
+ }
582
+ // The root's own inherited values reach the body through `rootStyle`.
583
+ for (const [property, value] of rootStyle.style){
584
+ rootStyle.style.set(property, property === 'font-family' ? withVariables(value, rootStyle.custom) : withVariables(value, rootStyle.custom).toLowerCase());
585
+ }
586
+ return {
587
+ root: -2,
588
+ of (element) {
589
+ if (element === -2) return rootStyle.style;
590
+ return elements[element] ? resolve(element).style : EMPTY_STYLE;
591
+ }
592
+ };
593
+ }
594
+ const NAMED_COLORS = {
595
+ white: '#ffffff',
596
+ black: '#000000',
597
+ red: '#ff0000',
598
+ green: '#008000',
599
+ blue: '#0000ff',
600
+ gray: '#808080',
601
+ grey: '#808080',
602
+ silver: '#c0c0c0',
603
+ whitesmoke: '#f5f5f5',
604
+ ghostwhite: '#f8f8ff',
605
+ snow: '#fffafa',
606
+ ivory: '#fffff0',
607
+ beige: '#f5f5dc',
608
+ linen: '#faf0e6',
609
+ navy: '#000080',
610
+ teal: '#008080',
611
+ orange: '#ffa500',
612
+ gold: '#ffd700',
613
+ purple: '#800080',
614
+ crimson: '#dc143c',
615
+ tomato: '#ff6347',
616
+ coral: '#ff7f50',
617
+ aliceblue: '#f0f8ff',
618
+ lightgray: '#d3d3d3',
619
+ lightgrey: '#d3d3d3',
620
+ gainsboro: '#dcdcdc',
621
+ darkslategray: '#2f4f4f',
622
+ dimgray: '#696969'
623
+ };
624
+ /** The first color a value names, or `null`; `transparent` is a color of no strength. */ export function siteImportColorOf(value) {
625
+ if (!value) return null;
626
+ const text = value.toLowerCase();
627
+ const hex = /#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})\b/.exec(text);
628
+ const rgb = /rgba?\(\s*(-?[\d.]+%?)\s*[,\s]\s*(-?[\d.]+%?)\s*[,\s]\s*(-?[\d.]+%?)\s*(?:[,/]\s*(-?[\d.]+%?)\s*)?\)/.exec(text);
629
+ const hsl = /hsla?\(\s*(-?[\d.]+)(?:deg)?\s*[,\s]\s*([\d.]+)%\s*[,\s]\s*([\d.]+)%\s*(?:[,/]\s*([\d.]+%?)\s*)?\)/.exec(text);
630
+ const named = /\b(transparent|[a-z]+)\b/.exec(text.replace(/[a-z-]+\(/g, ' ('));
631
+ const first = [
632
+ hex,
633
+ rgb,
634
+ hsl
635
+ ].filter((match)=>!!match).sort((a, b)=>a.index - b.index)[0];
636
+ const alphaOf = (raw)=>raw === undefined ? 1 : raw.endsWith('%') ? Number(raw.slice(0, -1)) / 100 : Number(raw);
637
+ if (first === hex && hex) {
638
+ const digits = hex[1];
639
+ const full = digits.length <= 4 ? digits.split('').map((d)=>d + d).join('') : digits;
640
+ return {
641
+ r: parseInt(full.slice(0, 2), 16),
642
+ g: parseInt(full.slice(2, 4), 16),
643
+ b: parseInt(full.slice(4, 6), 16),
644
+ a: full.length === 8 ? parseInt(full.slice(6, 8), 16) / 255 : 1
645
+ };
646
+ }
647
+ if (first === rgb && rgb) {
648
+ const channel = (raw)=>Math.max(0, Math.min(255, raw.endsWith('%') ? Number(raw.slice(0, -1)) / 100 * 255 : Number(raw)));
649
+ return {
650
+ r: channel(rgb[1]),
651
+ g: channel(rgb[2]),
652
+ b: channel(rgb[3]),
653
+ a: alphaOf(rgb[4])
654
+ };
655
+ }
656
+ if (first === hsl && hsl) {
657
+ const h = (Number(hsl[1]) % 360 + 360) % 360 / 360;
658
+ const s = Number(hsl[2]) / 100;
659
+ const l = Number(hsl[3]) / 100;
660
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
661
+ const p = 2 * l - q;
662
+ const tone = (t)=>{
663
+ const x = t < 0 ? t + 1 : t > 1 ? t - 1 : t;
664
+ if (x < 1 / 6) return p + (q - p) * 6 * x;
665
+ if (x < 1 / 2) return q;
666
+ if (x < 2 / 3) return p + (q - p) * (2 / 3 - x) * 6;
667
+ return p;
668
+ };
669
+ return {
670
+ r: tone(h + 1 / 3) * 255,
671
+ g: tone(h) * 255,
672
+ b: tone(h - 1 / 3) * 255,
673
+ a: alphaOf(hsl[4])
674
+ };
675
+ }
676
+ if (named) {
677
+ if (named[1] === 'transparent') return {
678
+ r: 0,
679
+ g: 0,
680
+ b: 0,
681
+ a: 0
682
+ };
683
+ const known = NAMED_COLORS[named[1]];
684
+ if (known) return siteImportColorOf(known);
685
+ }
686
+ return null;
687
+ }
688
+ /** `#rrggbb` of a color, its strength ignored. */ export function siteImportHex(color) {
689
+ return `#${[
690
+ color.r,
691
+ color.g,
692
+ color.b
693
+ ].map((channel)=>Math.round(channel).toString(16).padStart(2, '0')).join('')}`;
694
+ }
695
+ /** Relative luminance, 0 (black) to 1 (white). */ export function siteImportLuminance(color) {
696
+ const linear = (channel)=>{
697
+ const c = channel / 255;
698
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
699
+ };
700
+ return 0.2126 * linear(color.r) + 0.7152 * linear(color.g) + 0.0722 * linear(color.b);
701
+ }
702
+ /** How colorful a color is, 0 (grey) to 1. */ export function siteImportSaturation(color) {
703
+ const max = Math.max(color.r, color.g, color.b) / 255;
704
+ const min = Math.min(color.r, color.g, color.b) / 255;
705
+ if (max === min) return 0;
706
+ const l = (max + min) / 2;
707
+ return (max - min) / (1 - Math.abs(2 * l - 1));
708
+ }
709
+ /** An element's own background, from the shorthand or its parts. */ export function siteImportBackgroundOf(style) {
710
+ var _style_get, _style_get1, _style_get2;
711
+ const shorthand = (_style_get = style.get('background')) != null ? _style_get : '';
712
+ const imageValue = (_style_get1 = style.get('background-image')) != null ? _style_get1 : /(?:url|gradient)\(/.test(shorthand) ? shorthand : '';
713
+ const colorValue = (_style_get2 = style.get('background-color')) != null ? _style_get2 : shorthand && !/(?:url|gradient)\(/.test(shorthand) ? shorthand : shorthand.replace(/[a-z-]*gradient\((?:[^()]|\([^()]*\))*\)|url\([^)]*\)/g, ' ');
714
+ const color = siteImportColorOf(colorValue);
715
+ let gradient = null;
716
+ let image = null;
717
+ const gradientMatch = /gradient\(((?:[^()]|\([^()]*\))*)\)/.exec(imageValue);
718
+ if (gradientMatch) {
719
+ const stops = [];
720
+ for (const match of gradientMatch[1].matchAll(/#[0-9a-f]{3,8}\b|rgba?\([^)]*\)|hsla?\([^)]*\)|\b(?:white|black|transparent)\b/gi)){
721
+ const stop = siteImportColorOf(match[0]);
722
+ if (stop) stops.push(stop);
723
+ }
724
+ if (stops.length) gradient = stops;
725
+ }
726
+ const url = /url\(\s*(["']?)([^"')]+)\1\s*\)/.exec(imageValue);
727
+ if (url) image = url[2].trim();
728
+ return {
729
+ color: color && color.a > 0.02 ? color : null,
730
+ gradient,
731
+ image
732
+ };
733
+ }
734
+ /** Whether an element draws a border on any side. */ export function siteImportHasBorder(style) {
735
+ const sides = [
736
+ 'border',
737
+ 'border-top',
738
+ 'border-bottom',
739
+ 'border-left',
740
+ 'border-right'
741
+ ];
742
+ for (const side of sides){
743
+ var _siteImportColorOf;
744
+ const value = style.get(side);
745
+ if (!value) continue;
746
+ if (/\bnone\b|\bhidden\b/.test(value) || /^0(?:px)?$/.test(value.trim())) continue;
747
+ const color = (_siteImportColorOf = siteImportColorOf(value)) != null ? _siteImportColorOf : siteImportColorOf(style.get('border-color'));
748
+ if (color && color.a < 0.05) continue;
749
+ if (/\d/.test(value) || /\b(?:solid|dashed|dotted|double)\b/.test(value)) {
750
+ const width = /(-?[\d.]+)px/.exec(value);
751
+ if (!width || Number(width[1]) > 0) return true;
752
+ }
753
+ }
754
+ const width = style.get('border-width');
755
+ const kind = style.get('border-style');
756
+ if (kind && /\b(?:solid|dashed|dotted|double)\b/.test(kind) && (!width || !/^0(?:px)?$/.test(width.trim()))) return true;
757
+ return false;
758
+ }
759
+ /** Whether an element draws a shadow. */ export function siteImportHasShadow(style) {
760
+ const value = style.get('box-shadow');
761
+ return !!value && !/^(?:none|0|initial|unset)$/.test(value.trim()) && !/^0(?:px)?\s+0(?:px)?\s+0(?:px)?(?:\s+0(?:px)?)?\s+(?:transparent|rgba\([^)]*,\s*0\))/.test(value.trim());
762
+ }
763
+ /** An element's corner radius in px, or `null`. */ export function siteImportRadiusPx(style) {
764
+ var _value_trim_split_;
765
+ const value = style.get('border-radius');
766
+ if (!value) return null;
767
+ const first = (_value_trim_split_ = value.trim().split(/[\s/]+/)[0]) != null ? _value_trim_split_ : '';
768
+ if (first.endsWith('%')) return Number(first.slice(0, -1)) >= 50 ? 999 : Number(first.slice(0, -1));
769
+ return lengthPx(first);
770
+ }
771
+ /** A font size in px, or `null`. */ export function siteImportFontSizePx(style) {
772
+ const value = style.get('font-size');
773
+ if (!value) return null;
774
+ const clamp = /clamp\(([^,]+),([^,]+),([^)]+)\)/.exec(value);
775
+ // A fluid size at the read width: its maximum, as a wide screen shows it.
776
+ const text = clamp ? clamp[3] : value;
777
+ return lengthPx(text.trim());
778
+ }
779
+ /** The relative widths a grid template draws its columns at, or `null` for none. */ export function siteImportGridColumns(template) {
780
+ if (!template || /^(?:none|subgrid|masonry)$/.test(template.trim())) return null;
781
+ let text = template.trim();
782
+ // repeat(3, 1fr) / repeat(auto-fill, minmax(240px, 1fr)): the count, or three of them.
783
+ text = text.replace(/repeat\(\s*([\w-]+)\s*,\s*((?:[^()]|\([^()]*\))*)\)/g, (_, count, track)=>{
784
+ const times = /^\d+$/.test(count) ? Math.min(12, Number(count)) : 3;
785
+ return Array.from({
786
+ length: times
787
+ }, ()=>track.trim()).join(' ');
788
+ });
789
+ const tracks = [];
790
+ let depth = 0;
791
+ let token = '';
792
+ for (const char of text){
793
+ if (char === '(') depth += 1;
794
+ if (char === ')') depth -= 1;
795
+ if (/\s/.test(char) && depth === 0) {
796
+ if (token) tracks.push(token);
797
+ token = '';
798
+ } else token += char;
799
+ }
800
+ if (token) tracks.push(token);
801
+ const weights = tracks.filter((track)=>!/^\[.*\]$/.test(track)).map((track)=>{
802
+ const fr = /(-?[\d.]+)fr\)?$/.exec(track);
803
+ if (fr) return Number(fr[1]) || 1;
804
+ const px = /(-?[\d.]+)px/.exec(track);
805
+ if (px) return Number(px[1]) / 100;
806
+ const percent = /(-?[\d.]+)%/.exec(track);
807
+ if (percent) return Number(percent[1]) / 10;
808
+ return 1;
809
+ });
810
+ return weights.length ? weights : null;
811
+ }
812
+ /** Weights scaled to twelfths, each at least 1: `[420, 788]` is `[4, 8]`. */ export function siteImportTwelfths(weights) {
813
+ const total = weights.reduce((sum, weight)=>sum + Math.max(0, weight), 0);
814
+ if (!total) return weights.map(()=>1);
815
+ const scaled = weights.map((weight)=>Math.max(1, Math.round(Math.max(0, weight) / total * 12)));
816
+ // Two equal tracks stay equal, three stay thirds.
817
+ const unique = new Set(weights.map((weight)=>Math.round(weight * 1000)));
818
+ if (unique.size === 1) return weights.map(()=>1);
819
+ return scaled;
820
+ }
821
+ /** The families a `font-family` value names, quotes dropped, generic ones left out. */ export function siteImportFontFamilies(value) {
822
+ if (!value) return [];
823
+ return splitList(value).map((family)=>family.replace(/^["']|["']$/g, '').trim()).filter((family)=>family && !/^(?:serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-[\w-]+|-apple-system|blinkmacsystemfont|inherit|initial|emoji|math|var\(.*)$/i.test(family));
824
+ }
825
+
826
+ //# sourceMappingURL=site-import-css.js.map