@samva/markup 0.16.0 → 0.17.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 +42 -40
- package/CHANGELOG.md +72 -0
- package/README.md +10 -9
- package/dist/{brand-jFV6YtSV.d.ts → assets-CS5Xa_mr.d.ts} +41 -3
- package/dist/{brand-Ci05p37c.js → brand-CIiEqkU7.js} +64 -3
- package/dist/{brand-plugin-CHhcei-j.d.ts → brand-plugin-Dj9SMpfS.d.ts} +3 -1
- package/dist/brand.d.ts +2 -3
- package/dist/brand.js +3 -4
- package/dist/compatibility.d.ts +76 -0
- package/dist/compatibility.js +807 -0
- package/dist/compiled-template-Bc4Dq9zy.d.ts +268 -0
- package/dist/compiler.d.ts +59 -193
- package/dist/compiler.js +473 -775
- package/dist/{components-3XHjYD3i.js → components-C5850-Qg.js} +37 -29
- package/dist/css-B_aVKrDo.js +436 -0
- package/dist/css-color-DLM12Osu.js +315 -0
- package/dist/{diagnostic-codes-8XDLR9ef.js → diagnostic-codes-DZKb0Xcg.js} +40 -1
- package/dist/{diagnostics-CUYlQ_6x.d.ts → diagnostics-D4vSLQrO.d.ts} +1 -10
- package/dist/diagnostics.d.ts +6 -2
- package/dist/diagnostics.js +3 -4
- package/dist/edit.d.ts +10 -9
- package/dist/edit.js +12 -11
- package/dist/email/jsx-dev-runtime.d.ts +1 -1
- package/dist/email/jsx-dev-runtime.js +1 -1
- package/dist/email/jsx-runtime.d.ts +2 -2
- package/dist/email/jsx-runtime.js +2 -2
- package/dist/email.d.ts +1 -1
- package/dist/email.js +1 -1
- package/dist/html-DJUK2YJF.js +483 -0
- package/dist/index.d.ts +1 -1
- package/dist/{jsx-runtime-DC7xG0aA.d.ts → jsx-runtime-DL8NVF1f.d.ts} +12 -6
- package/dist/{jsx-runtime-CBlexoeR.js → jsx-runtime-qL19u3Fy.js} +17 -10
- package/dist/preview-rNoJBEIY.d.ts +104 -0
- package/dist/{profile-BsY4dkan.js → profile-JTvgCD6X.js} +714 -29
- package/dist/{html-BvRFepeO.js → render-template-DwPDm3Hq.js} +155 -572
- package/dist/render.d.ts +40 -28
- package/dist/render.js +3 -47
- package/dist/selectors-CYWCoxZr.js +157 -0
- package/dist/sms.d.ts +1 -1
- package/dist/sms.js +1 -1
- package/dist/{template-B-UDXK8a.d.ts → template-DmNcoH4Q.d.ts} +2 -9
- package/dist/{theme-css-s9QwD5Ko.js → theme-css-P9F5S7Ic.js} +3 -316
- package/dist/whatsapp.d.ts +1 -1
- package/dist/whatsapp.js +1 -1
- package/generated/contract.json +7 -2
- package/generated/reference.md +3 -2
- package/package.json +5 -5
- package/skills/sml/SKILL.md +2 -2
- package/dist/assets-BSpgZOSR.js +0 -61
- package/dist/assets-BSuNT9gr.d.ts +0 -39
- package/dist/brand-footer-ClRtWnIc.d.ts +0 -40
- package/dist/brand-footer-DXVGsKE5.js +0 -746
- package/dist/brand-sample.d.ts +0 -4
- package/dist/brand-sample.js +0 -2
- package/dist/cascade-DoLWZ66g.js +0 -701
- package/dist/diagnostic-model-DJtqbETd.js +0 -36
- package/dist/ir-KJLFW08u.js +0 -5
- package/dist/render-CVnXZMyK.js +0 -258
- package/dist/render-CuZej25A.d.ts +0 -564
- package/dist/theme-imports-u_P0ygvQ.js +0 -102
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
//#region src/email/css-color.ts
|
|
2
|
+
const clamp = (value, low = 0, high = 1) => value < low ? low : value > high ? high : value;
|
|
3
|
+
const toGamma = (channel) => channel <= .0031308 ? 12.92 * channel : 1.055 * channel ** (1 / 2.4) - .055;
|
|
4
|
+
const toLinear = (channel) => channel <= .04045 ? channel / 12.92 : ((channel + .055) / 1.055) ** 2.4;
|
|
5
|
+
/** OKLab is the interpolation space Tailwind mixes in, so conversions run both ways. */
|
|
6
|
+
const oklabToRgb = (lightness, a, b, alpha) => {
|
|
7
|
+
const l = (lightness + .3963377774 * a + .2158037573 * b) ** 3;
|
|
8
|
+
const m = (lightness - .1055613458 * a - .0638541728 * b) ** 3;
|
|
9
|
+
const s = (lightness - .0894841775 * a - 1.291485548 * b) ** 3;
|
|
10
|
+
return {
|
|
11
|
+
r: toGamma(4.0767416621 * l - 3.3077115913 * m + .2309699292 * s),
|
|
12
|
+
g: toGamma(-1.2684380046 * l + 2.6097574011 * m - .3413193965 * s),
|
|
13
|
+
b: toGamma(-.0041960863 * l - .7034186147 * m + 1.707614701 * s),
|
|
14
|
+
a: alpha
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const rgbToOklab = (color) => {
|
|
18
|
+
const r = toLinear(color.r);
|
|
19
|
+
const g = toLinear(color.g);
|
|
20
|
+
const b = toLinear(color.b);
|
|
21
|
+
const l = Math.cbrt(.4122214708 * r + .5363325363 * g + .0514459929 * b);
|
|
22
|
+
const m = Math.cbrt(.2119034982 * r + .6806995451 * g + .1073969566 * b);
|
|
23
|
+
const s = Math.cbrt(.0883024619 * r + .2817188376 * g + .6299787005 * b);
|
|
24
|
+
return {
|
|
25
|
+
L: .2104542553 * l + .793617785 * m - .0040720468 * s,
|
|
26
|
+
a: 1.9779984951 * l - 2.428592205 * m + .4505937099 * s,
|
|
27
|
+
b: .0259040371 * l + .7827717662 * m - .808675766 * s,
|
|
28
|
+
alpha: color.a
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const hslToRgb = (hue, saturation, lightness, alpha) => {
|
|
32
|
+
const h = (hue % 360 + 360) % 360;
|
|
33
|
+
const c = (1 - Math.abs(2 * lightness - 1)) * saturation;
|
|
34
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
35
|
+
const m = lightness - c / 2;
|
|
36
|
+
const [r, g, b] = [
|
|
37
|
+
[
|
|
38
|
+
c,
|
|
39
|
+
x,
|
|
40
|
+
0
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
x,
|
|
44
|
+
c,
|
|
45
|
+
0
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
0,
|
|
49
|
+
c,
|
|
50
|
+
x
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
0,
|
|
54
|
+
x,
|
|
55
|
+
c
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
x,
|
|
59
|
+
0,
|
|
60
|
+
c
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
c,
|
|
64
|
+
0,
|
|
65
|
+
x
|
|
66
|
+
]
|
|
67
|
+
][Math.min(5, Math.floor(h / 60))];
|
|
68
|
+
return {
|
|
69
|
+
r: r + m,
|
|
70
|
+
g: g + m,
|
|
71
|
+
b: b + m,
|
|
72
|
+
a: alpha
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/** The named colors an email template actually reaches for; anything else stays literal. */
|
|
76
|
+
const NAMED = {
|
|
77
|
+
transparent: "#00000000",
|
|
78
|
+
black: "#000000",
|
|
79
|
+
white: "#ffffff",
|
|
80
|
+
red: "#ff0000",
|
|
81
|
+
green: "#008000",
|
|
82
|
+
blue: "#0000ff",
|
|
83
|
+
gray: "#808080",
|
|
84
|
+
grey: "#808080",
|
|
85
|
+
silver: "#c0c0c0",
|
|
86
|
+
yellow: "#ffff00",
|
|
87
|
+
orange: "#ffa500",
|
|
88
|
+
purple: "#800080",
|
|
89
|
+
navy: "#000080",
|
|
90
|
+
teal: "#008080",
|
|
91
|
+
olive: "#808000",
|
|
92
|
+
maroon: "#800000",
|
|
93
|
+
lime: "#00ff00",
|
|
94
|
+
aqua: "#00ffff",
|
|
95
|
+
cyan: "#00ffff",
|
|
96
|
+
fuchsia: "#ff00ff",
|
|
97
|
+
magenta: "#ff00ff"
|
|
98
|
+
};
|
|
99
|
+
const number = (token, reference = 1) => {
|
|
100
|
+
const text = token.trim();
|
|
101
|
+
if (text === "none") return 0;
|
|
102
|
+
if (text.endsWith("%")) {
|
|
103
|
+
const value = Number.parseFloat(text.slice(0, -1));
|
|
104
|
+
return Number.isFinite(value) ? value / 100 * reference : void 0;
|
|
105
|
+
}
|
|
106
|
+
const value = Number.parseFloat(text);
|
|
107
|
+
return Number.isFinite(value) ? value : void 0;
|
|
108
|
+
};
|
|
109
|
+
const angle = (token) => {
|
|
110
|
+
const text = token.trim();
|
|
111
|
+
const value = Number.parseFloat(text);
|
|
112
|
+
if (!Number.isFinite(value)) return void 0;
|
|
113
|
+
if (text.endsWith("turn")) return value * 360;
|
|
114
|
+
if (text.endsWith("rad")) return value * 180 / Math.PI;
|
|
115
|
+
if (text.endsWith("grad")) return value * .9;
|
|
116
|
+
return value;
|
|
117
|
+
};
|
|
118
|
+
/** Split a function's arguments on top-level commas and `/`, keeping nested calls intact. */
|
|
119
|
+
const argumentTokens = (body) => {
|
|
120
|
+
const parts = [];
|
|
121
|
+
let alpha;
|
|
122
|
+
let depth = 0;
|
|
123
|
+
let current = "";
|
|
124
|
+
let afterSlash = false;
|
|
125
|
+
for (const character of body) {
|
|
126
|
+
if (character === "(") depth += 1;
|
|
127
|
+
if (character === ")") depth -= 1;
|
|
128
|
+
if (depth === 0 && (character === "," || character === " " || character === "/")) {
|
|
129
|
+
if (character === "/") {
|
|
130
|
+
if (current.trim() !== "") parts.push(current.trim());
|
|
131
|
+
current = "";
|
|
132
|
+
afterSlash = true;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (current.trim() !== "") {
|
|
136
|
+
if (afterSlash) alpha = current.trim();
|
|
137
|
+
else parts.push(current.trim());
|
|
138
|
+
}
|
|
139
|
+
current = "";
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
current += character;
|
|
143
|
+
}
|
|
144
|
+
if (current.trim() !== "") {
|
|
145
|
+
if (afterSlash) alpha = current.trim();
|
|
146
|
+
else parts.push(current.trim());
|
|
147
|
+
}
|
|
148
|
+
return alpha === void 0 ? { parts } : {
|
|
149
|
+
parts,
|
|
150
|
+
alpha
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
/** Read one function call at the start of `value`, returning its name and body. */
|
|
154
|
+
const functionCall = (value) => {
|
|
155
|
+
const match = /^([a-zA-Z-]+)\(/.exec(value.trim());
|
|
156
|
+
if (match === null) return void 0;
|
|
157
|
+
const text = value.trim();
|
|
158
|
+
let depth = 0;
|
|
159
|
+
for (let index = match[0].length - 1; index < text.length; index++) if (text[index] === "(") depth += 1;
|
|
160
|
+
else if (text[index] === ")") {
|
|
161
|
+
depth -= 1;
|
|
162
|
+
if (depth === 0) return {
|
|
163
|
+
name: match[1].toLowerCase(),
|
|
164
|
+
body: text.slice(match[0].length, index)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
/** Parse any supported color notation into extended sRGB. `undefined` means "leave it alone". */
|
|
169
|
+
const parseColor = (value) => {
|
|
170
|
+
const text = value.trim();
|
|
171
|
+
const named = NAMED[text.toLowerCase()];
|
|
172
|
+
if (named !== void 0) return parseColor(named);
|
|
173
|
+
if (text.startsWith("#")) {
|
|
174
|
+
const hex = text.slice(1);
|
|
175
|
+
const expand = (part) => Number.parseInt(part.repeat(2 / part.length), 16) / 255;
|
|
176
|
+
if (hex.length === 3 || hex.length === 4) return {
|
|
177
|
+
r: expand(hex[0]),
|
|
178
|
+
g: expand(hex[1]),
|
|
179
|
+
b: expand(hex[2]),
|
|
180
|
+
a: hex.length === 4 ? expand(hex[3]) : 1
|
|
181
|
+
};
|
|
182
|
+
if (hex.length === 6 || hex.length === 8) return {
|
|
183
|
+
r: Number.parseInt(hex.slice(0, 2), 16) / 255,
|
|
184
|
+
g: Number.parseInt(hex.slice(2, 4), 16) / 255,
|
|
185
|
+
b: Number.parseInt(hex.slice(4, 6), 16) / 255,
|
|
186
|
+
a: hex.length === 8 ? Number.parseInt(hex.slice(6, 8), 16) / 255 : 1
|
|
187
|
+
};
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const call = functionCall(text);
|
|
191
|
+
if (call === void 0) return void 0;
|
|
192
|
+
const { parts, alpha } = argumentTokens(call.body);
|
|
193
|
+
const opacity = alpha === void 0 ? 1 : number(alpha) ?? 1;
|
|
194
|
+
switch (call.name) {
|
|
195
|
+
case "rgb":
|
|
196
|
+
case "rgba": {
|
|
197
|
+
const [r, g, b, legacy] = parts;
|
|
198
|
+
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
199
|
+
const channel = (token) => {
|
|
200
|
+
const parsed = number(token, 255);
|
|
201
|
+
return parsed === void 0 ? void 0 : parsed / 255;
|
|
202
|
+
};
|
|
203
|
+
const [red, green, blue] = [
|
|
204
|
+
channel(r),
|
|
205
|
+
channel(g),
|
|
206
|
+
channel(b)
|
|
207
|
+
];
|
|
208
|
+
if (red === void 0 || green === void 0 || blue === void 0) return void 0;
|
|
209
|
+
return {
|
|
210
|
+
r: red,
|
|
211
|
+
g: green,
|
|
212
|
+
b: blue,
|
|
213
|
+
a: legacy === void 0 ? opacity : number(legacy) ?? 1
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
case "hsl":
|
|
217
|
+
case "hsla": {
|
|
218
|
+
const [h, s, l, legacy] = parts;
|
|
219
|
+
if (h === void 0 || s === void 0 || l === void 0) return void 0;
|
|
220
|
+
const hue = angle(h);
|
|
221
|
+
const saturation = number(s);
|
|
222
|
+
const lightness = number(l);
|
|
223
|
+
if (hue === void 0 || saturation === void 0 || lightness === void 0) return void 0;
|
|
224
|
+
return hslToRgb(hue, saturation, lightness, legacy === void 0 ? opacity : number(legacy) ?? 1);
|
|
225
|
+
}
|
|
226
|
+
case "oklch": {
|
|
227
|
+
const [l, c, h] = parts;
|
|
228
|
+
if (l === void 0 || c === void 0 || h === void 0) return void 0;
|
|
229
|
+
const lightness = number(l);
|
|
230
|
+
const chroma = number(c, .4);
|
|
231
|
+
const hue = angle(h);
|
|
232
|
+
if (lightness === void 0 || chroma === void 0 || hue === void 0) return void 0;
|
|
233
|
+
const radians = hue * Math.PI / 180;
|
|
234
|
+
return oklabToRgb(lightness, chroma * Math.cos(radians), chroma * Math.sin(radians), opacity);
|
|
235
|
+
}
|
|
236
|
+
case "oklab": {
|
|
237
|
+
const [l, a, b] = parts;
|
|
238
|
+
if (l === void 0 || a === void 0 || b === void 0) return void 0;
|
|
239
|
+
const lightness = number(l);
|
|
240
|
+
const green = number(a, .4);
|
|
241
|
+
const blue = number(b, .4);
|
|
242
|
+
if (lightness === void 0 || green === void 0 || blue === void 0) return void 0;
|
|
243
|
+
return oklabToRgb(lightness, green, blue, opacity);
|
|
244
|
+
}
|
|
245
|
+
case "color-mix": return mixColors(call.body);
|
|
246
|
+
default: return;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
/** `color-mix(in <space>, <color> <pct>?, <color> <pct>?)` — Tailwind's opacity modifier. */
|
|
250
|
+
const mixColors = (body) => {
|
|
251
|
+
const stops = body.split(",").map((part) => part.trim());
|
|
252
|
+
const [space, first, second] = stops;
|
|
253
|
+
if (space === void 0 || first === void 0 || second === void 0 || stops.length !== 3) return void 0;
|
|
254
|
+
const method = /^in\s+([a-z-]+)/i.exec(space)?.[1]?.toLowerCase();
|
|
255
|
+
if (method === void 0) return void 0;
|
|
256
|
+
const stop = (text) => {
|
|
257
|
+
const percentage = /\s([\d.]+)%$/.exec(text);
|
|
258
|
+
const color = parseColor(percentage === null ? text : text.slice(0, percentage.index));
|
|
259
|
+
if (color === void 0) return void 0;
|
|
260
|
+
return {
|
|
261
|
+
color,
|
|
262
|
+
weight: percentage === null ? void 0 : Number.parseFloat(percentage[1]) / 100
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const left = stop(first);
|
|
266
|
+
const right = stop(second);
|
|
267
|
+
if (left === void 0 || right === void 0) return void 0;
|
|
268
|
+
const leftWeight = left.weight ?? (right.weight === void 0 ? .5 : 1 - right.weight);
|
|
269
|
+
const total = leftWeight + (right.weight ?? 1 - leftWeight);
|
|
270
|
+
if (total === 0) return void 0;
|
|
271
|
+
const p = leftWeight / total;
|
|
272
|
+
const alpha = left.color.a * p + right.color.a * (1 - p);
|
|
273
|
+
const mix = (l, r) => alpha === 0 ? 0 : (l * left.color.a * p + r * right.color.a * (1 - p)) / alpha;
|
|
274
|
+
if (method === "srgb") return {
|
|
275
|
+
r: mix(left.color.r, right.color.r),
|
|
276
|
+
g: mix(left.color.g, right.color.g),
|
|
277
|
+
b: mix(left.color.b, right.color.b),
|
|
278
|
+
a: alpha
|
|
279
|
+
};
|
|
280
|
+
const l = rgbToOklab(left.color);
|
|
281
|
+
const r = rgbToOklab(right.color);
|
|
282
|
+
const channel = (a, b) => alpha === 0 ? 0 : (a * left.color.a * p + b * right.color.a * (1 - p)) / alpha;
|
|
283
|
+
return oklabToRgb(channel(l.L, r.L), channel(l.a, r.a), channel(l.b, r.b), alpha);
|
|
284
|
+
};
|
|
285
|
+
const byte = (channel) => Math.round(clamp(channel) * 255).toString(16).padStart(2, "0");
|
|
286
|
+
/** sRGB hex, or `rgba()` when the color is not opaque. */
|
|
287
|
+
const formatColor = (color) => color.a >= 1 ? `#${byte(color.r)}${byte(color.g)}${byte(color.b)}` : `rgba(${Math.round(clamp(color.r) * 255)},${Math.round(clamp(color.g) * 255)},${Math.round(clamp(color.b) * 255)},${Math.round(clamp(color.a) * 1e3) / 1e3})`;
|
|
288
|
+
const MODERN_COLOR = /\b(oklch|oklab|color-mix|lab|lch)\(/i;
|
|
289
|
+
/** Rewrite every modern color function in a declaration value; other text is untouched. */
|
|
290
|
+
const downlevelColors = (value) => {
|
|
291
|
+
if (!MODERN_COLOR.test(value)) return value;
|
|
292
|
+
let result = "";
|
|
293
|
+
let rest = value;
|
|
294
|
+
while (rest.length > 0) {
|
|
295
|
+
const match = MODERN_COLOR.exec(rest);
|
|
296
|
+
if (match === null || match.index === void 0) {
|
|
297
|
+
result += rest;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
result += rest.slice(0, match.index);
|
|
301
|
+
const remainder = rest.slice(match.index);
|
|
302
|
+
const call = functionCall(remainder);
|
|
303
|
+
if (call === void 0) {
|
|
304
|
+
result += remainder;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
const consumed = remainder.indexOf("(") + call.body.length + 2;
|
|
308
|
+
const color = parseColor(remainder.slice(0, consumed));
|
|
309
|
+
result += color === void 0 ? remainder.slice(0, consumed) : formatColor(color);
|
|
310
|
+
rest = remainder.slice(consumed);
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
};
|
|
314
|
+
//#endregion
|
|
315
|
+
export { parseColor as n, downlevelColors as t };
|
|
@@ -1,3 +1,38 @@
|
|
|
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
|
|
1
36
|
//#region src/diagnostic-codes.ts
|
|
2
37
|
const codes = (table) => table;
|
|
3
38
|
const DIAGNOSTIC_CODES = codes({
|
|
@@ -341,6 +376,10 @@ const DIAGNOSTIC_CODES = codes({
|
|
|
341
376
|
summary: "@font-face names a file the project does not hold.",
|
|
342
377
|
fix: "Add the file; paths resolve from the stylesheet's directory."
|
|
343
378
|
},
|
|
379
|
+
"email-external-asset": {
|
|
380
|
+
summary: "An image points at another site's URL; a hotlinked image can disappear, be blocked, track recipients or slow the message.",
|
|
381
|
+
fix: "Add the image to the project's files and import it (import logo from './logo.png'), then use the import as the src."
|
|
382
|
+
},
|
|
344
383
|
"invalid-asset-base": {
|
|
345
384
|
summary: "The asset base is not an https URL or a path.",
|
|
346
385
|
fix: "Use an https URL, an absolute path, or a relative path."
|
|
@@ -431,4 +470,4 @@ const DIAGNOSTIC_CODE_FAMILIES = { "caniemail/": {
|
|
|
431
470
|
fix: "Use the alternative the message names, such as an email component instead of display: flex."
|
|
432
471
|
} };
|
|
433
472
|
//#endregion
|
|
434
|
-
export { DIAGNOSTIC_CODE_FAMILIES as n, hasUpgradeRecipe as r, DIAGNOSTIC_CODES as t };
|
|
473
|
+
export { createDiagnosticFormatter as a, buildLineMap as i, DIAGNOSTIC_CODE_FAMILIES as n, formatDiagnostic as o, hasUpgradeRecipe as r, lineColumn as s, DIAGNOSTIC_CODES as t };
|
|
@@ -34,18 +34,9 @@ interface TemplateDiagnostic extends EmailDiagnostic {
|
|
|
34
34
|
/** Agent instructions for migrating a template made incompatible by a compiler change. */
|
|
35
35
|
readonly upgrade?: string | undefined;
|
|
36
36
|
}
|
|
37
|
-
declare const EmailCompileError_base: import("better-result").TaggedErrorClass<"EmailCompileError">;
|
|
38
|
-
declare class EmailCompileError extends EmailCompileError_base<{
|
|
39
|
-
readonly diagnostics: ReadonlyArray<EmailDiagnostic>;
|
|
40
|
-
readonly message: string;
|
|
41
|
-
}> {
|
|
42
|
-
constructor(args: {
|
|
43
|
-
readonly diagnostics: ReadonlyArray<EmailDiagnostic>;
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
37
|
/** `emails/welcome.tsx:12:3 unsupported-element: …` — the shape agents iterate on. */
|
|
47
38
|
declare const formatEmailDiagnostic: (diagnostic: TemplateDiagnostic) => string;
|
|
48
39
|
/** True when a set of findings must stop the compile or refuse a publication. */
|
|
49
40
|
declare const hasBlockingDiagnostic: (diagnostics: readonly EmailDiagnostic[]) => boolean;
|
|
50
41
|
//#endregion
|
|
51
|
-
export {
|
|
42
|
+
export { formatEmailDiagnostic as a, TemplateDiagnostic as i, EmailDiagnostic as n, hasBlockingDiagnostic as o, EmailDiagnosticSeverity as r, EmailCompatibilityAssessment as t };
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as formatEmailDiagnostic, i as TemplateDiagnostic, n as EmailDiagnostic, o as hasBlockingDiagnostic, r as EmailDiagnosticSeverity, t as EmailCompatibilityAssessment } from "./diagnostics-D4vSLQrO.js";
|
|
2
2
|
import { a as createDiagnosticFormatter, i as buildLineMap, n as DiagnosticRepair, o as formatDiagnostic, r as Span, s as lineColumn, t as Diagnostic } from "./diagnostic-model-DxthUZBT.js";
|
|
3
3
|
//#region src/diagnostic-codes.d.ts
|
|
4
4
|
interface DiagnosticCodeSpec {
|
|
@@ -340,6 +340,10 @@ declare const DIAGNOSTIC_CODES: {
|
|
|
340
340
|
readonly summary: "@font-face names a file the project does not hold.";
|
|
341
341
|
readonly fix: "Add the file; paths resolve from the stylesheet's directory.";
|
|
342
342
|
};
|
|
343
|
+
readonly "email-external-asset": {
|
|
344
|
+
readonly summary: "An image points at another site's URL; a hotlinked image can disappear, be blocked, track recipients or slow the message.";
|
|
345
|
+
readonly fix: "Add the image to the project's files and import it (import logo from './logo.png'), then use the import as the src.";
|
|
346
|
+
};
|
|
343
347
|
readonly "invalid-asset-base": {
|
|
344
348
|
readonly summary: "The asset base is not an https URL or a path.";
|
|
345
349
|
readonly fix: "Use an https URL, an absolute path, or a relative path.";
|
|
@@ -429,4 +433,4 @@ declare const DIAGNOSTIC_CODE_FAMILIES: {
|
|
|
429
433
|
};
|
|
430
434
|
};
|
|
431
435
|
//#endregion
|
|
432
|
-
export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, Diagnostic, DiagnosticCode, DiagnosticCodeSpec, DiagnosticRepair, EmailCompatibilityAssessment,
|
|
436
|
+
export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, Diagnostic, DiagnosticCode, DiagnosticCodeSpec, DiagnosticRepair, type EmailCompatibilityAssessment, type EmailDiagnostic, type EmailDiagnosticSeverity, Span, type TemplateDiagnostic, buildLineMap, createDiagnosticFormatter, formatDiagnostic, formatEmailDiagnostic, hasBlockingDiagnostic, hasUpgradeRecipe, lineColumn };
|
package/dist/diagnostics.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
|
|
4
|
-
export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, EmailCompileError, buildLineMap, createDiagnosticFormatter, formatDiagnostic, formatEmailDiagnostic, hasBlockingDiagnostic, hasUpgradeRecipe, lineColumn };
|
|
1
|
+
import { n as formatEmailDiagnostic, r as hasBlockingDiagnostic } from "./diagnostics-C0Tr8-eQ.js";
|
|
2
|
+
import { a as createDiagnosticFormatter, i as buildLineMap, n as DIAGNOSTIC_CODE_FAMILIES, o as formatDiagnostic, r as hasUpgradeRecipe, s as lineColumn, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-DZKb0Xcg.js";
|
|
3
|
+
export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, buildLineMap, createDiagnosticFormatter, formatDiagnostic, formatEmailDiagnostic, hasBlockingDiagnostic, hasUpgradeRecipe, lineColumn };
|
package/dist/edit.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { i as completeJsxSourceLocation, n as JsxSource, r as JsxSourceLocation, t as JSX_SOURCE_LOCATION_PROP } from "./source-locations-C8wCsiAy.js";
|
|
2
|
+
import { a as emailAuthoringTarget, c as emailSelectionIndex, d as previewEmail, i as INSTANCE_PATH_ATTRIBUTE, l as emailSelectionSiblings, n as EmailPreview, o as emailDefinitionInstances, r as EmittedPosition, s as emailSelectionAncestry, t as EmailElementSelection, u as emailSelections } from "./preview-rNoJBEIY.js";
|
|
2
3
|
import { n as WhatsAppCategory, r as WhatsAppHeaderType, t as SmsCategory } from "./channel-specs-C2feJwuP.js";
|
|
3
4
|
//#region src/source-edit.d.ts
|
|
4
5
|
/** One exact source replacement; before text guards against concurrent changes. */
|
|
@@ -163,12 +164,12 @@ type WhatsAppButtonRow = {
|
|
|
163
164
|
interface WhatsAppChannelForm {
|
|
164
165
|
readonly language: AttributeState;
|
|
165
166
|
readonly category: AttributeState;
|
|
166
|
-
/**
|
|
167
|
-
readonly header: readonly ChannelSegment[] |
|
|
167
|
+
/** Undefined when the template has no header. */
|
|
168
|
+
readonly header: readonly ChannelSegment[] | undefined;
|
|
168
169
|
readonly headerType: AttributeState;
|
|
169
170
|
readonly body: readonly ChannelSegment[];
|
|
170
|
-
/**
|
|
171
|
-
readonly footer: readonly ChannelSegment[] |
|
|
171
|
+
/** Undefined when the template has no footer. */
|
|
172
|
+
readonly footer: readonly ChannelSegment[] | undefined;
|
|
172
173
|
readonly buttons: readonly WhatsAppButtonRow[];
|
|
173
174
|
readonly variables: readonly string[];
|
|
174
175
|
}
|
|
@@ -220,13 +221,13 @@ type WhatsAppEdit = {
|
|
|
220
221
|
readonly kind: "buttonRemove";
|
|
221
222
|
readonly button: number;
|
|
222
223
|
};
|
|
223
|
-
/** The form model of the template's SMS body, or
|
|
224
|
-
declare const readSmsChannel: (source: string) => SmsChannelForm |
|
|
224
|
+
/** The form model of the template's SMS body, or undefined when it is not a `<Sms>` written in place. */
|
|
225
|
+
declare const readSmsChannel: (source: string) => SmsChannelForm | undefined;
|
|
225
226
|
/** The source replacements that apply one form change to the SMS body. */
|
|
226
227
|
declare const editSmsChannel: (source: string, edit: SmsEdit) => ChannelEditResult;
|
|
227
|
-
/** The form model of the template's WhatsApp body, or
|
|
228
|
-
declare const readWhatsAppChannel: (source: string) => WhatsAppChannelForm |
|
|
228
|
+
/** The form model of the template's WhatsApp body, or undefined when it is not a `<WhatsApp>` written in place. */
|
|
229
|
+
declare const readWhatsAppChannel: (source: string) => WhatsAppChannelForm | undefined;
|
|
229
230
|
/** The source replacements that apply one form change to the WhatsApp body. */
|
|
230
231
|
declare const editWhatsAppChannel: (source: string, edit: WhatsAppEdit) => ChannelEditResult;
|
|
231
232
|
//#endregion
|
|
232
|
-
export { ApplyOptions, AttributeState, ChannelEditResult, ChannelSegment, ClassNameOperation, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, LockedSegment, ProfileGuard, type SmsCategory, SmsChannelForm, SmsEdit, SourceEditResult, SourceElement, SourceFixture, SourceProperty, SourceProposal, SourceRange, SourceReplacement, TextSegment, WhatsAppButtonKind, WhatsAppButtonRow, type WhatsAppCategory, WhatsAppChannelForm, WhatsAppEdit, type WhatsAppHeaderType, WhatsAppPart, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
233
|
+
export { ApplyOptions, AttributeState, ChannelEditResult, ChannelSegment, ClassNameOperation, type EmailElementSelection, type EmailPreview, type EmittedPosition, INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, LockedSegment, ProfileGuard, type SmsCategory, SmsChannelForm, SmsEdit, SourceEditResult, SourceElement, SourceFixture, SourceProperty, SourceProposal, SourceRange, SourceReplacement, TextSegment, WhatsAppButtonKind, WhatsAppButtonRow, type WhatsAppCategory, WhatsAppChannelForm, WhatsAppEdit, type WhatsAppHeaderType, WhatsAppPart, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
package/dist/edit.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { n as isObject } from "./guards-eV1qLbf2.js";
|
|
2
|
-
import { d as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { c as INSTANCE_PATH_ATTRIBUTE, d as emailSelectionAncestry, f as emailSelectionIndex, h as previewEmail, l as emailAuthoringTarget, m as emailSelections, p as emailSelectionSiblings, u as emailDefinitionInstances } from "./html-DJUK2YJF.js";
|
|
3
|
+
import { d as completeJsxSourceLocation, u as JSX_SOURCE_LOCATION_PROP } from "./jsx-runtime-qL19u3Fy.js";
|
|
4
|
+
import { i as buildLineMap } from "./diagnostic-codes-DZKb0Xcg.js";
|
|
5
|
+
import { c as SMS_CATEGORIES, l as WHATSAPP_CATEGORIES, n as introducedErrors, t as checkTemplate, u as WHATSAPP_HEADER_TYPES } from "./profile-JTvgCD6X.js";
|
|
5
6
|
import { parse } from "@babel/parser";
|
|
6
7
|
//#region src/source-edit.ts
|
|
7
8
|
const node = (value) => isObject(value) && "type" in value && typeof value.type === "string" && "start" in value && typeof value.start === "number" && "end" in value && typeof value.end === "number";
|
|
@@ -146,7 +147,7 @@ const profileRefusal = (source, result, guard) => {
|
|
|
146
147
|
...guard.files,
|
|
147
148
|
[file]: content
|
|
148
149
|
});
|
|
149
|
-
const introduced =
|
|
150
|
+
const introduced = introducedErrors(checkTemplate(project(source), { entry: guard.entry }), checkTemplate(project(result), { entry: guard.entry }));
|
|
150
151
|
const first = introduced[0];
|
|
151
152
|
if (first === void 0) return null;
|
|
152
153
|
const more = introduced.length > 1 ? ` (and ${introduced.length - 1} more)` : "";
|
|
@@ -700,10 +701,10 @@ const smsChannel = (source) => {
|
|
|
700
701
|
tree
|
|
701
702
|
};
|
|
702
703
|
};
|
|
703
|
-
/** The form model of the template's SMS body, or
|
|
704
|
+
/** The form model of the template's SMS body, or undefined when it is not a `<Sms>` written in place. */
|
|
704
705
|
const readSmsChannel = (source) => {
|
|
705
706
|
const channel = smsChannel(source);
|
|
706
|
-
if (channel === null) return
|
|
707
|
+
if (channel === null) return void 0;
|
|
707
708
|
return {
|
|
708
709
|
category: attributeState(source, channel.root, "category"),
|
|
709
710
|
segments: segmentsOf(source, channel.root),
|
|
@@ -800,18 +801,18 @@ const buttonRows = (source, channel) => {
|
|
|
800
801
|
}
|
|
801
802
|
return rows;
|
|
802
803
|
};
|
|
803
|
-
/** The form model of the template's WhatsApp body, or
|
|
804
|
+
/** The form model of the template's WhatsApp body, or undefined when it is not a `<WhatsApp>` written in place. */
|
|
804
805
|
const readWhatsAppChannel = (source) => {
|
|
805
806
|
const channel = whatsappChannel(source);
|
|
806
|
-
if (channel === null || channel.body === null) return
|
|
807
|
+
if (channel === null || channel.body === null) return void 0;
|
|
807
808
|
const { root } = channel;
|
|
808
809
|
return {
|
|
809
810
|
language: attributeState(source, root, "language"),
|
|
810
811
|
category: attributeState(source, root, "category"),
|
|
811
|
-
header: channel.header === null ?
|
|
812
|
+
header: channel.header === null ? void 0 : segmentsOf(source, channel.header),
|
|
812
813
|
headerType: channel.header === null ? { kind: "absent" } : attributeState(source, channel.header, "type"),
|
|
813
814
|
body: segmentsOf(source, channel.body),
|
|
814
|
-
footer: channel.footer === null ?
|
|
815
|
+
footer: channel.footer === null ? void 0 : segmentsOf(source, channel.footer),
|
|
815
816
|
buttons: buttonRows(source, channel).map(({ row }) => row),
|
|
816
817
|
variables: variablesOf(source, root, channel.parameter)
|
|
817
818
|
};
|
|
@@ -939,4 +940,4 @@ const editWhatsAppChannel = (source, edit) => {
|
|
|
939
940
|
}
|
|
940
941
|
};
|
|
941
942
|
//#endregion
|
|
942
|
-
export { JSX_SOURCE_LOCATION_PROP, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
943
|
+
export { INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as JsxSource } from "../source-locations-C8wCsiAy.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as Fragment, i as EmailNode, n as EmailComponent, o as JSX } from "../jsx-runtime-DL8NVF1f.js";
|
|
3
3
|
//#region src/email/jsx-dev-runtime.d.ts
|
|
4
4
|
/** Development JSX retains compiler-provided origins directly on the semantic tree. */
|
|
5
5
|
declare const jsxDEV: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null, _key?: unknown, _staticChildren?: boolean, source?: JsxSource) => EmailNode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as jsx, n as Fragment, u as JSX_SOURCE_LOCATION_PROP } from "../jsx-runtime-
|
|
1
|
+
import { a as jsx, n as Fragment, u as JSX_SOURCE_LOCATION_PROP } from "../jsx-runtime-qL19u3Fy.js";
|
|
2
2
|
//#region src/email/jsx-dev-runtime.ts
|
|
3
3
|
/** Development JSX retains compiler-provided origins directly on the semantic tree. */
|
|
4
4
|
const jsxDEV = (type, props, _key, _staticChildren, source) => jsx(type, {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { EmailChild, EmailComponent, EmailNode, Fragment, JSX,
|
|
1
|
+
import { a as Fragment, c as currentEmailOrigins, d as normalizeChildren, f as rawMarkup, i as EmailNode, l as jsx, n as EmailComponent, o as JSX, p as withEmailDiagnostics, r as EmailFinding, s as collectEmailDiagnostics, t as EmailChild, u as jsxs } from "../jsx-runtime-DL8NVF1f.js";
|
|
2
|
+
export { EmailChild, EmailComponent, EmailFinding, EmailNode, Fragment, JSX, collectEmailDiagnostics, currentEmailOrigins, jsx, jsxs, normalizeChildren, rawMarkup, withEmailDiagnostics };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as jsx, c as rawMarkup, i as
|
|
2
|
-
export { EmailNode, Fragment,
|
|
1
|
+
import { a as jsx, c as rawMarkup, i as currentEmailOrigins, l as withEmailDiagnostics, n as Fragment, o as jsxs, r as collectEmailDiagnostics, s as normalizeChildren, t as EmailNode } from "../jsx-runtime-qL19u3Fy.js";
|
|
2
|
+
export { EmailNode, Fragment, collectEmailDiagnostics, currentEmailOrigins, jsx, jsxs, normalizeChildren, rawMarkup, withEmailDiagnostics };
|
package/dist/email.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as EmailNode, t as EmailChild, x as EmailStyle } from "./jsx-runtime-DL8NVF1f.js";
|
|
2
2
|
//#region src/email/components.d.ts
|
|
3
3
|
/** Marks the hidden preview block so the renderer can read it back and the text lane can keep it. */
|
|
4
4
|
declare const PREHEADER_ATTRIBUTE = "data-samva-preheader";
|
package/dist/email.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-
|
|
1
|
+
import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-C5850-Qg.js";
|
|
2
2
|
export { Button, Column, Columns, Divider, Email, Image, Link, PREHEADER_ATTRIBUTE, Section, Spacer, equalColumnWidth, preheaderNode };
|