@samva/markup 0.16.0 → 0.17.1

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 (60) hide show
  1. package/AGENTS.md +42 -40
  2. package/CHANGELOG.md +72 -0
  3. package/README.md +10 -9
  4. package/dist/{brand-jFV6YtSV.d.ts → assets-CS5Xa_mr.d.ts} +41 -3
  5. package/dist/{brand-Ci05p37c.js → brand-CIiEqkU7.js} +64 -3
  6. package/dist/{brand-plugin-CHhcei-j.d.ts → brand-plugin-Dj9SMpfS.d.ts} +3 -1
  7. package/dist/brand.d.ts +2 -3
  8. package/dist/brand.js +3 -4
  9. package/dist/compatibility.d.ts +76 -0
  10. package/dist/compatibility.js +807 -0
  11. package/dist/compiled-template-Bc4Dq9zy.d.ts +268 -0
  12. package/dist/compiler.d.ts +59 -193
  13. package/dist/compiler.js +473 -775
  14. package/dist/{components-3XHjYD3i.js → components-C5850-Qg.js} +37 -29
  15. package/dist/css-B_aVKrDo.js +436 -0
  16. package/dist/css-color-DLM12Osu.js +315 -0
  17. package/dist/{diagnostic-codes-8XDLR9ef.js → diagnostic-codes-DZKb0Xcg.js} +40 -1
  18. package/dist/{diagnostics-CUYlQ_6x.d.ts → diagnostics-D4vSLQrO.d.ts} +1 -10
  19. package/dist/diagnostics.d.ts +6 -2
  20. package/dist/diagnostics.js +3 -4
  21. package/dist/edit.d.ts +10 -9
  22. package/dist/edit.js +12 -11
  23. package/dist/email/jsx-dev-runtime.d.ts +1 -1
  24. package/dist/email/jsx-dev-runtime.js +1 -1
  25. package/dist/email/jsx-runtime.d.ts +2 -2
  26. package/dist/email/jsx-runtime.js +2 -2
  27. package/dist/email.d.ts +1 -1
  28. package/dist/email.js +1 -1
  29. package/dist/html-DJUK2YJF.js +483 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/{jsx-runtime-DC7xG0aA.d.ts → jsx-runtime-DL8NVF1f.d.ts} +12 -6
  32. package/dist/{jsx-runtime-CBlexoeR.js → jsx-runtime-qL19u3Fy.js} +17 -10
  33. package/dist/preview-rNoJBEIY.d.ts +104 -0
  34. package/dist/{profile-BsY4dkan.js → profile-JTvgCD6X.js} +714 -29
  35. package/dist/{html-BvRFepeO.js → render-template-DwPDm3Hq.js} +155 -572
  36. package/dist/render.d.ts +40 -28
  37. package/dist/render.js +3 -47
  38. package/dist/selectors-CYWCoxZr.js +157 -0
  39. package/dist/sms.d.ts +1 -1
  40. package/dist/sms.js +1 -1
  41. package/dist/{template-B-UDXK8a.d.ts → template-DmNcoH4Q.d.ts} +2 -9
  42. package/dist/{theme-css-s9QwD5Ko.js → theme-css-P9F5S7Ic.js} +3 -316
  43. package/dist/whatsapp.d.ts +1 -1
  44. package/dist/whatsapp.js +1 -1
  45. package/generated/contract.json +7 -2
  46. package/generated/reference.md +3 -2
  47. package/package.json +5 -5
  48. package/skills/sml/SKILL.md +2 -2
  49. package/dist/assets-BSpgZOSR.js +0 -61
  50. package/dist/assets-BSuNT9gr.d.ts +0 -39
  51. package/dist/brand-footer-ClRtWnIc.d.ts +0 -40
  52. package/dist/brand-footer-DXVGsKE5.js +0 -746
  53. package/dist/brand-sample.d.ts +0 -4
  54. package/dist/brand-sample.js +0 -2
  55. package/dist/cascade-DoLWZ66g.js +0 -701
  56. package/dist/diagnostic-model-DJtqbETd.js +0 -36
  57. package/dist/ir-KJLFW08u.js +0 -5
  58. package/dist/render-CVnXZMyK.js +0 -258
  59. package/dist/render-CuZej25A.d.ts +0 -564
  60. package/dist/theme-imports-u_P0ygvQ.js +0 -102
