@sdxc/mail 0.0.0-pre.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.
- package/LICENSE.md +21 -0
- package/README.md +544 -0
- package/dist/cloudflare.d.ts +82 -0
- package/dist/cloudflare.js +71 -0
- package/dist/components.d.ts +346 -0
- package/dist/components.js +293 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +24 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +22 -0
- package/dist/lib/address.d.ts +37 -0
- package/dist/lib/address.js +56 -0
- package/dist/lib/html-to-text.d.ts +19 -0
- package/dist/lib/html-to-text.js +149 -0
- package/dist/mailer.d.ts +82 -0
- package/dist/mailer.js +188 -0
- package/dist/markdown.d.ts +46 -0
- package/dist/markdown.js +182 -0
- package/dist/memory.d.ts +81 -0
- package/dist/memory.js +84 -0
- package/dist/middleware.d.ts +53 -0
- package/dist/middleware.js +50 -0
- package/dist/mime.d.ts +21 -0
- package/dist/mime.js +374 -0
- package/dist/render.d.ts +27 -0
- package/dist/render.js +33 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.js +8 -0
- package/package.json +30 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport for the Workers email sending binding, whose structured send call takes
|
|
3
|
+
* the same fields a normalized message already holds, so it maps them across and
|
|
4
|
+
* assembles no MIME of its own. The binding is injected rather than read from an
|
|
5
|
+
* ambient global, which keeps the platform surface a single seam this package can be
|
|
6
|
+
* tested and corrected at.
|
|
7
|
+
*
|
|
8
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
9
|
+
* @copyright Sergio Xalambrí 2026
|
|
10
|
+
*/
|
|
11
|
+
import { failure, isFailure, success, wrap } from "@sdxc/result";
|
|
12
|
+
import { MailError } from "./errors.js";
|
|
13
|
+
import { formatAddress } from "./lib/address.js";
|
|
14
|
+
/** Returns the formatted list, or `undefined` when empty so an absent field is omitted. */
|
|
15
|
+
function optionalAddresses(addresses) {
|
|
16
|
+
if (addresses.length === 0)
|
|
17
|
+
return undefined;
|
|
18
|
+
return addresses.map(formatAddress);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Maps a normalized message onto the binding's send payload. Only the first
|
|
22
|
+
* reply-to mailbox survives, since the platform's field holds one address, so
|
|
23
|
+
* every shipped `Reply-To` is one the platform actually wrote itself.
|
|
24
|
+
*/
|
|
25
|
+
function toPayload(message) {
|
|
26
|
+
let replyTo = message.replyTo.at(0);
|
|
27
|
+
return {
|
|
28
|
+
from: formatAddress(message.from),
|
|
29
|
+
to: message.to.map(formatAddress),
|
|
30
|
+
cc: optionalAddresses(message.cc),
|
|
31
|
+
bcc: optionalAddresses(message.bcc),
|
|
32
|
+
replyTo: replyTo && formatAddress(replyTo),
|
|
33
|
+
subject: message.subject,
|
|
34
|
+
text: message.text,
|
|
35
|
+
html: message.html,
|
|
36
|
+
headers: message.headers,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Transport that delivers through the Workers email sending binding, which writes
|
|
41
|
+
* its own `Date` and `Message-ID` and returns the identifier its delivery logs are
|
|
42
|
+
* keyed by, enforcing sender and destination limits from its configuration on send.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* let transport = new CloudflareTransport(env.EMAIL);
|
|
46
|
+
* let mailer = new Mailer({ transport, from: SENDER });
|
|
47
|
+
*/
|
|
48
|
+
export class CloudflareTransport {
|
|
49
|
+
binding;
|
|
50
|
+
/**
|
|
51
|
+
* Creates the transport around an app's binding.
|
|
52
|
+
*
|
|
53
|
+
* @param binding - The `send_email` binding declared in the app's Wrangler configuration.
|
|
54
|
+
*/
|
|
55
|
+
constructor(binding) {
|
|
56
|
+
this.binding = binding;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Hands the message's fields to the binding, which composes and delivers it.
|
|
60
|
+
*
|
|
61
|
+
* @param message - The normalized message to deliver.
|
|
62
|
+
* @returns The identifier the platform assigned on success, a `MailError` on rejection.
|
|
63
|
+
*/
|
|
64
|
+
async send(message) {
|
|
65
|
+
let outcome = await wrap(() => this.binding.send(toPayload(message)));
|
|
66
|
+
if (isFailure(outcome)) {
|
|
67
|
+
return failure(new MailError("The mail binding rejected the message.", { cause: outcome.error }));
|
|
68
|
+
}
|
|
69
|
+
return success({ messageId: outcome.data.messageId });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout kit for email bodies, built for the constraints mail clients impose:
|
|
3
|
+
* table layout, inline styles, and no external stylesheet. It carries no product
|
|
4
|
+
* branding — colors, logo, and footer content are props — so every app styles its
|
|
5
|
+
* own mail without forking the components.
|
|
6
|
+
*
|
|
7
|
+
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
8
|
+
* @copyright Sergio Xalambrí 2026
|
|
9
|
+
*/
|
|
10
|
+
import type { Handle, RemixNode } from "remix/ui";
|
|
11
|
+
/**
|
|
12
|
+
* A web font to load, and what to use instead everywhere it does not arrive. It
|
|
13
|
+
* is a prop of {@link Layout} instead of its own component because `@font-face`
|
|
14
|
+
* only works inside `<head>`, which only the layout renders in the document.
|
|
15
|
+
*/
|
|
16
|
+
export interface Font {
|
|
17
|
+
/** Family name, as the `@font-face` rule declares it and the copy asks for it. */
|
|
18
|
+
family: string;
|
|
19
|
+
/**
|
|
20
|
+
* What to fall back to, as a CSS font list. It is required because most readers get
|
|
21
|
+
* it: `@font-face` is unsupported in Gmail, Yahoo, and Outlook on Windows, which is
|
|
22
|
+
* most of any list, so this is the font the email is actually set in.
|
|
23
|
+
*/
|
|
24
|
+
fallback: string;
|
|
25
|
+
/** Where to fetch the font, omitted for a family the reader is assumed to have. */
|
|
26
|
+
src?: {
|
|
27
|
+
/** Absolute URL of the font file. */
|
|
28
|
+
url: string;
|
|
29
|
+
/** Format at that URL, which the rule has to name for the client to accept it. */
|
|
30
|
+
format: "woff" | "woff2" | "truetype" | "opentype" | "embedded-opentype" | "svg";
|
|
31
|
+
};
|
|
32
|
+
/** Weight this file provides. */
|
|
33
|
+
weight?: number;
|
|
34
|
+
/** Style this file provides. */
|
|
35
|
+
style?: "normal" | "italic";
|
|
36
|
+
}
|
|
37
|
+
export declare namespace Layout {
|
|
38
|
+
/** Props accepted by {@link Layout}. */
|
|
39
|
+
interface Props {
|
|
40
|
+
/** Body of the email, usually headings, text, and a button. */
|
|
41
|
+
children?: RemixNode;
|
|
42
|
+
/**
|
|
43
|
+
* Preheader shown beside the subject in inbox previews. It is hidden in the
|
|
44
|
+
* body and dropped from the plain-text part, so it never reads as duplicate copy.
|
|
45
|
+
*/
|
|
46
|
+
preview?: string;
|
|
47
|
+
/** Logo rendered above the content; omitted means no logo. */
|
|
48
|
+
logo?: {
|
|
49
|
+
src: string;
|
|
50
|
+
alt: string;
|
|
51
|
+
width?: number;
|
|
52
|
+
};
|
|
53
|
+
/** Document title, which some clients show when the email is opened in a browser. */
|
|
54
|
+
title?: string;
|
|
55
|
+
/** Language of the copy, so screen readers pronounce it correctly. */
|
|
56
|
+
lang?: string;
|
|
57
|
+
/** Page color behind the content card. */
|
|
58
|
+
background?: string;
|
|
59
|
+
/** Content card color. */
|
|
60
|
+
surface?: string;
|
|
61
|
+
/** Body copy color. */
|
|
62
|
+
color?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Font stack applied to the whole document. Omitted with `fonts` set, the stack is
|
|
65
|
+
* built from the first of those; omitted with neither, the kit's own stack is used.
|
|
66
|
+
*/
|
|
67
|
+
fontFamily?: string;
|
|
68
|
+
/** Web fonts to declare, in preference order. */
|
|
69
|
+
fonts?: Font[];
|
|
70
|
+
/** Content width in pixels. */
|
|
71
|
+
width?: number;
|
|
72
|
+
/**
|
|
73
|
+
* CSS appended inside the kit's `prefers-color-scheme: dark` block, for the dark
|
|
74
|
+
* counterpart of colors the app's own components paint with. It is escaped as a
|
|
75
|
+
* text node, so it cannot contain `>` or `&`.
|
|
76
|
+
*/
|
|
77
|
+
darkStyles?: string;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wraps an email body in a centered, fixed-width card of inline-styled tables,
|
|
82
|
+
* with colors as props so the kit stays unbranded, and renders the document's
|
|
83
|
+
* only stylesheet — the dark counterpart of its colors; see {@link DARK_RULES}.
|
|
84
|
+
*
|
|
85
|
+
* @example <Email.Layout preview="Your invite is ready"><Email.Text>Hi</Email.Text></Email.Layout>
|
|
86
|
+
*/
|
|
87
|
+
export declare function Layout(handle: Handle<Layout.Props>): () => import("remix/ui").RemixElement;
|
|
88
|
+
export declare namespace Heading {
|
|
89
|
+
/** Props accepted by {@link Heading}. */
|
|
90
|
+
interface Props {
|
|
91
|
+
/** Heading copy. */
|
|
92
|
+
children?: RemixNode;
|
|
93
|
+
/** Outline level, which also picks the font size. */
|
|
94
|
+
level?: 1 | 2 | 3;
|
|
95
|
+
/** Heading color. */
|
|
96
|
+
color?: string;
|
|
97
|
+
/** Horizontal alignment of the copy. */
|
|
98
|
+
align?: "left" | "center" | "right";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Renders a heading with the margin collapsed to zero on top, because mail clients
|
|
103
|
+
* disagree on default heading margins and an inline reset is the only fix that holds.
|
|
104
|
+
*
|
|
105
|
+
* @example <Email.Heading level={2}>You have been invited</Email.Heading>
|
|
106
|
+
*/
|
|
107
|
+
export declare function Heading(handle: Handle<Heading.Props>): () => import("remix/ui").RemixElement;
|
|
108
|
+
export declare namespace Text {
|
|
109
|
+
/** Props accepted by {@link Text}. */
|
|
110
|
+
interface Props {
|
|
111
|
+
/** Paragraph copy. */
|
|
112
|
+
children?: RemixNode;
|
|
113
|
+
/** Copy color; defaults to the body color, or the muted one when `muted` is set. */
|
|
114
|
+
color?: string;
|
|
115
|
+
/** Renders the paragraph in the de-emphasized color. */
|
|
116
|
+
muted?: boolean;
|
|
117
|
+
/** Font size in pixels. */
|
|
118
|
+
size?: number;
|
|
119
|
+
/** Horizontal alignment of the copy. */
|
|
120
|
+
align?: "left" | "center" | "right";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Renders a paragraph of body copy with an explicit line height, since clients
|
|
125
|
+
* that strip the document styles fall back to a cramped default otherwise.
|
|
126
|
+
*
|
|
127
|
+
* @example <Email.Text muted>This link expires in 24 hours.</Email.Text>
|
|
128
|
+
*/
|
|
129
|
+
export declare function Text(handle: Handle<Text.Props>): () => import("remix/ui").RemixElement;
|
|
130
|
+
export declare namespace Button {
|
|
131
|
+
/** Props accepted by {@link Button}. */
|
|
132
|
+
interface Props {
|
|
133
|
+
/** Target of the call to action. */
|
|
134
|
+
href: string;
|
|
135
|
+
/** Button label. */
|
|
136
|
+
children?: RemixNode;
|
|
137
|
+
/** Fill color of the button. */
|
|
138
|
+
background?: string;
|
|
139
|
+
/** Label color of the button. */
|
|
140
|
+
color?: string;
|
|
141
|
+
/** Corner radius in pixels. */
|
|
142
|
+
radius?: number;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Renders a call-to-action as a link inside a single-cell table so the fill
|
|
147
|
+
* survives in clients that drop CSS backgrounds on anchors, with the padding
|
|
148
|
+
* on the cell since Word supports neither inline-block nor padding on an anchor.
|
|
149
|
+
*
|
|
150
|
+
* @example <Email.Button href={url}>Accept invite</Email.Button>
|
|
151
|
+
*/
|
|
152
|
+
export declare function Button(handle: Handle<Button.Props>): () => import("remix/ui").RemixElement;
|
|
153
|
+
export declare namespace Table {
|
|
154
|
+
/** One row: what is being reported, and what it says. */
|
|
155
|
+
interface Row {
|
|
156
|
+
/** Name of the thing reported, rendered in the de-emphasized color. */
|
|
157
|
+
label: string;
|
|
158
|
+
/** The value as the reader sees it, already formatted and translated. */
|
|
159
|
+
value: RemixNode;
|
|
160
|
+
}
|
|
161
|
+
/** Props accepted by {@link Table}. */
|
|
162
|
+
interface Props {
|
|
163
|
+
/** Rows in reading order. Rendering nothing for an empty list is deliberate. */
|
|
164
|
+
rows: Row[];
|
|
165
|
+
/** Color of the hairlines between rows. */
|
|
166
|
+
borderColor?: string;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* A set of facts as a two-column table: label on the left, value against the
|
|
171
|
+
* right, with hairlines between rows — a scannable column beats one paragraph
|
|
172
|
+
* per fact, and stays a layout every mail client renders alike.
|
|
173
|
+
*
|
|
174
|
+
* @example <Email.Table rows={[{ label: "Status", value: "Up" }]} />
|
|
175
|
+
*/
|
|
176
|
+
export declare function Table(handle: Handle<Table.Props>): () => import("remix/ui").RemixElement | null;
|
|
177
|
+
export declare namespace Footer {
|
|
178
|
+
/** Props accepted by {@link Footer}. */
|
|
179
|
+
interface Props {
|
|
180
|
+
/** Footer content, such as an address, an unsubscribe link, or a legal note. */
|
|
181
|
+
children?: RemixNode;
|
|
182
|
+
/** Footer copy color. */
|
|
183
|
+
color?: string;
|
|
184
|
+
/** Color of the hairline above the footer. */
|
|
185
|
+
borderColor?: string;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Closes an email with de-emphasized copy under a hairline. Content is entirely a
|
|
190
|
+
* prop, so the kit never ships a company name, address, or unsubscribe wording.
|
|
191
|
+
*
|
|
192
|
+
* @example <Email.Footer>Sent because you were invited to a team.</Email.Footer>
|
|
193
|
+
*/
|
|
194
|
+
export declare function Footer(handle: Handle<Footer.Props>): () => import("remix/ui").RemixElement;
|
|
195
|
+
export declare namespace Section {
|
|
196
|
+
/** Props accepted by {@link Section}. */
|
|
197
|
+
interface Props {
|
|
198
|
+
/** Content of the section. */
|
|
199
|
+
children?: RemixNode;
|
|
200
|
+
/** Padding shorthand applied inside the section, in CSS syntax. */
|
|
201
|
+
padding?: string;
|
|
202
|
+
/** Fill behind the section; omitted means it inherits whatever it sits on. */
|
|
203
|
+
background?: string;
|
|
204
|
+
/** Horizontal alignment of the content. */
|
|
205
|
+
align?: "left" | "center" | "right";
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* A full-width band of content, as a single-cell table. Padding sits on the
|
|
210
|
+
* cell and the fill on the table — the split Outlook needs, since it drops
|
|
211
|
+
* padding declared directly on a `<table>`, so the fill paints the whole band.
|
|
212
|
+
*
|
|
213
|
+
* @example <Email.Section padding="24px 0"><Email.Text>Grouped copy</Email.Text></Email.Section>
|
|
214
|
+
*/
|
|
215
|
+
export declare function Section(handle: Handle<Section.Props>): () => import("remix/ui").RemixElement;
|
|
216
|
+
export declare namespace Row {
|
|
217
|
+
/** Props accepted by {@link Row}. */
|
|
218
|
+
interface Props {
|
|
219
|
+
/** The row's cells, which should be {@link Column} elements. */
|
|
220
|
+
children?: RemixNode;
|
|
221
|
+
/** Space below the row in pixels. */
|
|
222
|
+
gap?: number;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Puts its columns side by side as one table row, the one horizontal
|
|
227
|
+
* arrangement no client gets wrong, since Outlook supports neither flex nor
|
|
228
|
+
* grid; width stays fixed, so keep the column count low for phones.
|
|
229
|
+
*
|
|
230
|
+
* @example <Email.Row><Email.Column width="50%">Left</Email.Column><Email.Column>Right</Email.Column></Email.Row>
|
|
231
|
+
*/
|
|
232
|
+
export declare function Row(handle: Handle<Row.Props>): () => import("remix/ui").RemixElement;
|
|
233
|
+
export declare namespace Column {
|
|
234
|
+
/** Props accepted by {@link Column}. */
|
|
235
|
+
interface Props {
|
|
236
|
+
/** Content of the cell. */
|
|
237
|
+
children?: RemixNode;
|
|
238
|
+
/**
|
|
239
|
+
* Width as a CSS length or percentage; a bare number is read as pixels. Omitted
|
|
240
|
+
* lets the table divide the space.
|
|
241
|
+
*/
|
|
242
|
+
width?: string | number;
|
|
243
|
+
/** Horizontal alignment of the content. */
|
|
244
|
+
align?: "left" | "center" | "right";
|
|
245
|
+
/** Vertical alignment of the content against the tallest cell in the row. */
|
|
246
|
+
valign?: "top" | "middle" | "bottom";
|
|
247
|
+
/** Padding shorthand inside the cell, in CSS syntax. */
|
|
248
|
+
padding?: string;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* One cell of a {@link Row}.
|
|
253
|
+
*
|
|
254
|
+
* The width is set as both the `width` attribute and a style, because Outlook reads
|
|
255
|
+
* the attribute and ignores the style while everything newer does the reverse.
|
|
256
|
+
*
|
|
257
|
+
* @example <Email.Column width="120" valign="top"><Email.Img src={logo} alt="Acme" /></Email.Column>
|
|
258
|
+
*/
|
|
259
|
+
export declare function Column(handle: Handle<Column.Props>): () => import("remix/ui").RemixElement;
|
|
260
|
+
export declare namespace Link {
|
|
261
|
+
/** Props accepted by {@link Link}. */
|
|
262
|
+
interface Props {
|
|
263
|
+
/** Where the link goes. */
|
|
264
|
+
href: string;
|
|
265
|
+
/** Link text. */
|
|
266
|
+
children?: RemixNode;
|
|
267
|
+
/** Link color; omitted inherits the surrounding copy, so dark mode carries it. */
|
|
268
|
+
color?: string;
|
|
269
|
+
/** Whether the link is underlined. */
|
|
270
|
+
underline?: boolean;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* An inline link, opening in a new tab, whose color is inherited so it always
|
|
275
|
+
* matches the copy around it and never needs its own entry in the dark rules;
|
|
276
|
+
* the underline alone marks it as a link, as a mail reader expects.
|
|
277
|
+
*
|
|
278
|
+
* @example <Email.Link href={url}>the settings page</Email.Link>
|
|
279
|
+
*/
|
|
280
|
+
export declare function Link(handle: Handle<Link.Props>): () => import("remix/ui").RemixElement;
|
|
281
|
+
export declare namespace Img {
|
|
282
|
+
/** Props accepted by {@link Img}. */
|
|
283
|
+
interface Props {
|
|
284
|
+
/** Absolute URL of the image; a relative one resolves against nothing in an inbox. */
|
|
285
|
+
src: string;
|
|
286
|
+
/**
|
|
287
|
+
* What the image says, for the reader who never sees it. Required since most
|
|
288
|
+
* readers are that reader — every major client blocks remote images until
|
|
289
|
+
* asked — so pass an empty string for an image that carries no meaning.
|
|
290
|
+
*/
|
|
291
|
+
alt: string;
|
|
292
|
+
/** Width in pixels. */
|
|
293
|
+
width?: number;
|
|
294
|
+
/** Height in pixels; omitted lets the image keep its ratio. */
|
|
295
|
+
height?: number;
|
|
296
|
+
/** Corner radius in pixels. */
|
|
297
|
+
radius?: number;
|
|
298
|
+
/** Space below the image in pixels. */
|
|
299
|
+
gap?: number;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* An image with the four resets an inbox needs: `display:block` so no line-height gap
|
|
304
|
+
* opens underneath it, and a cleared border, outline and underline so a client that
|
|
305
|
+
* inherits link styling leaves it unframed.
|
|
306
|
+
*
|
|
307
|
+
* @example <Email.Img src="https://acme.com/logo.png" alt="Acme" width={120} />
|
|
308
|
+
*/
|
|
309
|
+
export declare function Img(handle: Handle<Img.Props>): () => import("remix/ui").RemixElement;
|
|
310
|
+
export declare namespace Hr {
|
|
311
|
+
/** Props accepted by {@link Hr}. */
|
|
312
|
+
interface Props {
|
|
313
|
+
/** Rule color. */
|
|
314
|
+
color?: string;
|
|
315
|
+
/** Space above and below the rule in pixels. */
|
|
316
|
+
gap?: number;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* A horizontal rule drawn as a top border, since clients disagree about what an
|
|
321
|
+
* `<hr>` looks like by default and several render the native one as an inset
|
|
322
|
+
* two-tone groove.
|
|
323
|
+
*
|
|
324
|
+
* @example <Email.Hr />
|
|
325
|
+
*/
|
|
326
|
+
export declare function Hr(handle: Handle<Hr.Props>): () => import("remix/ui").RemixElement;
|
|
327
|
+
/** Monospace stack, chosen so no family needs quoting inside a `style` attribute. */
|
|
328
|
+
export declare const MONO_FAMILY = "ui-monospace, SFMono-Regular, Menlo, Consolas, Liberation Mono, monospace";
|
|
329
|
+
/** Fill behind code, light enough to sit inside the card without reading as a block. */
|
|
330
|
+
export declare const CODE_COLOR = "#f4f4f5";
|
|
331
|
+
export declare namespace CodeInline {
|
|
332
|
+
/** Props accepted by {@link CodeInline}. */
|
|
333
|
+
interface Props {
|
|
334
|
+
/** The code, as text. */
|
|
335
|
+
children?: RemixNode;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* A short run of code inside a sentence.
|
|
340
|
+
*
|
|
341
|
+
* The size is `0.9em` rather than a pixel value so it tracks whatever it is set inside —
|
|
342
|
+
* a heading, body copy, a footer — instead of turning into body-sized code in a caption.
|
|
343
|
+
*
|
|
344
|
+
* @example <Email.Text>Set <Email.CodeInline>DEBUG=1</Email.CodeInline> and retry.</Email.Text>
|
|
345
|
+
*/
|
|
346
|
+
export declare function CodeInline(handle: Handle<CodeInline.Props>): () => import("remix/ui").RemixElement;
|