@samva/markup 0.2.0 → 0.3.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/dist/compiler.d.ts +7 -5
- package/dist/compiler.js +7 -6
- package/dist/{corpus-Dz5irJ9x.d.ts → corpus-qrUD5O6r.d.ts} +96 -3
- package/dist/{stylesheets-oDRAnYA3.d.ts → css-ar2lU3NC.d.ts} +2 -12
- package/dist/email/components.d.ts +8 -2
- package/dist/email/components.js +8 -5
- package/dist/email/render.d.ts +8 -1
- package/dist/email/render.js +2 -2
- package/dist/email/stylesheets.d.ts +15 -2
- package/dist/email/stylesheets.js +3 -1
- package/dist/email/text.js +9 -1
- package/dist/presets/corpus.d.ts +2 -2
- package/dist/presets/corpus.js +65 -4
- package/dist/presets/public-gallery.d.ts +23 -5
- package/dist/presets/public-gallery.js +154 -31
- package/dist/presets/public-handoff.d.ts +30 -6
- package/dist/presets/public-handoff.js +104 -33
- package/dist/presets/public-preview-artifacts.js +6 -6
- package/dist/presets/public-preview.d.ts +11 -2
- package/dist/presets/public-preview.js +14 -0
- package/dist/presets/sources.d.ts +21 -3
- package/dist/presets/sources.js +2164 -1048
- package/dist/presets.d.ts +37 -3
- package/dist/presets.js +96 -0
- package/dist/{render-Cddsy_XF.js → render-ykQhtMs_.js} +13 -6
- package/package.json +1 -1
package/dist/compiler.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { a as EmailGlobalProps, c as EmailStyle, d as isUrlAttribute, i as Email
|
|
|
3
3
|
import { EmailElementSelection, EmittedPosition, INSTANCE_PATH_ATTRIBUTE, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections } from "./email/selection.js";
|
|
4
4
|
import { EmailHtmlOutput, SerializeEmailOptions, escapeAttribute, escapeText, serializeEmailHtml } from "./email/html.js";
|
|
5
5
|
import { deriveEmailText } from "./email/text.js";
|
|
6
|
+
import { a as compileSelector, c as CompiledSelector, d as matchesSelector, i as Stylesheet, l as Compound, n as CssRule, o as parseStylesheet, r as Specificity, s as Ancestry, t as CssDeclaration, u as classListOf } from "./css-ar2lU3NC.js";
|
|
6
7
|
import { EMAIL_DOCTYPE, EmailCompileOutput, RenderedEmail, compileEmail, renderEmail } from "./email/render.js";
|
|
7
|
-
import {
|
|
8
|
+
import { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets } from "./email/stylesheets.js";
|
|
8
9
|
import { instrumentTsxSourceForPreview } from "./tsx-source-instrumentation.js";
|
|
9
10
|
import { caniemail } from "caniemail";
|
|
10
11
|
//#region src/email/compatibility.d.ts
|
|
@@ -268,10 +269,11 @@ declare const EMAIL_ENTRY_WRAPPER = ".samva-email-entry.ts";
|
|
|
268
269
|
*/
|
|
269
270
|
declare const EMAIL_RESET_MODULE = ".samva-email-reset.ts";
|
|
270
271
|
/**
|
|
271
|
-
* The wrapper's source. It
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
272
|
+
* The wrapper's source. It exports the template beside a render bound to the
|
|
273
|
+
* sheets the project's own imports registered, so the render runs inside the
|
|
274
|
+
* project's module graph and keeps its styles when a later project loaded into
|
|
275
|
+
* the same process resets the registry. Importing the renderer from outside
|
|
276
|
+
* that graph reaches a second copy of the registry and silently renders unstyled.
|
|
275
277
|
*/
|
|
276
278
|
declare const emailEntryWrapper: (entryPoint: string, imports?: readonly string[]) => string;
|
|
277
279
|
/**
|
package/dist/compiler.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as applyStylesheet, c as downlevelMediaQuery, d as renderHeadCss, f as resolveVariables, i as renderEmail, l as finalizeValue, o as classListOf, r as compileEmail, s as matchesSelector, t as EMAIL_DOCTYPE, u as foldCalc } from "./render-ykQhtMs_.js";
|
|
2
2
|
import { EMAIL_ELEMENTS, EMAIL_GLOBAL_ATTRIBUTES, emailElement, isUrlAttribute } from "./email/elements.js";
|
|
3
3
|
import { EmailElementSelection, EmittedPosition, INSTANCE_PATH_ATTRIBUTE, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections } from "./email/selection.js";
|
|
4
4
|
import { escapeAttribute, escapeText, serializeEmailHtml } from "./email/html.js";
|
|
@@ -1340,12 +1340,13 @@ const EMAIL_ENTRY_WRAPPER = ".samva-email-entry.ts";
|
|
|
1340
1340
|
*/
|
|
1341
1341
|
const EMAIL_RESET_MODULE = ".samva-email-reset.ts";
|
|
1342
1342
|
/**
|
|
1343
|
-
* The wrapper's source. It
|
|
1344
|
-
*
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1343
|
+
* The wrapper's source. It exports the template beside a render bound to the
|
|
1344
|
+
* sheets the project's own imports registered, so the render runs inside the
|
|
1345
|
+
* project's module graph and keeps its styles when a later project loaded into
|
|
1346
|
+
* the same process resets the registry. Importing the renderer from outside
|
|
1347
|
+
* that graph reaches a second copy of the registry and silently renders unstyled.
|
|
1347
1348
|
*/
|
|
1348
|
-
const emailEntryWrapper = (entryPoint, imports = []) => `${[`./${EMAIL_RESET_MODULE}`, ...imports].map((specifier) => `import ${JSON.stringify(specifier)};\n`).join("")}import * as entry from ${JSON.stringify(`./${entryPoint}`)};\
|
|
1349
|
+
const emailEntryWrapper = (entryPoint, imports = []) => `${[`./${EMAIL_RESET_MODULE}`, ...imports].map((specifier) => `import ${JSON.stringify(specifier)};\n`).join("")}import * as entry from ${JSON.stringify(`./${entryPoint}`)};\nimport { bindStylesheets } from "@samva/markup/email/render";\nexport const template = entry.default;\nexport const renderEmail = bindStylesheets();\nexport { isTemplateDefinition } from "@samva/markup/template";\n`;
|
|
1349
1350
|
/**
|
|
1350
1351
|
* A host that renders more than one project in one process shares the registry
|
|
1351
1352
|
* through its module cache, so every project graph starts by clearing it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Result, Schema } from "effect";
|
|
2
2
|
//#region presets/corpus.d.ts
|
|
3
|
-
declare const TemplateCorpusKind: Schema.Literals<readonly ["owned-editable-sml", "inspiration-reference"]>;
|
|
3
|
+
declare const TemplateCorpusKind: Schema.Literals<readonly ["owned-editable-sml", "brand-recreation-sml", "inspiration-reference"]>;
|
|
4
4
|
type TemplateCorpusKind = typeof TemplateCorpusKind.Type;
|
|
5
5
|
declare const templateFacetOptions: {
|
|
6
6
|
readonly jobType: readonly ["account-access", "commerce", "product-communication", "lifecycle", "publishing-campaigns"];
|
|
@@ -49,6 +49,68 @@ declare const OwnedEditablePreset: Schema.TaggedStruct<"OwnedEditablePreset", {
|
|
|
49
49
|
}>;
|
|
50
50
|
}>;
|
|
51
51
|
interface OwnedEditablePreset extends Schema.Schema.Type<typeof OwnedEditablePreset> {}
|
|
52
|
+
declare const BrandRecreationPreset: Schema.TaggedStruct<"BrandRecreationPreset", {
|
|
53
|
+
readonly corpusKind: Schema.Literal<"brand-recreation-sml">;
|
|
54
|
+
readonly provenance: Schema.Struct<{
|
|
55
|
+
readonly owner: Schema.Literal<"Samva">;
|
|
56
|
+
readonly sourcePath: Schema.String;
|
|
57
|
+
readonly recordedAt: Schema.String;
|
|
58
|
+
/** The brand whose own email is recreated, named on every public surface. */
|
|
59
|
+
readonly brand: Schema.String;
|
|
60
|
+
/** What that brand's email does, in the customer's words. */
|
|
61
|
+
readonly subject: Schema.String;
|
|
62
|
+
readonly reviewedNote: Schema.String;
|
|
63
|
+
}>;
|
|
64
|
+
readonly review: Schema.Struct<{
|
|
65
|
+
readonly state: Schema.Literals<readonly ["draft", "approved", "rejected"]>;
|
|
66
|
+
readonly reviewedBy: Schema.String;
|
|
67
|
+
readonly reviewedAt: Schema.String;
|
|
68
|
+
readonly notes: Schema.String;
|
|
69
|
+
}>;
|
|
70
|
+
readonly display: Schema.Struct<{
|
|
71
|
+
readonly state: Schema.Literals<readonly ["hidden", "public"]>;
|
|
72
|
+
readonly attributionLabel: Schema.String;
|
|
73
|
+
}>;
|
|
74
|
+
readonly retention: Schema.Struct<{
|
|
75
|
+
readonly policy: Schema.Literals<readonly ["retain-until-withdrawn", "retain-while-linked"]>;
|
|
76
|
+
readonly reviewedAt: Schema.String;
|
|
77
|
+
}>;
|
|
78
|
+
readonly takedown: Schema.Struct<{
|
|
79
|
+
readonly state: Schema.Literals<readonly ["active", "removed"]>;
|
|
80
|
+
readonly contact: Schema.String;
|
|
81
|
+
readonly procedureUrl: Schema.String;
|
|
82
|
+
}>;
|
|
83
|
+
}>;
|
|
84
|
+
interface BrandRecreationPreset extends Schema.Schema.Type<typeof BrandRecreationPreset> {}
|
|
85
|
+
/** Clean-room recreations of a named brand's own email, authored as editable Samva projects. */
|
|
86
|
+
declare const brandRecreationProvenance: {
|
|
87
|
+
readonly "linear-login-code": {
|
|
88
|
+
readonly owner: "Samva";
|
|
89
|
+
readonly sourcePath: "examples/email-showcase/emails/linear-login-code.tsx";
|
|
90
|
+
readonly recordedAt: "2026-09-08T00:00:00.000Z";
|
|
91
|
+
readonly brand: "Linear";
|
|
92
|
+
readonly subject: "login code";
|
|
93
|
+
readonly reviewedNote: "Reviewed recreation of Linear's login code email, its canonical project assets, and its brand attribution.";
|
|
94
|
+
};
|
|
95
|
+
readonly "vercel-invitation": {
|
|
96
|
+
readonly owner: "Samva";
|
|
97
|
+
readonly sourcePath: "examples/email-showcase/emails/vercel-invitation.tsx";
|
|
98
|
+
readonly recordedAt: "2026-09-08T00:00:00.000Z";
|
|
99
|
+
readonly brand: "Vercel";
|
|
100
|
+
readonly subject: "team invitation";
|
|
101
|
+
readonly reviewedNote: "Reviewed recreation of Vercel's team invitation email, its canonical project assets, and its brand attribution.";
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Owned presets authored as complete showcase projects with their own stylesheet and assets.
|
|
106
|
+
* Every other owned preset is the single module `packages/markup/presets/<id>.tsx`.
|
|
107
|
+
*/
|
|
108
|
+
declare const ownedShowcasePresetSources: {
|
|
109
|
+
readonly "order-receipt": "examples/email-showcase/emails/order-receipt.tsx";
|
|
110
|
+
readonly "product-launch-promo": "examples/email-showcase/emails/product-launch-promo.tsx";
|
|
111
|
+
readonly "product-newsletter": "examples/email-showcase/emails/product-newsletter.tsx";
|
|
112
|
+
};
|
|
113
|
+
declare const ownedPresetSourcePath: (id: string) => string;
|
|
52
114
|
declare const InspirationReference: Schema.TaggedStruct<"InspirationReference", {
|
|
53
115
|
readonly corpusKind: Schema.Literal<"inspiration-reference">;
|
|
54
116
|
readonly provenance: Schema.Struct<{
|
|
@@ -104,6 +166,37 @@ declare const TemplateCorpusRecord: Schema.Union<readonly [Schema.TaggedStruct<"
|
|
|
104
166
|
readonly contact: Schema.String;
|
|
105
167
|
readonly procedureUrl: Schema.String;
|
|
106
168
|
}>;
|
|
169
|
+
}>, Schema.TaggedStruct<"BrandRecreationPreset", {
|
|
170
|
+
readonly corpusKind: Schema.Literal<"brand-recreation-sml">;
|
|
171
|
+
readonly provenance: Schema.Struct<{
|
|
172
|
+
readonly owner: Schema.Literal<"Samva">;
|
|
173
|
+
readonly sourcePath: Schema.String;
|
|
174
|
+
readonly recordedAt: Schema.String;
|
|
175
|
+
/** The brand whose own email is recreated, named on every public surface. */
|
|
176
|
+
readonly brand: Schema.String;
|
|
177
|
+
/** What that brand's email does, in the customer's words. */
|
|
178
|
+
readonly subject: Schema.String;
|
|
179
|
+
readonly reviewedNote: Schema.String;
|
|
180
|
+
}>;
|
|
181
|
+
readonly review: Schema.Struct<{
|
|
182
|
+
readonly state: Schema.Literals<readonly ["draft", "approved", "rejected"]>;
|
|
183
|
+
readonly reviewedBy: Schema.String;
|
|
184
|
+
readonly reviewedAt: Schema.String;
|
|
185
|
+
readonly notes: Schema.String;
|
|
186
|
+
}>;
|
|
187
|
+
readonly display: Schema.Struct<{
|
|
188
|
+
readonly state: Schema.Literals<readonly ["hidden", "public"]>;
|
|
189
|
+
readonly attributionLabel: Schema.String;
|
|
190
|
+
}>;
|
|
191
|
+
readonly retention: Schema.Struct<{
|
|
192
|
+
readonly policy: Schema.Literals<readonly ["retain-until-withdrawn", "retain-while-linked"]>;
|
|
193
|
+
readonly reviewedAt: Schema.String;
|
|
194
|
+
}>;
|
|
195
|
+
readonly takedown: Schema.Struct<{
|
|
196
|
+
readonly state: Schema.Literals<readonly ["active", "removed"]>;
|
|
197
|
+
readonly contact: Schema.String;
|
|
198
|
+
readonly procedureUrl: Schema.String;
|
|
199
|
+
}>;
|
|
107
200
|
}>, Schema.TaggedStruct<"InspirationReference", {
|
|
108
201
|
readonly corpusKind: Schema.Literal<"inspiration-reference">;
|
|
109
202
|
readonly provenance: Schema.Struct<{
|
|
@@ -157,5 +250,5 @@ interface TemplateFacetQuery extends Schema.Schema.Type<typeof TemplateFacetQuer
|
|
|
157
250
|
/** Strict intersection: every selected value in every selected dimension must match. */
|
|
158
251
|
declare const matchesTemplateFacets: (facets: TemplateFacetSet, query: TemplateFacetQuery) => boolean;
|
|
159
252
|
//#endregion
|
|
160
|
-
export {
|
|
161
|
-
//# sourceMappingURL=corpus-
|
|
253
|
+
export { PublicCorpusRegistrationIssue as a, TemplateFacetQuery as c, matchesTemplateFacets as d, ownedPresetSourcePath as f, templateFacetOptions as h, PublicCorpusRegistration as i, TemplateFacetSet as l, registerPublicCorpusEntry as m, InspirationReference as n, TemplateCorpusKind as o, ownedShowcasePresetSources as p, OwnedEditablePreset as r, TemplateCorpusRecord as s, BrandRecreationPreset as t, brandRecreationProvenance as u };
|
|
254
|
+
//# sourceMappingURL=corpus-qrUD5O6r.d.ts.map
|
|
@@ -80,15 +80,5 @@ declare const parseStylesheet: (source: string, options?: {
|
|
|
80
80
|
readonly startOrder?: number;
|
|
81
81
|
}) => Stylesheet;
|
|
82
82
|
//#endregion
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
declare const registerStylesheet: (sheet: Stylesheet) => void;
|
|
86
|
-
/** Drop every registration. The render entry calls this before a render. */
|
|
87
|
-
declare const resetStylesheets: () => void;
|
|
88
|
-
/** Every registered sheet flattened into one, with order preserved across sheets. */
|
|
89
|
-
declare const collectStylesheets: () => Stylesheet;
|
|
90
|
-
/** True when a template registered no styles at all, so the render can skip the pass. */
|
|
91
|
-
declare const hasStylesheets: () => boolean;
|
|
92
|
-
//#endregion
|
|
93
|
-
export { CssDeclaration as a, Stylesheet as c, Ancestry as d, CompiledSelector as f, matchesSelector as h, resetStylesheets as i, compileSelector as l, classListOf as m, hasStylesheets as n, CssRule as o, Compound as p, registerStylesheet as r, Specificity as s, collectStylesheets as t, parseStylesheet as u };
|
|
94
|
-
//# sourceMappingURL=stylesheets-oDRAnYA3.d.ts.map
|
|
83
|
+
export { compileSelector as a, CompiledSelector as c, matchesSelector as d, Stylesheet as i, Compound as l, CssRule as n, parseStylesheet as o, Specificity as r, Ancestry as s, CssDeclaration as t, classListOf as u };
|
|
84
|
+
//# sourceMappingURL=css-ar2lU3NC.d.ts.map
|
|
@@ -59,9 +59,15 @@ interface ButtonProps {
|
|
|
59
59
|
/** CSS classes applied to the primitive's content element. */
|
|
60
60
|
readonly className?: string | undefined;
|
|
61
61
|
readonly href: string;
|
|
62
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Pixel width of the box. Classic Outlook draws the VML shape at a fixed width, so without
|
|
64
|
+
* this it is estimated from the label and `paddingX`, and the anchor sizes to its label.
|
|
65
|
+
*/
|
|
63
66
|
readonly width?: number | undefined;
|
|
67
|
+
/** Pixel height; classic Outlook's VML fallback needs it. */
|
|
64
68
|
readonly height?: number | undefined;
|
|
69
|
+
/** Horizontal padding on each side of the label when no `width` fixes the box. */
|
|
70
|
+
readonly paddingX?: number | undefined;
|
|
65
71
|
readonly backgroundColor?: string | undefined;
|
|
66
72
|
readonly color?: string | undefined;
|
|
67
73
|
readonly borderRadius?: number | undefined;
|
|
@@ -75,7 +81,7 @@ interface ButtonProps {
|
|
|
75
81
|
readonly children?: EmailChild;
|
|
76
82
|
}
|
|
77
83
|
/** A link styled as a button, with a VML fallback so classic Outlook draws the shape. */
|
|
78
|
-
declare const Button: ({ className, href, width, height, backgroundColor, color, borderRadius, borderColor, borderWidth, fontSize, fontFamily, align, style, children }: ButtonProps) => EmailNode;
|
|
84
|
+
declare const Button: ({ className, href, width, height, paddingX, backgroundColor, color, borderRadius, borderColor, borderWidth, fontSize, fontFamily, align, style, children }: ButtonProps) => EmailNode;
|
|
79
85
|
interface SpacerProps {
|
|
80
86
|
readonly height: number;
|
|
81
87
|
}
|
package/dist/email/components.js
CHANGED
|
@@ -105,23 +105,26 @@ const Column = ({ className, align, valign, width, style, children }) => jsx("td
|
|
|
105
105
|
children
|
|
106
106
|
});
|
|
107
107
|
const DEFAULT_BUTTON_FONT = "Helvetica, Arial, sans-serif";
|
|
108
|
+
/** Average advance of a bold sans-serif glyph, as a fraction of the font size. */
|
|
109
|
+
const BOLD_GLYPH_ADVANCE = .6;
|
|
108
110
|
/** A link styled as a button, with a VML fallback so classic Outlook draws the shape. */
|
|
109
|
-
const Button = ({ className, href, width, height, backgroundColor = "#000000", color = "#ffffff", borderRadius = 0, borderColor, borderWidth = 0, fontSize = 16, fontFamily = DEFAULT_BUTTON_FONT, align = "left", style, children }) => {
|
|
111
|
+
const Button = ({ className, href, width, height, paddingX = 16, backgroundColor = "#000000", color = "#ffffff", borderRadius = 0, borderColor, borderWidth = 0, fontSize = 16, fontFamily = DEFAULT_BUTTON_FONT, align = "left", style, children }) => {
|
|
110
112
|
const label = typeof children === "string" ? children : void 0;
|
|
111
|
-
const fallback =
|
|
113
|
+
const fallback = height !== void 0 && label !== void 0;
|
|
112
114
|
if (!fallback) reportEmailDiagnostic({
|
|
113
115
|
code: "button-outlook-fallback",
|
|
114
116
|
severity: "warning",
|
|
115
|
-
message: "<Button> draws its classic Outlook shape from VML, which needs
|
|
117
|
+
message: "<Button> draws its classic Outlook shape from VML, which needs a height and a plain string label. Without both, classic Windows Outlook shows an unstyled link.",
|
|
116
118
|
clients: ["outlook-windows"]
|
|
117
119
|
});
|
|
118
120
|
const arcsize = height === void 0 ? 0 : Math.round(borderRadius / height * 100);
|
|
121
|
+
const fallbackWidth = width ?? Math.ceil((label?.length ?? 0) * fontSize * BOLD_GLYPH_ADVANCE) + 2 * paddingX;
|
|
119
122
|
return presentation({
|
|
120
123
|
width: "100%",
|
|
121
124
|
style: { borderCollapse: "collapse" }
|
|
122
125
|
}, jsx("td", {
|
|
123
126
|
align,
|
|
124
|
-
children: [fallback ? rawMarkup(`<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${escapeAttribute(href)}" style="height:${height}px;v-text-anchor:middle;width:${
|
|
127
|
+
children: [fallback ? rawMarkup(`<!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${escapeAttribute(href)}" style="height:${height}px;v-text-anchor:middle;width:${fallbackWidth}px;" arcsize="${arcsize}%" ${borderColor === void 0 ? "stroke=\"false\"" : `strokecolor="${escapeAttribute(borderColor)}" strokeweight="${borderWidth}px"`} fillcolor="${escapeAttribute(backgroundColor)}"><w:anchorlock/><center style="color:${escapeAttribute(color)};font-family:${escapeAttribute(fontFamily)};font-size:${fontSize}px;font-weight:bold;">${escapeText(label)}</center></v:roundrect><![endif]-->`) : null, jsx("a", {
|
|
125
128
|
className,
|
|
126
129
|
href,
|
|
127
130
|
target: "_blank",
|
|
@@ -137,7 +140,7 @@ const Button = ({ className, href, width, height, backgroundColor = "#000000", c
|
|
|
137
140
|
lineHeight: height === void 0 ? "120%" : `${height - 2 * borderWidth}px`,
|
|
138
141
|
textAlign: "center",
|
|
139
142
|
textDecoration: "none",
|
|
140
|
-
...width === void 0 ? {} : {
|
|
143
|
+
...width === void 0 ? { padding: `0 ${paddingX}px` } : {
|
|
141
144
|
width: `${width}px`,
|
|
142
145
|
maxWidth: "100%"
|
|
143
146
|
},
|
package/dist/email/render.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { m as EmailNode } from "../elements-DtwKlFVH.js";
|
|
|
3
3
|
import { EmittedPosition } from "./selection.js";
|
|
4
4
|
import { SerializeEmailOptions } from "./html.js";
|
|
5
5
|
import { r as TemplateDefinition } from "../template-Dy5yOPyp.js";
|
|
6
|
+
import { i as Stylesheet } from "../css-ar2lU3NC.js";
|
|
6
7
|
//#region src/email/render.d.ts
|
|
7
8
|
/** Classic Outlook lays a document out correctly only under a transitional doctype. */
|
|
8
9
|
declare const EMAIL_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
|
|
@@ -31,6 +32,12 @@ interface RenderedEmail {
|
|
|
31
32
|
}
|
|
32
33
|
/** Render one validated input through the template's only content function. Host isolation is required for untrusted source. */
|
|
33
34
|
declare const renderEmail: <Input>(template: TemplateDefinition<Input>, data: unknown, options?: SerializeEmailOptions) => RenderedEmail;
|
|
35
|
+
/**
|
|
36
|
+
* A render bound to the sheets registered so far. A compiled project's wrapper
|
|
37
|
+
* calls this once its own stylesheet imports have run, so a later project
|
|
38
|
+
* loaded into the same process cannot restyle an entry that is rendered again.
|
|
39
|
+
*/
|
|
40
|
+
declare const bindStylesheets: (stylesheet?: Stylesheet | undefined) => typeof renderEmail;
|
|
34
41
|
//#endregion
|
|
35
|
-
export { EMAIL_DOCTYPE, EmailCompileOutput, RenderedEmail, compileEmail, renderEmail };
|
|
42
|
+
export { EMAIL_DOCTYPE, EmailCompileOutput, RenderedEmail, bindStylesheets, compileEmail, renderEmail };
|
|
36
43
|
//# sourceMappingURL=render.d.ts.map
|
package/dist/email/render.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as renderEmail, n as bindStylesheets, r as compileEmail, t as EMAIL_DOCTYPE } from "../render-ykQhtMs_.js";
|
|
2
2
|
import "./html.js";
|
|
3
3
|
import "./diagnostics.js";
|
|
4
4
|
import "./stylesheets.js";
|
|
5
5
|
import "./text.js";
|
|
6
|
-
export { EMAIL_DOCTYPE, compileEmail, renderEmail };
|
|
6
|
+
export { EMAIL_DOCTYPE, bindStylesheets, compileEmail, renderEmail };
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
|
|
1
|
+
import { i as Stylesheet } from "../css-ar2lU3NC.js";
|
|
2
|
+
//#region src/email/stylesheets.d.ts
|
|
3
|
+
/** Called by a compiler-generated stylesheet module at import time. */
|
|
4
|
+
declare const registerStylesheet: (sheet: Stylesheet) => void;
|
|
5
|
+
/** Drop every registration. The render entry calls this before a render. */
|
|
6
|
+
declare const resetStylesheets: () => void;
|
|
7
|
+
/** Every registered sheet flattened into one, with order preserved across sheets. */
|
|
8
|
+
declare const collectStylesheets: () => Stylesheet;
|
|
9
|
+
/** True when a template registered no styles at all, so the render can skip the pass. */
|
|
10
|
+
declare const hasStylesheets: () => boolean;
|
|
11
|
+
/** The registry as it stands, or `undefined` when no sheet registered. */
|
|
12
|
+
declare const snapshotStylesheets: () => Stylesheet | undefined;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets, snapshotStylesheets };
|
|
15
|
+
//# sourceMappingURL=stylesheets.d.ts.map
|
|
@@ -37,7 +37,9 @@ const collectStylesheets = () => {
|
|
|
37
37
|
};
|
|
38
38
|
/** True when a template registered no styles at all, so the render can skip the pass. */
|
|
39
39
|
const hasStylesheets = () => registry.length > 0;
|
|
40
|
+
/** The registry as it stands, or `undefined` when no sheet registered. */
|
|
41
|
+
const snapshotStylesheets = () => hasStylesheets() ? collectStylesheets() : void 0;
|
|
40
42
|
//#endregion
|
|
41
|
-
export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets };
|
|
43
|
+
export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets, snapshotStylesheets };
|
|
42
44
|
|
|
43
45
|
//# sourceMappingURL=stylesheets.js.map
|
package/dist/email/text.js
CHANGED
|
@@ -4,6 +4,14 @@ const PARAGRAPH = "\n\n";
|
|
|
4
4
|
const LINE = "\n";
|
|
5
5
|
const INVISIBLE = /\u200b|\u200c|\u200d|\u200e|\u200f|\u2060|\ufeff/g;
|
|
6
6
|
const isLayoutTable = (props) => props.role === "presentation" || props.role === "none";
|
|
7
|
+
/**
|
|
8
|
+
* A data-table cell that reads as nothing and holds only imagery with empty alt text carries no
|
|
9
|
+
* column of its own; an empty cell without such an image is a positional placeholder.
|
|
10
|
+
*/
|
|
11
|
+
const isDecorativeCell = (cell, context) => {
|
|
12
|
+
const images = collect(cell.children, (tag) => tag === "img", () => true);
|
|
13
|
+
return images.length > 0 && images.every((image) => image.props.alt === "") && childText(cell, context).trim() === "";
|
|
14
|
+
};
|
|
7
15
|
/** Wrap a block's content in separators; empty blocks contribute nothing. */
|
|
8
16
|
const block = (inner, separator) => {
|
|
9
17
|
const trimmed = inner.replace(/^\s+/, "").replace(/\s+$/, "");
|
|
@@ -64,7 +72,7 @@ const derive = (node, context) => {
|
|
|
64
72
|
}
|
|
65
73
|
case "table": {
|
|
66
74
|
if (isLayoutTable(node.props)) return childText(node, context);
|
|
67
|
-
const rows = collect(node.children, (tag) => tag === "tr", (tag) => TABLE_SECTIONS.has(tag)).map((row) => collect(row.children, (tag) => tag === "td" || tag === "th", () => false).map((cell) => childText(cell, context).replace(/\s+/g, " ").trim()).join(" | "));
|
|
75
|
+
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(" | "));
|
|
68
76
|
return block(rows.join(LINE), PARAGRAPH);
|
|
69
77
|
}
|
|
70
78
|
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);
|
package/dist/presets/corpus.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { InspirationReference, OwnedEditablePreset, PublicCorpusRegistration, PublicCorpusRegistrationIssue, TemplateCorpusKind, TemplateCorpusRecord, TemplateFacetQuery, TemplateFacetSet, matchesTemplateFacets, registerPublicCorpusEntry, templateFacetOptions };
|
|
1
|
+
import { a as PublicCorpusRegistrationIssue, c as TemplateFacetQuery, d as matchesTemplateFacets, f as ownedPresetSourcePath, h as templateFacetOptions, i as PublicCorpusRegistration, l as TemplateFacetSet, m as registerPublicCorpusEntry, n as InspirationReference, o as TemplateCorpusKind, p as ownedShowcasePresetSources, r as OwnedEditablePreset, s as TemplateCorpusRecord, t as BrandRecreationPreset, u as brandRecreationProvenance } from "../corpus-qrUD5O6r.js";
|
|
2
|
+
export { BrandRecreationPreset, InspirationReference, OwnedEditablePreset, PublicCorpusRegistration, PublicCorpusRegistrationIssue, TemplateCorpusKind, TemplateCorpusRecord, TemplateFacetQuery, TemplateFacetSet, brandRecreationProvenance, matchesTemplateFacets, ownedPresetSourcePath, ownedShowcasePresetSources, registerPublicCorpusEntry, templateFacetOptions };
|
package/dist/presets/corpus.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { presetCatalog } from "../presets.js";
|
|
2
2
|
import { Result, Schema } from "effect";
|
|
3
3
|
//#region presets/corpus.ts
|
|
4
|
-
const TemplateCorpusKind = Schema.Literals([
|
|
4
|
+
const TemplateCorpusKind = Schema.Literals([
|
|
5
|
+
"owned-editable-sml",
|
|
6
|
+
"brand-recreation-sml",
|
|
7
|
+
"inspiration-reference"
|
|
8
|
+
]);
|
|
5
9
|
const templateFacetOptions = {
|
|
6
10
|
jobType: [
|
|
7
11
|
"account-access",
|
|
@@ -93,6 +97,52 @@ const OwnedEditablePreset = Schema.TaggedStruct("OwnedEditablePreset", {
|
|
|
93
97
|
retention: CorpusRetention,
|
|
94
98
|
takedown: CorpusTakedown
|
|
95
99
|
});
|
|
100
|
+
const BrandRecreationPreset = Schema.TaggedStruct("BrandRecreationPreset", {
|
|
101
|
+
corpusKind: Schema.Literal("brand-recreation-sml"),
|
|
102
|
+
provenance: Schema.Struct({
|
|
103
|
+
owner: Schema.Literal("Samva"),
|
|
104
|
+
sourcePath: Schema.String,
|
|
105
|
+
recordedAt: Schema.String,
|
|
106
|
+
/** The brand whose own email is recreated, named on every public surface. */
|
|
107
|
+
brand: Schema.String,
|
|
108
|
+
/** What that brand's email does, in the customer's words. */
|
|
109
|
+
subject: Schema.String,
|
|
110
|
+
reviewedNote: Schema.String
|
|
111
|
+
}),
|
|
112
|
+
review: CorpusReview,
|
|
113
|
+
display: CorpusDisplay,
|
|
114
|
+
retention: CorpusRetention,
|
|
115
|
+
takedown: CorpusTakedown
|
|
116
|
+
});
|
|
117
|
+
/** Clean-room recreations of a named brand's own email, authored as editable Samva projects. */
|
|
118
|
+
const brandRecreationProvenance = {
|
|
119
|
+
"linear-login-code": {
|
|
120
|
+
owner: "Samva",
|
|
121
|
+
sourcePath: "examples/email-showcase/emails/linear-login-code.tsx",
|
|
122
|
+
recordedAt: "2026-09-08T00:00:00.000Z",
|
|
123
|
+
brand: "Linear",
|
|
124
|
+
subject: "login code",
|
|
125
|
+
reviewedNote: "Reviewed recreation of Linear's login code email, its canonical project assets, and its brand attribution."
|
|
126
|
+
},
|
|
127
|
+
"vercel-invitation": {
|
|
128
|
+
owner: "Samva",
|
|
129
|
+
sourcePath: "examples/email-showcase/emails/vercel-invitation.tsx",
|
|
130
|
+
recordedAt: "2026-09-08T00:00:00.000Z",
|
|
131
|
+
brand: "Vercel",
|
|
132
|
+
subject: "team invitation",
|
|
133
|
+
reviewedNote: "Reviewed recreation of Vercel's team invitation email, its canonical project assets, and its brand attribution."
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Owned presets authored as complete showcase projects with their own stylesheet and assets.
|
|
138
|
+
* Every other owned preset is the single module `packages/markup/presets/<id>.tsx`.
|
|
139
|
+
*/
|
|
140
|
+
const ownedShowcasePresetSources = {
|
|
141
|
+
"order-receipt": "examples/email-showcase/emails/order-receipt.tsx",
|
|
142
|
+
"product-launch-promo": "examples/email-showcase/emails/product-launch-promo.tsx",
|
|
143
|
+
"product-newsletter": "examples/email-showcase/emails/product-newsletter.tsx"
|
|
144
|
+
};
|
|
145
|
+
const ownedPresetSourcePath = (id) => Object.hasOwn(ownedShowcasePresetSources, id) ? ownedShowcasePresetSources[id] : `packages/markup/presets/${id}.tsx`;
|
|
96
146
|
const InspirationReference = Schema.TaggedStruct("InspirationReference", {
|
|
97
147
|
corpusKind: Schema.Literal("inspiration-reference"),
|
|
98
148
|
provenance: Schema.Struct({
|
|
@@ -106,7 +156,11 @@ const InspirationReference = Schema.TaggedStruct("InspirationReference", {
|
|
|
106
156
|
retention: CorpusRetention,
|
|
107
157
|
takedown: CorpusTakedown
|
|
108
158
|
});
|
|
109
|
-
const TemplateCorpusRecord = Schema.Union([
|
|
159
|
+
const TemplateCorpusRecord = Schema.Union([
|
|
160
|
+
OwnedEditablePreset,
|
|
161
|
+
BrandRecreationPreset,
|
|
162
|
+
InspirationReference
|
|
163
|
+
]);
|
|
110
164
|
const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
111
165
|
const nonEmpty = (value) => value.trim().length > 0;
|
|
112
166
|
const isHttpsUrl = (value) => {
|
|
@@ -122,7 +176,7 @@ const requiredFacetDimensions = [
|
|
|
122
176
|
"design",
|
|
123
177
|
"brandProvenance"
|
|
124
178
|
];
|
|
125
|
-
const canonicalOwnedPresetSources = new Map(presetCatalog.filter((preset) => preset.channel === "email").map((preset) => [preset.id,
|
|
179
|
+
const canonicalOwnedPresetSources = new Map(presetCatalog.filter((preset) => preset.channel === "email" && !Object.hasOwn(brandRecreationProvenance, preset.id)).map((preset) => [preset.id, ownedPresetSourcePath(preset.id)]));
|
|
126
180
|
/** Decode and gate a record before it can enter any public projection. */
|
|
127
181
|
const registerPublicCorpusEntry = (input) => {
|
|
128
182
|
const decoded = Schema.decodeUnknownResult(Schema.Struct({
|
|
@@ -148,6 +202,13 @@ const registerPublicCorpusEntry = (input) => {
|
|
|
148
202
|
id,
|
|
149
203
|
message: "owned presets require a canonical editable TSX source"
|
|
150
204
|
});
|
|
205
|
+
if (corpus._tag === "BrandRecreationPreset") {
|
|
206
|
+
const canonical = Object.entries(brandRecreationProvenance).find(([presetId]) => presetId === id)?.[1];
|
|
207
|
+
if (canonical === void 0 || Object.entries(canonical).some(([key, value]) => corpus.provenance[key] !== value) || !nonEmpty(corpus.provenance.brand) || !nonEmpty(corpus.provenance.subject) || corpus.retention.policy !== "retain-until-withdrawn" || facets.brandProvenance.some((value) => value !== "third-party-reference")) return Result.fail({
|
|
208
|
+
id,
|
|
209
|
+
message: "brand recreations require a canonical source, a referenced brand and subject, and third-party provenance"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
151
212
|
if (corpus._tag === "InspirationReference" && (!isHttpsUrl(corpus.provenance.sourceUrl) || corpus.retention.policy !== "retain-while-linked" || facets.brandProvenance.some((value) => value !== "third-party-reference"))) return Result.fail({
|
|
152
213
|
id,
|
|
153
214
|
message: "inspiration references require an HTTPS source and third-party provenance facet"
|
|
@@ -181,6 +242,6 @@ const matchesTemplateFacets = (facets, query) => requiredFacetDimensions.every((
|
|
|
181
242
|
return selected === void 0 || selected.every((value) => available.has(value));
|
|
182
243
|
});
|
|
183
244
|
//#endregion
|
|
184
|
-
export { InspirationReference, OwnedEditablePreset, TemplateCorpusKind, TemplateCorpusRecord, TemplateFacetQuery, TemplateFacetSet, matchesTemplateFacets, registerPublicCorpusEntry, templateFacetOptions };
|
|
245
|
+
export { BrandRecreationPreset, InspirationReference, OwnedEditablePreset, TemplateCorpusKind, TemplateCorpusRecord, TemplateFacetQuery, TemplateFacetSet, brandRecreationProvenance, matchesTemplateFacets, ownedPresetSourcePath, ownedShowcasePresetSources, registerPublicCorpusEntry, templateFacetOptions };
|
|
185
246
|
|
|
186
247
|
//# sourceMappingURL=corpus.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EmailPresetId } from "../presets.js";
|
|
2
|
-
import { c as TemplateFacetSet,
|
|
2
|
+
import { c as TemplateFacetQuery, l as TemplateFacetSet, r as OwnedEditablePreset, t as BrandRecreationPreset } from "../corpus-qrUD5O6r.js";
|
|
3
3
|
//#region presets/public-gallery.d.ts
|
|
4
4
|
declare const templateUseCases: readonly ["account-access", "commerce", "product-communication", "lifecycle", "publishing-campaigns"];
|
|
5
5
|
declare const templateCollectionFamilies: readonly ["category", "industry", "stage", "style"];
|
|
@@ -12,8 +12,8 @@ interface PublicTemplate {
|
|
|
12
12
|
readonly useCase: TemplateUseCase;
|
|
13
13
|
readonly collections: ReadonlyArray<TemplateCollectionId>;
|
|
14
14
|
readonly relatedTemplates: ReadonlyArray<EmailPresetId>;
|
|
15
|
-
/**
|
|
16
|
-
readonly corpus: OwnedEditablePreset;
|
|
15
|
+
/** Editable SML authored by Samva, either an original or a named-brand recreation. */
|
|
16
|
+
readonly corpus: OwnedEditablePreset | BrandRecreationPreset;
|
|
17
17
|
readonly facets: TemplateFacetSet;
|
|
18
18
|
}
|
|
19
19
|
declare const isMadeWithSamvaTemplate: (template: Pick<PublicTemplate, "corpus">) => boolean;
|
|
@@ -82,6 +82,12 @@ declare const ownedPresetReviewManifest: {
|
|
|
82
82
|
readonly reviewedAt: "2026-08-31T00:00:00.000Z";
|
|
83
83
|
readonly notes: "Reviewed team-invite editable SML, public preview data, and gallery identity.";
|
|
84
84
|
};
|
|
85
|
+
readonly "product-newsletter": {
|
|
86
|
+
readonly sourcePath: "examples/email-showcase/emails/product-newsletter.tsx";
|
|
87
|
+
readonly reviewedBy: "Samva catalog review";
|
|
88
|
+
readonly reviewedAt: "2026-09-08T00:00:00.000Z";
|
|
89
|
+
readonly notes: "Reviewed product-newsletter editable SML project, authored assets, public preview data, and gallery identity.";
|
|
90
|
+
};
|
|
85
91
|
readonly "product-digest": {
|
|
86
92
|
readonly sourcePath: "packages/markup/presets/product-digest.tsx";
|
|
87
93
|
readonly reviewedBy: "Samva catalog review";
|
|
@@ -178,6 +184,12 @@ declare const ownedPresetReviewManifest: {
|
|
|
178
184
|
readonly reviewedAt: "2026-08-31T00:00:00.000Z";
|
|
179
185
|
readonly notes: "Reviewed order-delivered editable SML, public preview data, and gallery identity.";
|
|
180
186
|
};
|
|
187
|
+
readonly "order-receipt": {
|
|
188
|
+
readonly sourcePath: "examples/email-showcase/emails/order-receipt.tsx";
|
|
189
|
+
readonly reviewedBy: "Samva catalog review";
|
|
190
|
+
readonly reviewedAt: "2026-09-08T00:00:00.000Z";
|
|
191
|
+
readonly notes: "Reviewed order-receipt editable project with stylesheet and original assets, public preview data, and gallery identity.";
|
|
192
|
+
};
|
|
181
193
|
readonly "travel-rewards-promo": {
|
|
182
194
|
readonly sourcePath: "packages/markup/presets/travel-rewards-promo.tsx";
|
|
183
195
|
readonly reviewedBy: "Samva catalog review";
|
|
@@ -256,15 +268,21 @@ declare const ownedPresetReviewManifest: {
|
|
|
256
268
|
readonly reviewedAt: "2026-08-31T00:00:00.000Z";
|
|
257
269
|
readonly notes: "Reviewed season-clearance-promo editable SML, public preview data, and gallery identity.";
|
|
258
270
|
};
|
|
271
|
+
readonly "product-launch-promo": {
|
|
272
|
+
readonly sourcePath: "examples/email-showcase/emails/product-launch-promo.tsx";
|
|
273
|
+
readonly reviewedBy: "Samva catalog review";
|
|
274
|
+
readonly reviewedAt: "2026-09-08T00:00:00.000Z";
|
|
275
|
+
readonly notes: "Reviewed product-launch-promo editable project, original Oakline assets, public preview data, and gallery identity.";
|
|
276
|
+
};
|
|
259
277
|
};
|
|
260
|
-
declare const publicTemplates: readonly [PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate];
|
|
278
|
+
declare const publicTemplates: readonly [PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate];
|
|
261
279
|
/** Deterministic strict-intersection search over the canonical public corpus order. */
|
|
262
280
|
declare const filterPublicTemplatesByFacets: (query: TemplateFacetQuery) => ReadonlyArray<PublicTemplate>;
|
|
263
281
|
declare const templateCollections: readonly [TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection];
|
|
264
282
|
declare const publicTemplateGallery: {
|
|
265
283
|
readonly hubPath: "/templates";
|
|
266
284
|
readonly useCases: readonly ["account-access", "commerce", "product-communication", "lifecycle", "publishing-campaigns"];
|
|
267
|
-
readonly templates: readonly [PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate];
|
|
285
|
+
readonly templates: readonly [PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate, PublicTemplate];
|
|
268
286
|
readonly collections: readonly [TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection, TemplateCollection];
|
|
269
287
|
};
|
|
270
288
|
//#endregion
|