@@ -1,36 +0,0 @@
1
- //#region src/diagnostic-model.ts
2
- /** Newline-offset line map. Build once per source, query with `lineColumn`. */
3
- const buildLineMap = (source) => {
4
- const lineStarts = [0];
5
- for (let index = 0; index < source.length; index++) {
6
- const code = source.charCodeAt(index);
7
- if (code === 10 || code === 13 && source.charCodeAt(index + 1) !== 10 || code === 8232 || code === 8233) lineStarts.push(index + 1);
8
- }
9
- return lineStarts;
10
- };
11
- /** 1-based line/column for an absolute offset, via binary search. */
12
- const lineColumn = (lineMap, offset) => {
13
- let low = 0;
14
- let high = lineMap.length - 1;
15
- while (low < high) {
16
- const middle = low + high + 1 >> 1;
17
- if (lineMap[middle] <= offset) low = middle;
18
- else high = middle - 1;
19
- }
20
- return {
21
- line: low + 1,
22
- column: offset - lineMap[low] + 1
23
- };
24
- };
25
- /** Build one formatter for a batch of diagnostics against the same source. */
26
- const createDiagnosticFormatter = (source) => {
27
- const lineMap = buildLineMap(source);
28
- return (diagnostic) => {
29
- const { line, column } = lineColumn(lineMap, diagnostic.span.start);
30
- return `line ${line}:${column} ${diagnostic.message}`;
31
- };
32
- };
33
- /** "line 12:8 unknown class ..." — the shape agents iterate on. */
34
- const formatDiagnostic = (source, diagnostic) => createDiagnosticFormatter(source)(diagnostic);
35
- //#endregion
36
- export { lineColumn as i, createDiagnosticFormatter as n, formatDiagnostic as r, buildLineMap as t };
@@ -1,5 +0,0 @@
1
- //#region src/ir.ts
2
- /** The IR version this package writes. A publication pins the version it was built with. */
3
- const SML_IR_VERSION = 1;
4
- //#endregion
5
- export { SML_IR_VERSION as t };
@@ -1,258 +0,0 @@
1
- import { S as emailElement, o as serializeEmailHtml } from "./html-BvRFepeO.js";
2
- import { i as drainEmailDiagnostics, t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
3
- import { f as preheaderNode } from "./components-3XHjYD3i.js";
4
- import { n as applyStylesheet, r as renderDarkRules } from "./cascade-DoLWZ66g.js";
5
- import { r as hasBlockingDiagnostic, t as EmailCompileError } from "./diagnostics-C0Tr8-eQ.js";
6
- //#region src/email/text.ts
7
- const PARAGRAPH = "\n\n";
8
- const LINE = "\n";
9
- const INVISIBLE = /\u200b|\u200c|\u200d|\u200e|\u200f|\u2060|\ufeff/g;
10
- const isLayoutTable = (props) => props.role === "presentation" || props.role === "none";
11
- /**
12
- * A data-table cell that reads as nothing and holds only imagery with empty alt text carries no
13
- * column of its own; an empty cell without such an image is a positional placeholder.
14
- */
15
- const isDecorativeCell = (cell, context) => {
16
- const images = collect(cell.children, (tag) => tag === "img", () => true);
17
- return images.length > 0 && images.every((image) => image.props.alt === "") && childText(cell, context).trim() === "";
18
- };
19
- /** Wrap a block's content in separators; empty blocks contribute nothing. */
20
- const block = (inner, separator) => {
21
- const trimmed = inner.replace(/^\s+/, "").replace(/\s+$/, "");
22
- return trimmed === "" ? "" : `${separator}${trimmed}${separator}`;
23
- };
24
- const collect = (nodes, keep, descend) => nodes.flatMap((child) => {
25
- if (child.type === "Fragment") return collect(child.children, keep, descend);
26
- if (child.type !== "Element") return [];
27
- if (keep(child.tag)) return [child];
28
- return descend(child.tag) ? collect(child.children, keep, descend) : [];
29
- });
30
- const TABLE_SECTIONS = /* @__PURE__ */ new Set([
31
- "thead",
32
- "tbody",
33
- "tfoot"
34
- ]);
35
- const childText = (node, context) => node.children.map((child) => derive(child, context)).join("");
36
- const derive = (node, context) => {
37
- switch (node.type) {
38
- case "Text": return context.preformatted ? node.value : node.value.replace(INVISIBLE, "").replace(/[^\S\n]+/g, " ");
39
- case "Raw": return node.text;
40
- case "Fragment": return node.children.map((child) => derive(child, context)).join("");
41
- case "Element": {
42
- const spec = emailElement(node.tag);
43
- if (spec === void 0) return childText(node, context);
44
- switch (spec.layout) {
45
- case "metadata": return "";
46
- case "document":
47
- case "inline":
48
- case "list-item": return childText(node, context);
49
- case "break": return LINE;
50
- case "rule": return `${PARAGRAPH}---${PARAGRAPH}`;
51
- case "image": {
52
- const alt = typeof node.props.alt === "string" ? node.props.alt.trim() : "";
53
- return alt === "" ? "" : `[${alt}]`;
54
- }
55
- case "link": {
56
- const label = childText(node, context).replace(/\s+/g, " ").trim();
57
- const href = typeof node.props.href === "string" ? node.props.href : "";
58
- if (href === "" || href === label) return label;
59
- return label === "" ? href : `${label} (${href})`;
60
- }
61
- case "list": {
62
- const indent = " ".repeat(context.listDepth);
63
- const start = Number(node.props.start ?? 1);
64
- const first = Number.isFinite(start) ? start : 1;
65
- const nested = {
66
- ...context,
67
- listDepth: context.listDepth + 1
68
- };
69
- const lines = collect(node.children, (tag) => tag === "li", () => false).map((item, index) => {
70
- const marker = node.tag === "ol" ? `${first + index}.` : "-";
71
- const continuation = `${indent}${" ".repeat(marker.length + 1)}`;
72
- const [head = "", ...rest] = derive(item, nested).replace(/^\s+/, "").replace(/\s+$/, "").replace(/\n{2,}/g, LINE).split("\n");
73
- return [`${indent}${marker} ${head}`, ...rest.map((line) => line.trim() === "" ? "" : /^\s/.test(line) ? line : `${continuation}${line}`)].join(LINE);
74
- });
75
- return block(lines.join(LINE), PARAGRAPH);
76
- }
77
- case "table": {
78
- if (isLayoutTable(node.props)) return childText(node, context);
79
- const rows = collect(node.children, (tag) => tag === "tr", (tag) => TABLE_SECTIONS.has(tag)).map((row) => collect(row.children, (tag) => tag === "td" || tag === "th", () => false).filter((cell) => !isDecorativeCell(cell, context)).map((cell) => childText(cell, context).replace(/\s+/g, " ").trim()).join(" | "));
80
- return block(rows.join(LINE), PARAGRAPH);
81
- }
82
- case "row": return block(collect(node.children, (tag) => tag === "td" || tag === "th", () => false).map((cell) => childText(cell, context).replace(/^\s+|\s+$/g, "")).filter((cell) => cell !== "").join(LINE), LINE);
83
- case "table-section":
84
- case "cell": return block(childText(node, context), LINE);
85
- case "preformatted": return block(childText(node, {
86
- ...context,
87
- preformatted: true
88
- }), PARAGRAPH);
89
- case "block": return block(childText(node, context), PARAGRAPH);
90
- }
91
- }
92
- }
93
- };
94
- /** Collapse the separators the walk emits into readable plain text. */
95
- const normalize = (value) => value.replace(/\r\n?/g, "\n").split("\n").map((line) => line.replace(/[^\S\n]+$/, "")).join("\n").replace(/\n{3,}/g, PARAGRAPH).trim();
96
- /** Derive the plain-text alternative from the semantic tree in reading order. */
97
- const deriveEmailText = (node) => normalize(derive(node, {
98
- preformatted: false,
99
- listDepth: 0
100
- }));
101
- //#endregion
102
- //#region src/email/render.ts
103
- /** Classic Outlook lays a document out correctly only under a transitional doctype. */
104
- const EMAIL_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
105
- const settle = (diagnostics) => {
106
- if (hasBlockingDiagnostic(diagnostics)) throw new EmailCompileError({ diagnostics });
107
- return diagnostics;
108
- };
109
- /** Lower the derived tree to HTML and its plain-text alternative. */
110
- const compileEmail = (node, options = {}) => {
111
- const serialized = serializeEmailHtml(node, options);
112
- const diagnostics = settle([...drainEmailDiagnostics(), ...serialized.diagnostics]);
113
- return {
114
- html: serialized.html,
115
- text: deriveEmailText(node),
116
- diagnostics,
117
- positions: serialized.positions
118
- };
119
- };
120
- /** Put the hidden preview block first inside `<body>`, or first overall when there is no shell. */
121
- const withPreheader = (node, text) => {
122
- const insert = (current) => {
123
- if (current.type === "Fragment") {
124
- for (const [index, child] of current.children.entries()) {
125
- const replaced = insert(child);
126
- if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
127
- }
128
- return;
129
- }
130
- if (current.type !== "Element") return void 0;
131
- if (current.tag === "body") return EmailNode.Element({
132
- ...current,
133
- children: [preheaderNode(text), ...current.children]
134
- });
135
- for (const [index, child] of current.children.entries()) {
136
- const replaced = insert(child);
137
- if (replaced !== void 0) return EmailNode.Element({
138
- ...current,
139
- children: current.children.with(index, replaced)
140
- });
141
- }
142
- };
143
- return insert(node) ?? EmailNode.Fragment({ children: [preheaderNode(text), node] });
144
- };
145
- const headStyle = (css, origins) => EmailNode.Element({
146
- tag: "style",
147
- props: {},
148
- children: [EmailNode.Text({ value: css })],
149
- origins,
150
- authored: false
151
- });
152
- /**
153
- * Put the retained rules in a `<style>` at the end of `<head>`, and project
154
- * `@font-face` rules in a sibling classic Outlook cannot see. Outlook for
155
- * Windows registers a face it cannot load and then renders the family in Times
156
- * New Roman, ignoring the stack; hidden, the family is merely missing and the
157
- * stack's fallback applies.
158
- */
159
- const withHeadCss = (node, css, fontFaceCss) => {
160
- const insert = (current) => {
161
- if (current.type === "Fragment") {
162
- for (const [index, child] of current.children.entries()) {
163
- const replaced = insert(child);
164
- if (replaced !== void 0) return EmailNode.Fragment({ children: current.children.with(index, replaced) });
165
- }
166
- return;
167
- }
168
- if (current.type !== "Element") return void 0;
169
- if (current.tag === "head") return EmailNode.Element({
170
- ...current,
171
- children: [
172
- ...current.children,
173
- ...css === "" ? [] : [headStyle(css, current.origins)],
174
- ...fontFaceCss === "" ? [] : [
175
- EmailNode.Raw({
176
- html: "<!--[if !mso]><!-->",
177
- text: "",
178
- origins: []
179
- }),
180
- headStyle(fontFaceCss, current.origins),
181
- EmailNode.Raw({
182
- html: "<!--<![endif]-->",
183
- text: "",
184
- origins: []
185
- })
186
- ]
187
- ]
188
- });
189
- for (const [index, child] of current.children.entries()) {
190
- const replaced = insert(child);
191
- if (replaced !== void 0) return EmailNode.Element({
192
- ...current,
193
- children: current.children.with(index, replaced)
194
- });
195
- }
196
- };
197
- return insert(node);
198
- };
199
- /**
200
- * Render authored content against a stylesheet: inline what a client honors,
201
- * keep the rest for `<head>`, and lower to HTML and text.
202
- */
203
- const renderEmailContent = (stylesheet, render, options = {}) => {
204
- drainEmailDiagnostics();
205
- const content = render();
206
- const invalid = [];
207
- const fail = (code, message) => invalid.push({
208
- code,
209
- severity: "error",
210
- message,
211
- origins: []
212
- });
213
- if (typeof content.subject !== "string" || /[\r\n\u0000]/.test(content.subject)) fail("invalid-subject", "Invalid email subject: it must be one line of text.");
214
- if (content.preheader !== void 0 && typeof content.preheader !== "string") fail("invalid-preheader", "Invalid email preheader: it must be a string.");
215
- if (content.text !== void 0 && typeof content.text !== "string") fail("invalid-text", "Invalid plain text: it must be a string.");
216
- settle(invalid);
217
- const base = content.preheader === void 0 ? content.body : withPreheader(content.body, content.preheader);
218
- const styled = stylesheet === void 0 ? {
219
- tree: base,
220
- headCss: "",
221
- fontFaceCss: "",
222
- diagnostics: [],
223
- darkRules: /* @__PURE__ */ new Map()
224
- } : applyStylesheet(base, stylesheet);
225
- const headCss = `${renderDarkRules(styled.darkRules)}${styled.headCss}`;
226
- const style = [...styled.diagnostics];
227
- let tree = styled.tree;
228
- if (headCss !== "" || styled.fontFaceCss !== "") {
229
- const placed = withHeadCss(tree, headCss, styled.fontFaceCss);
230
- if (placed === void 0) style.push({
231
- code: "missing-head",
232
- severity: "error",
233
- message: "Styles that cannot be inlined — media queries, dark-scheme rules and pseudo-selectors — need a <head> to live in. Wrap the message in <Email>, or write those declarations as inline styles.",
234
- origins: []
235
- });
236
- else tree = placed;
237
- }
238
- settle(style);
239
- const compiled = compileEmail(tree, options);
240
- const prefix = compiled.html.startsWith("<html") ? EMAIL_DOCTYPE : "";
241
- const html = `${prefix}${compiled.html}`;
242
- const positions = prefix === "" ? compiled.positions : compiled.positions.map((position) => ({
243
- ...position,
244
- start: position.start + prefix.length,
245
- end: position.end + prefix.length
246
- }));
247
- return {
248
- subject: content.subject,
249
- preheader: content.preheader,
250
- html,
251
- text: content.text ?? compiled.text,
252
- tree,
253
- diagnostics: [...style, ...compiled.diagnostics],
254
- positions
255
- };
256
- };
257
- //#endregion
258
- export { deriveEmailText as i, compileEmail as n, renderEmailContent as r, EMAIL_DOCTYPE as t };