@samva/markup 0.13.1 → 0.15.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.
- package/AGENTS.md +12 -2
- package/CHANGELOG.md +58 -0
- package/README.md +1 -1
- package/dist/{assets-DCULM6QD.d.ts → assets-tISHaQNF.d.ts} +1 -1
- package/dist/{brand-C45QBKX9.js → brand-nreeKWxF.js} +5 -5
- package/dist/brand.d.ts +48 -25
- package/dist/brand.js +4 -4
- package/dist/compiler.d.ts +23 -6
- package/dist/compiler.js +658 -200
- package/dist/components-3XHjYD3i.js +401 -0
- package/dist/dark-scheme-CbCwGDWH.js +12 -0
- package/dist/{diagnostic-codes-DdZ4tka5.js → diagnostic-codes-UXwOO3A-.js} +22 -0
- package/dist/diagnostics.d.ts +17 -0
- package/dist/diagnostics.js +1 -1
- package/dist/edit.js +2 -2
- package/dist/email.d.ts +52 -9
- package/dist/email.js +1 -1
- package/dist/{html-eOZfDwVf.js → html-BvRFepeO.js} +9 -0
- package/dist/{ir-C-ubz6N3.js → ir-DNL6H7jA.js} +203 -79
- package/dist/{profile-CXnkfEMi.js → profile-AatO_SvP.js} +135 -13
- package/dist/{render-C6OTH8BQ.d.ts → render-BdvewoQt.d.ts} +19 -0
- package/dist/{render-Cja448i0.js → render-DpYouYYG.js} +8 -6
- package/dist/render.d.ts +1 -1
- package/dist/render.js +3 -3
- package/dist/{theme-css-CEUrDpqq.js → theme-css-s9QwD5Ko.js} +108 -46
- package/dist/whatsapp.d.ts +20 -2
- package/dist/whatsapp.js +3 -2
- package/generated/contract.json +206 -2
- package/generated/reference.md +165 -110
- package/package.json +1 -1
- package/skills/sml/SKILL.md +63 -5
- package/dist/components-DycSEKva.js +0 -273
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as isObject } from "./guards-eV1qLbf2.js";
|
|
2
|
-
import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-
|
|
2
|
+
import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-BvRFepeO.js";
|
|
3
3
|
import { t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
|
|
4
|
+
import { i as withinDarkScheme, t as DARK_SCHEME_MEDIA } from "./dark-scheme-CbCwGDWH.js";
|
|
4
5
|
//#region src/email/font-stacks.ts
|
|
5
6
|
/** Generic families every email client understands as the last resort in a stack. */
|
|
6
7
|
const GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
@@ -90,6 +91,73 @@ const fontFallbackDiagnostic = (value) => ({
|
|
|
90
91
|
message: `font-family ${JSON.stringify(value)} uses a project font without a generic fallback. Clients that drop webfonts (Gmail, classic Outlook) render this stack as written, so end it with sans-serif, serif or monospace.`
|
|
91
92
|
});
|
|
92
93
|
//#endregion
|
|
94
|
+
//#region src/email/selectors.ts
|
|
95
|
+
const classListOf = (element) => {
|
|
96
|
+
const value = element.props.class ?? element.props.className;
|
|
97
|
+
return typeof value === "string" ? value.split(/\s+/).filter((name) => name !== "") : [];
|
|
98
|
+
};
|
|
99
|
+
const attributeOf = (element, name) => {
|
|
100
|
+
const value = element.props[name];
|
|
101
|
+
if (value === void 0 || value === null || value === false) return void 0;
|
|
102
|
+
if (value === true) return "";
|
|
103
|
+
return typeof value === "string" || typeof value === "number" ? String(value) : void 0;
|
|
104
|
+
};
|
|
105
|
+
const matchesAttribute = (element, test) => {
|
|
106
|
+
const actual = test.name === "class" ? classListOf(element).join(" ") : attributeOf(element, test.name);
|
|
107
|
+
if (actual === void 0) return false;
|
|
108
|
+
if (test.operator === void 0 || test.value === void 0) return true;
|
|
109
|
+
switch (test.operator) {
|
|
110
|
+
case "=": return actual === test.value;
|
|
111
|
+
case "~=": return actual.split(/\s+/).includes(test.value);
|
|
112
|
+
case "^=": return actual.startsWith(test.value);
|
|
113
|
+
case "$=": return actual.endsWith(test.value);
|
|
114
|
+
case "*=": return actual.includes(test.value);
|
|
115
|
+
case "|=": return actual === test.value || actual.startsWith(`${test.value}-`);
|
|
116
|
+
default: return false;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const matchesCompound = (element, compound) => {
|
|
120
|
+
if (compound.opaque) return false;
|
|
121
|
+
if (compound.tag !== void 0 && compound.tag !== element.tag) return false;
|
|
122
|
+
if (compound.id !== void 0 && attributeOf(element, "id") !== compound.id) return false;
|
|
123
|
+
if (compound.classes.length > 0) {
|
|
124
|
+
const classes = classListOf(element);
|
|
125
|
+
if (!compound.classes.every((name) => classes.includes(name))) return false;
|
|
126
|
+
}
|
|
127
|
+
return compound.attributes.every((test) => matchesAttribute(element, test));
|
|
128
|
+
};
|
|
129
|
+
const matchesFrom = (selector, index, ancestry) => {
|
|
130
|
+
if (ancestry === void 0) return false;
|
|
131
|
+
const part = selector.parts[index];
|
|
132
|
+
if (part === void 0) return false;
|
|
133
|
+
if (!matchesCompound(ancestry.element, part.compound)) return false;
|
|
134
|
+
const left = selector.parts[index - 1];
|
|
135
|
+
if (left === void 0) return true;
|
|
136
|
+
switch (left.after ?? "descendant") {
|
|
137
|
+
case "child": return matchesFrom(selector, index - 1, ancestry.parent);
|
|
138
|
+
case "descendant":
|
|
139
|
+
for (let parent = ancestry.parent; parent !== void 0; parent = parent.parent) if (matchesFrom(selector, index - 1, parent)) return true;
|
|
140
|
+
return false;
|
|
141
|
+
case "adjacent": {
|
|
142
|
+
const sibling = ancestry.previous.at(-1);
|
|
143
|
+
return sibling === void 0 ? false : matchesFrom(selector, index - 1, {
|
|
144
|
+
element: sibling,
|
|
145
|
+
parent: ancestry.parent,
|
|
146
|
+
previous: ancestry.previous.slice(0, -1)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
case "sibling":
|
|
150
|
+
for (let position = ancestry.previous.length - 1; position >= 0; position--) if (matchesFrom(selector, index - 1, {
|
|
151
|
+
element: ancestry.previous[position],
|
|
152
|
+
parent: ancestry.parent,
|
|
153
|
+
previous: ancestry.previous.slice(0, position)
|
|
154
|
+
})) return true;
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
/** Match a compiled selector against one element in its tree position. */
|
|
159
|
+
const matchesSelector = (selector, ancestry) => selector === void 0 || selector.parts.length === 0 ? false : matchesFrom(selector, selector.parts.length - 1, ancestry);
|
|
160
|
+
//#endregion
|
|
93
161
|
//#region src/email/css-values.ts
|
|
94
162
|
const MAX_RESOLVED_VALUE_LENGTH = 65536;
|
|
95
163
|
const MAX_RESOLUTION_WORK = 262144;
|
|
@@ -253,8 +321,12 @@ const downlevelMediaQuery = (condition) => condition.replace(RANGE_FEATURE, (who
|
|
|
253
321
|
return `(${comparison.startsWith(">") ? "min-" : "max-"}${feature}:${bound})`;
|
|
254
322
|
});
|
|
255
323
|
/** Render the rules that could not be inlined as one `<style>` body. */
|
|
256
|
-
const renderHeadCss = (rules, headAtRules, variables) => {
|
|
324
|
+
const renderHeadCss = (rules, headAtRules, variables, darkVariables = {}) => {
|
|
257
325
|
const blocks = [...headAtRules];
|
|
326
|
+
const darkScope = {
|
|
327
|
+
...variables,
|
|
328
|
+
...darkVariables
|
|
329
|
+
};
|
|
258
330
|
const grouped = /* @__PURE__ */ new Map();
|
|
259
331
|
for (const rule of rules) {
|
|
260
332
|
const conditions = rule.conditions.map(downlevelMediaQuery);
|
|
@@ -267,8 +339,9 @@ const renderHeadCss = (rules, headAtRules, variables) => {
|
|
|
267
339
|
else bucket.rules.push(rule);
|
|
268
340
|
}
|
|
269
341
|
for (const { conditions, rules: bucket } of grouped.values()) {
|
|
342
|
+
const scope = withinDarkScheme(conditions) ? darkScope : variables;
|
|
270
343
|
const body = bucket.map((rule) => {
|
|
271
|
-
const declarations = rule.declarations.filter((declaration) => !declaration.property.startsWith("--")).map((declaration) => `${declaration.property}:${finalizeValue(declaration.value,
|
|
344
|
+
const declarations = rule.declarations.filter((declaration) => !declaration.property.startsWith("--")).map((declaration) => `${declaration.property}:${finalizeValue(declaration.value, scope)}!important`).join(";");
|
|
272
345
|
return declarations === "" ? "" : `${rule.selector}{${declarations}}`;
|
|
273
346
|
}).filter((block) => block !== "").join("");
|
|
274
347
|
if (body === "") continue;
|
|
@@ -277,73 +350,6 @@ const renderHeadCss = (rules, headAtRules, variables) => {
|
|
|
277
350
|
return blocks.join("");
|
|
278
351
|
};
|
|
279
352
|
//#endregion
|
|
280
|
-
//#region src/email/selectors.ts
|
|
281
|
-
const classListOf = (element) => {
|
|
282
|
-
const value = element.props.class ?? element.props.className;
|
|
283
|
-
return typeof value === "string" ? value.split(/\s+/).filter((name) => name !== "") : [];
|
|
284
|
-
};
|
|
285
|
-
const attributeOf = (element, name) => {
|
|
286
|
-
const value = element.props[name];
|
|
287
|
-
if (value === void 0 || value === null || value === false) return void 0;
|
|
288
|
-
if (value === true) return "";
|
|
289
|
-
return typeof value === "string" || typeof value === "number" ? String(value) : void 0;
|
|
290
|
-
};
|
|
291
|
-
const matchesAttribute = (element, test) => {
|
|
292
|
-
const actual = test.name === "class" ? classListOf(element).join(" ") : attributeOf(element, test.name);
|
|
293
|
-
if (actual === void 0) return false;
|
|
294
|
-
if (test.operator === void 0 || test.value === void 0) return true;
|
|
295
|
-
switch (test.operator) {
|
|
296
|
-
case "=": return actual === test.value;
|
|
297
|
-
case "~=": return actual.split(/\s+/).includes(test.value);
|
|
298
|
-
case "^=": return actual.startsWith(test.value);
|
|
299
|
-
case "$=": return actual.endsWith(test.value);
|
|
300
|
-
case "*=": return actual.includes(test.value);
|
|
301
|
-
case "|=": return actual === test.value || actual.startsWith(`${test.value}-`);
|
|
302
|
-
default: return false;
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
const matchesCompound = (element, compound) => {
|
|
306
|
-
if (compound.opaque) return false;
|
|
307
|
-
if (compound.tag !== void 0 && compound.tag !== element.tag) return false;
|
|
308
|
-
if (compound.id !== void 0 && attributeOf(element, "id") !== compound.id) return false;
|
|
309
|
-
if (compound.classes.length > 0) {
|
|
310
|
-
const classes = classListOf(element);
|
|
311
|
-
if (!compound.classes.every((name) => classes.includes(name))) return false;
|
|
312
|
-
}
|
|
313
|
-
return compound.attributes.every((test) => matchesAttribute(element, test));
|
|
314
|
-
};
|
|
315
|
-
const matchesFrom = (selector, index, ancestry) => {
|
|
316
|
-
if (ancestry === void 0) return false;
|
|
317
|
-
const part = selector.parts[index];
|
|
318
|
-
if (part === void 0) return false;
|
|
319
|
-
if (!matchesCompound(ancestry.element, part.compound)) return false;
|
|
320
|
-
const left = selector.parts[index - 1];
|
|
321
|
-
if (left === void 0) return true;
|
|
322
|
-
switch (left.after ?? "descendant") {
|
|
323
|
-
case "child": return matchesFrom(selector, index - 1, ancestry.parent);
|
|
324
|
-
case "descendant":
|
|
325
|
-
for (let parent = ancestry.parent; parent !== void 0; parent = parent.parent) if (matchesFrom(selector, index - 1, parent)) return true;
|
|
326
|
-
return false;
|
|
327
|
-
case "adjacent": {
|
|
328
|
-
const sibling = ancestry.previous.at(-1);
|
|
329
|
-
return sibling === void 0 ? false : matchesFrom(selector, index - 1, {
|
|
330
|
-
element: sibling,
|
|
331
|
-
parent: ancestry.parent,
|
|
332
|
-
previous: ancestry.previous.slice(0, -1)
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
case "sibling":
|
|
336
|
-
for (let position = ancestry.previous.length - 1; position >= 0; position--) if (matchesFrom(selector, index - 1, {
|
|
337
|
-
element: ancestry.previous[position],
|
|
338
|
-
parent: ancestry.parent,
|
|
339
|
-
previous: ancestry.previous.slice(0, position)
|
|
340
|
-
})) return true;
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
/** Match a compiled selector against one element in its tree position. */
|
|
345
|
-
const matchesSelector = (selector, ancestry) => selector === void 0 || selector.parts.length === 0 ? false : matchesFrom(selector, selector.parts.length - 1, ancestry);
|
|
346
|
-
//#endregion
|
|
347
353
|
//#region src/email/cascade.ts
|
|
348
354
|
const VARIABLE_REFERENCE = /var\(\s*--[\w-]+\s*(?:,[^()]*)?\)/g;
|
|
349
355
|
/**
|
|
@@ -434,12 +440,98 @@ const physicalPadding = (property, value, context) => {
|
|
|
434
440
|
if (match[2] !== void 0) return [[`padding-${sides[match[2] === "start" ? 0 : 1]}`, values[0]]];
|
|
435
441
|
return [[`padding-${sides[0]}`, values[0]], [`padding-${sides[1]}`, values[1] ?? values[0]]];
|
|
436
442
|
};
|
|
443
|
+
/** Generated dark-mode classes share this prefix; it is reserved for the compiler. */
|
|
444
|
+
const DARK_CLASS_PREFIX = "sml-dark-";
|
|
445
|
+
/** A class named for its declarations, so equal sets share one rule and builds are stable. */
|
|
446
|
+
const darkClassName = (body) => {
|
|
447
|
+
let hash = 2166136261;
|
|
448
|
+
for (let index = 0; index < body.length; index++) {
|
|
449
|
+
hash ^= body.charCodeAt(index);
|
|
450
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
451
|
+
}
|
|
452
|
+
return `${DARK_CLASS_PREFIX}${hash.toString(36)}`;
|
|
453
|
+
};
|
|
454
|
+
const SIDES = [
|
|
455
|
+
"top",
|
|
456
|
+
"right",
|
|
457
|
+
"bottom",
|
|
458
|
+
"left"
|
|
459
|
+
];
|
|
460
|
+
const BORDER_PARTS = [
|
|
461
|
+
"width",
|
|
462
|
+
"style",
|
|
463
|
+
"color"
|
|
464
|
+
];
|
|
465
|
+
const CORNERS = [
|
|
466
|
+
"top-left",
|
|
467
|
+
"top-right",
|
|
468
|
+
"bottom-right",
|
|
469
|
+
"bottom-left"
|
|
470
|
+
];
|
|
471
|
+
const BORDER_IMAGE = [
|
|
472
|
+
"source",
|
|
473
|
+
"slice",
|
|
474
|
+
"width",
|
|
475
|
+
"outset",
|
|
476
|
+
"repeat"
|
|
477
|
+
].map((part) => `border-image-${part}`);
|
|
478
|
+
/**
|
|
479
|
+
* The longhands a border property writes: `border` writes the twelve per-side longhands and
|
|
480
|
+
* resets the border image, `border-top` the top three, `border-color` the four colors,
|
|
481
|
+
* `border-radius` the four corners.
|
|
482
|
+
* `undefined` for a property outside the border box.
|
|
483
|
+
*/
|
|
484
|
+
const borderLonghands = (property) => {
|
|
485
|
+
if (property === "border") return [...SIDES.flatMap((side) => BORDER_PARTS.map((part) => `border-${side}-${part}`)), ...BORDER_IMAGE];
|
|
486
|
+
if (property === "border-image") return BORDER_IMAGE;
|
|
487
|
+
if (property === "border-radius") return CORNERS.map((corner) => `border-${corner}-radius`);
|
|
488
|
+
const side = /^border-(top|right|bottom|left)$/.exec(property)?.[1];
|
|
489
|
+
if (side !== void 0) return BORDER_PARTS.map((part) => `border-${side}-${part}`);
|
|
490
|
+
const part = /^border-(width|style|color)$/.exec(property)?.[1];
|
|
491
|
+
if (part !== void 0) return SIDES.map((each) => `border-${each}-${part}`);
|
|
492
|
+
return property.startsWith("border-") ? [property] : void 0;
|
|
493
|
+
};
|
|
494
|
+
/**
|
|
495
|
+
* Whether two declarations can override one another because they write a property in common:
|
|
496
|
+
* `background` and `background-color`, `border-color` and `border-top`, `font` and `line-height`.
|
|
497
|
+
* `border-radius` shares nothing with `border-color`.
|
|
498
|
+
*/
|
|
499
|
+
const overlaps = (left, right) => {
|
|
500
|
+
if (left === right) return true;
|
|
501
|
+
const leftBorder = borderLonghands(left);
|
|
502
|
+
const rightBorder = borderLonghands(right);
|
|
503
|
+
if (leftBorder !== void 0 || rightBorder !== void 0) return leftBorder !== void 0 && rightBorder !== void 0 && leftBorder.some((longhand) => rightBorder.includes(longhand));
|
|
504
|
+
const sets = (shorthand, longhand) => longhand.startsWith(`${shorthand}-`) || shorthand === "font" && longhand === "line-height";
|
|
505
|
+
return sets(left, right) || sets(right, left);
|
|
506
|
+
};
|
|
507
|
+
/** Add a class to an element's props, whichever of `class` and `className` it uses. */
|
|
508
|
+
const withClass = (props, name) => {
|
|
509
|
+
const key = typeof props.class === "string" || props.className === void 0 ? "class" : "className";
|
|
510
|
+
const current = props[key];
|
|
511
|
+
return {
|
|
512
|
+
...props,
|
|
513
|
+
[key]: typeof current === "string" && current.trim() !== "" ? `${current} ${name}` : name
|
|
514
|
+
};
|
|
515
|
+
};
|
|
516
|
+
/** The generated dark-mode classes as one `@media (prefers-color-scheme:dark)` block, written as the head CSS writes its media queries. */
|
|
517
|
+
const renderDarkRules = (rules) => rules.size === 0 ? "" : `${DARK_SCHEME_MEDIA.replace(": ", ":")}{${[...rules].sort(([left], [right]) => left.localeCompare(right)).map(([name, body]) => `.${name}{${body}}`).join("")}}`;
|
|
437
518
|
/**
|
|
438
519
|
* Resolve every stylesheet against the tree: inline what a client will honor on
|
|
439
|
-
* the element, and keep the rest for `<head>`.
|
|
520
|
+
* the element, and keep the rest for `<head>`. Where the sheet has a dark-mode
|
|
521
|
+
* theme, an element whose inlined styles read a color it redefines also gets a
|
|
522
|
+
* generated class whose dark-mode rule restates those styles at their dark
|
|
523
|
+
* values.
|
|
440
524
|
*/
|
|
441
525
|
const applyStylesheet = (root, sheet, options = {}) => {
|
|
442
526
|
const diagnostics = [...sheet.diagnostics];
|
|
527
|
+
const hasDarkTheme = Object.keys(sheet.darkVariables).length > 0;
|
|
528
|
+
const darkRules = /* @__PURE__ */ new Map();
|
|
529
|
+
const darkClassFor = (declarations) => {
|
|
530
|
+
const body = declarations.map(([property, value]) => `${property}:${value}!important`).join(";");
|
|
531
|
+
const name = darkClassName(body);
|
|
532
|
+
darkRules.set(name, body);
|
|
533
|
+
return name;
|
|
534
|
+
};
|
|
443
535
|
const inlinable = [];
|
|
444
536
|
const retained = [];
|
|
445
537
|
const styledClasses = /* @__PURE__ */ new Set();
|
|
@@ -462,7 +554,7 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
462
554
|
* A fragment is not an element in the serialized document, so its children
|
|
463
555
|
* join that same list rather than starting a new one.
|
|
464
556
|
*/
|
|
465
|
-
const visitChildren = (children, ancestry, variables, writing) => {
|
|
557
|
+
const visitChildren = (children, ancestry, variables, darkScope, writing) => {
|
|
466
558
|
const previous = [];
|
|
467
559
|
const visit = (nodes) => nodes.map((child) => {
|
|
468
560
|
if (child.type === "Element") {
|
|
@@ -470,7 +562,7 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
470
562
|
element: child,
|
|
471
563
|
parent: ancestry,
|
|
472
564
|
previous: [...previous]
|
|
473
|
-
}, variables, writing);
|
|
565
|
+
}, variables, darkScope, writing);
|
|
474
566
|
previous.push(child);
|
|
475
567
|
return result;
|
|
476
568
|
}
|
|
@@ -489,7 +581,7 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
489
581
|
* cascade like any other declaration and then inherit, so a `--brand` declared
|
|
490
582
|
* on `.card` has to reach `var(--brand)` on `.card` and everything under it.
|
|
491
583
|
*/
|
|
492
|
-
const applyTo = (element, ancestry, inherited, inheritedWriting) => {
|
|
584
|
+
const applyTo = (element, ancestry, inherited, inheritedDark, inheritedWriting) => {
|
|
493
585
|
const conditionalWriting = conditionalWritingRules.some((rule) => {
|
|
494
586
|
if (rule.matcher === void 0 || rule.matcher.parts.length === 0 || rule.matcher.parts.some((part) => part.compound.opaque)) return true;
|
|
495
587
|
for (let context = ancestry; context !== void 0; context = context.parent) if (matchesSelector(rule.matcher, context)) return true;
|
|
@@ -519,6 +611,10 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
519
611
|
...inherited,
|
|
520
612
|
...declared
|
|
521
613
|
};
|
|
614
|
+
const darkScope = Object.keys(declared).length === 0 ? inheritedDark : {
|
|
615
|
+
...inheritedDark,
|
|
616
|
+
...declared
|
|
617
|
+
};
|
|
522
618
|
const inheritedValue = (property, parent, initial, fallback = parent) => {
|
|
523
619
|
const value = winners.get(property)?.value;
|
|
524
620
|
if (value === void 0) return fallback;
|
|
@@ -537,24 +633,51 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
537
633
|
physical.set(name, lowered);
|
|
538
634
|
}
|
|
539
635
|
const bgcolor = element.props.bgcolor;
|
|
540
|
-
const
|
|
636
|
+
const lightProps = typeof bgcolor === "string" && bgcolor.includes("var(") ? {
|
|
541
637
|
...element.props,
|
|
542
638
|
bgcolor: finalizeValue(bgcolor, variables)
|
|
543
639
|
} : element.props;
|
|
640
|
+
const dark = [];
|
|
641
|
+
if (hasDarkTheme) {
|
|
642
|
+
const changed = [];
|
|
643
|
+
for (const [property, entry] of winners) {
|
|
644
|
+
if (property.startsWith("--") || !entry.value.includes("var(")) continue;
|
|
645
|
+
if (finalizeValue(entry.value, darkScope) !== finalizeValue(entry.value, variables)) changed.push(property);
|
|
646
|
+
}
|
|
647
|
+
const restated = new Set(changed);
|
|
648
|
+
for (let grew = changed.length > 0; grew;) {
|
|
649
|
+
grew = false;
|
|
650
|
+
for (const property of winners.keys()) if (!property.startsWith("--") && !restated.has(property) && [...restated].some((candidate) => overlaps(candidate, property))) {
|
|
651
|
+
restated.add(property);
|
|
652
|
+
grew = true;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
for (const [property, entry] of [...winners].sort(([, left], [, right]) => compareRank(left.key, right.key))) if (restated.has(property)) dark.push([property, finalizeValue(entry.value, darkScope)]);
|
|
656
|
+
if (typeof bgcolor === "string" && bgcolor.includes("var(") && !winners.has("background-color") && !winners.has("background")) {
|
|
657
|
+
const value = finalizeValue(bgcolor, darkScope);
|
|
658
|
+
if (value !== finalizeValue(bgcolor, variables)) dark.push(["background-color", value]);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const darkClass = dark.length === 0 ? void 0 : darkClassFor(dark);
|
|
662
|
+
const props = darkClass === void 0 ? lightProps : withClass(lightProps, darkClass);
|
|
544
663
|
return EmailNode.Element({
|
|
545
664
|
...element,
|
|
546
665
|
props: emitted.length === 0 ? props : {
|
|
547
666
|
...props,
|
|
548
667
|
style: Object.fromEntries(physical)
|
|
549
668
|
},
|
|
550
|
-
children: visitChildren(element.children, ancestry, variables, writing)
|
|
669
|
+
children: visitChildren(element.children, ancestry, variables, darkScope, writing)
|
|
551
670
|
});
|
|
552
671
|
};
|
|
672
|
+
const rootDark = {
|
|
673
|
+
...sheet.variables,
|
|
674
|
+
...sheet.darkVariables
|
|
675
|
+
};
|
|
553
676
|
const tree = root.type === "Element" ? applyTo(root, {
|
|
554
677
|
element: root,
|
|
555
678
|
parent: void 0,
|
|
556
679
|
previous: []
|
|
557
|
-
}, sheet.variables, horizontalWriting) : root.type === "Fragment" ? EmailNode.Fragment({ children: visitChildren(root.children, void 0, sheet.variables, horizontalWriting) }) : root;
|
|
680
|
+
}, sheet.variables, rootDark, horizontalWriting) : root.type === "Fragment" ? EmailNode.Fragment({ children: visitChildren(root.children, void 0, sheet.variables, rootDark, horizontalWriting) }) : root;
|
|
558
681
|
for (const rule of retained) for (const declaration of rule.declarations) checkFallback(declaration.property, finalizeValue(declaration.value, sheet.variables), []);
|
|
559
682
|
for (const [value, origins] of fallbackFindings) diagnostics.push({
|
|
560
683
|
...fontFallbackDiagnostic(value),
|
|
@@ -568,7 +691,8 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
568
691
|
});
|
|
569
692
|
return {
|
|
570
693
|
tree,
|
|
571
|
-
headCss: renderHeadCss(retained, sheet.headAtRules, sheet.variables),
|
|
694
|
+
headCss: renderHeadCss(retained, sheet.headAtRules, sheet.variables, sheet.darkVariables),
|
|
695
|
+
darkRules,
|
|
572
696
|
fontFaceCss: sheet.fontFaces.map((face) => face.css).join(""),
|
|
573
697
|
diagnostics
|
|
574
698
|
};
|
|
@@ -578,4 +702,4 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
578
702
|
/** The IR version this package writes. A publication pins the version it was built with. */
|
|
579
703
|
const SML_IR_VERSION = 1;
|
|
580
704
|
//#endregion
|
|
581
|
-
export { downlevelMediaQuery as a, renderHeadCss as c, matchesSelector as i, resolveVariables as l,
|
|
705
|
+
export { downlevelMediaQuery as a, renderHeadCss as c, matchesSelector as d, unquote as f, renderDarkRules as i, resolveVariables as l, DARK_CLASS_PREFIX as n, finalizeValue as o, applyStylesheet as r, foldCalc as s, SML_IR_VERSION as t, classListOf as u };
|