@lupinum/nuxt-email 0.1.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +172 -0
  4. package/THIRD_PARTY_NOTICES +75 -0
  5. package/dist/chunks/setup.mjs +149 -0
  6. package/dist/module.d.mts +15 -0
  7. package/dist/module.json +12 -0
  8. package/dist/module.mjs +300 -0
  9. package/dist/runtime/code-block/create-component.d.ts +16 -0
  10. package/dist/runtime/code-block/create-component.js +106 -0
  11. package/dist/runtime/components/EBody.d.ts +4 -0
  12. package/dist/runtime/components/EBody.js +75 -0
  13. package/dist/runtime/components/EButton.d.ts +11 -0
  14. package/dist/runtime/components/EButton.js +89 -0
  15. package/dist/runtime/components/EButton.padding.d.ts +12 -0
  16. package/dist/runtime/components/EButton.padding.js +118 -0
  17. package/dist/runtime/components/ECodeInline.d.ts +13 -0
  18. package/dist/runtime/components/ECodeInline.js +43 -0
  19. package/dist/runtime/components/EColumn.d.ts +4 -0
  20. package/dist/runtime/components/EColumn.js +12 -0
  21. package/dist/runtime/components/EContainer.d.ts +4 -0
  22. package/dist/runtime/components/EContainer.js +35 -0
  23. package/dist/runtime/components/EFont.d.ts +24 -0
  24. package/dist/runtime/components/EFont.js +124 -0
  25. package/dist/runtime/components/EHead.d.ts +4 -0
  26. package/dist/runtime/components/EHead.js +17 -0
  27. package/dist/runtime/components/EHeading.d.ts +7 -0
  28. package/dist/runtime/components/EHeading.js +23 -0
  29. package/dist/runtime/components/EHr.d.ts +4 -0
  30. package/dist/runtime/components/EHr.js +26 -0
  31. package/dist/runtime/components/EHtml.d.ts +4 -0
  32. package/dist/runtime/components/EHtml.js +17 -0
  33. package/dist/runtime/components/EImg.d.ts +7 -0
  34. package/dist/runtime/components/EImg.js +44 -0
  35. package/dist/runtime/components/ELink.d.ts +6 -0
  36. package/dist/runtime/components/ELink.js +35 -0
  37. package/dist/runtime/components/EMarkdown.d.ts +8 -0
  38. package/dist/runtime/components/EMarkdown.js +64 -0
  39. package/dist/runtime/components/EPreview.d.ts +8 -0
  40. package/dist/runtime/components/EPreview.js +85 -0
  41. package/dist/runtime/components/ERow.d.ts +4 -0
  42. package/dist/runtime/components/ERow.js +53 -0
  43. package/dist/runtime/components/ESection.d.ts +4 -0
  44. package/dist/runtime/components/ESection.js +35 -0
  45. package/dist/runtime/components/ETailwind.d.ts +24 -0
  46. package/dist/runtime/components/ETailwind.js +49 -0
  47. package/dist/runtime/components/EText.d.ts +4 -0
  48. package/dist/runtime/components/EText.js +20 -0
  49. package/dist/runtime/components/EText.margins.d.ts +18 -0
  50. package/dist/runtime/components/EText.margins.js +81 -0
  51. package/dist/runtime/components/attributes.d.ts +4 -0
  52. package/dist/runtime/components/attributes.js +11 -0
  53. package/dist/runtime/components/email-component-names.d.ts +2 -0
  54. package/dist/runtime/components/email-component-names.js +20 -0
  55. package/dist/runtime/components/email-component-registry.d.ts +8 -0
  56. package/dist/runtime/components/email-component-registry.js +2 -0
  57. package/dist/runtime/components/index.d.ts +23 -0
  58. package/dist/runtime/components/index.js +18 -0
  59. package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.d.ts +2 -0
  60. package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.js +60 -0
  61. package/dist/runtime/components/markdown/render-markdown.d.ts +2 -0
  62. package/dist/runtime/components/markdown/render-markdown.js +137 -0
  63. package/dist/runtime/components/markdown/styles.d.ts +30 -0
  64. package/dist/runtime/components/markdown/styles.js +91 -0
  65. package/dist/runtime/components/presentation-table.d.ts +7 -0
  66. package/dist/runtime/components/presentation-table.js +11 -0
  67. package/dist/runtime/components/style.d.ts +5 -0
  68. package/dist/runtime/components/style.js +32 -0
  69. package/dist/runtime/components/table-padding.d.ts +6 -0
  70. package/dist/runtime/components/table-padding.js +20 -0
  71. package/dist/runtime/define-email.d.ts +2 -0
  72. package/dist/runtime/define-email.js +5 -0
  73. package/dist/runtime/dev-preview/page-script.d.ts +1 -0
  74. package/dist/runtime/dev-preview/page-script.js +403 -0
  75. package/dist/runtime/dev-preview/page.css.d.ts +1 -0
  76. package/dist/runtime/dev-preview/page.css.js +602 -0
  77. package/dist/runtime/dev-preview/page.get.d.ts +4 -0
  78. package/dist/runtime/dev-preview/page.get.js +119 -0
  79. package/dist/runtime/dev-preview/render.get.d.ts +3 -0
  80. package/dist/runtime/dev-preview/render.get.js +123 -0
  81. package/dist/runtime/dev-preview/templates.get.d.ts +7 -0
  82. package/dist/runtime/dev-preview/templates.get.js +14 -0
  83. package/dist/runtime/errors.d.ts +4 -0
  84. package/dist/runtime/errors.js +7 -0
  85. package/dist/runtime/render/define-email.d.ts +56 -0
  86. package/dist/runtime/render/define-email.js +40 -0
  87. package/dist/runtime/render/document.d.ts +4 -0
  88. package/dist/runtime/render/document.js +75 -0
  89. package/dist/runtime/render/errors.d.ts +4 -0
  90. package/dist/runtime/render/errors.js +8 -0
  91. package/dist/runtime/render/plain-text.d.ts +1 -0
  92. package/dist/runtime/render/plain-text.js +15 -0
  93. package/dist/runtime/render/render-component.d.ts +4 -0
  94. package/dist/runtime/render/render-component.js +46 -0
  95. package/dist/runtime/render/render-email-component.d.ts +16 -0
  96. package/dist/runtime/render/render-email-component.js +46 -0
  97. package/dist/runtime/render/types.d.ts +9 -0
  98. package/dist/runtime/render/types.js +0 -0
  99. package/dist/runtime/tailwind/engine/compatibility/escape-class-name.d.ts +8 -0
  100. package/dist/runtime/tailwind/engine/compatibility/escape-class-name.js +10 -0
  101. package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.d.ts +7 -0
  102. package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.js +17 -0
  103. package/dist/runtime/tailwind/engine/compatibility/unescape-class.d.ts +1 -0
  104. package/dist/runtime/tailwind/engine/compatibility/unescape-class.js +3 -0
  105. package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.d.ts +63 -0
  106. package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +107 -0
  107. package/dist/runtime/tailwind/engine/css/extract-rules-per-class.d.ts +6 -0
  108. package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +54 -0
  109. package/dist/runtime/tailwind/engine/css/get-css-property.d.ts +17 -0
  110. package/dist/runtime/tailwind/engine/css/get-css-property.js +3 -0
  111. package/dist/runtime/tailwind/engine/css/get-custom-properties.d.ts +8 -0
  112. package/dist/runtime/tailwind/engine/css/get-custom-properties.js +37 -0
  113. package/dist/runtime/tailwind/engine/css/is-part-inlinable.d.ts +2 -0
  114. package/dist/runtime/tailwind/engine/css/is-part-inlinable.js +9 -0
  115. package/dist/runtime/tailwind/engine/css/is-rule-inlinable.d.ts +2 -0
  116. package/dist/runtime/tailwind/engine/css/is-rule-inlinable.js +9 -0
  117. package/dist/runtime/tailwind/engine/css/make-inline-styles-for.d.ts +21 -0
  118. package/dist/runtime/tailwind/engine/css/make-inline-styles-for.js +62 -0
  119. package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.d.ts +8 -0
  120. package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.js +138 -0
  121. package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.d.ts +5 -0
  122. package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.js +101 -0
  123. package/dist/runtime/tailwind/engine/css/sanitize-declarations.d.ts +15 -0
  124. package/dist/runtime/tailwind/engine/css/sanitize-declarations.js +358 -0
  125. package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.d.ts +17 -0
  126. package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +30 -0
  127. package/dist/runtime/tailwind/engine/css/split-mixed-rule.d.ts +12 -0
  128. package/dist/runtime/tailwind/engine/css/split-mixed-rule.js +37 -0
  129. package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.d.ts +19 -0
  130. package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.js +44 -0
  131. package/dist/runtime/tailwind/engine/css/unwrap-value.d.ts +2 -0
  132. package/dist/runtime/tailwind/engine/css/unwrap-value.js +6 -0
  133. package/dist/runtime/tailwind/engine/index.d.ts +63 -0
  134. package/dist/runtime/tailwind/engine/index.js +53 -0
  135. package/dist/runtime/tailwind/engine/pixel-based-preset.d.ts +9 -0
  136. package/dist/runtime/tailwind/engine/pixel-based-preset.js +58 -0
  137. package/dist/runtime/tailwind/engine/sanitize-stylesheet.d.ts +10 -0
  138. package/dist/runtime/tailwind/engine/sanitize-stylesheet.js +8 -0
  139. package/dist/runtime/tailwind/engine/setup-tailwind.d.ts +23 -0
  140. package/dist/runtime/tailwind/engine/setup-tailwind.js +98 -0
  141. package/dist/runtime/tailwind/engine/stylesheets.d.ts +4 -0
  142. package/dist/runtime/tailwind/engine/stylesheets.js +1756 -0
  143. package/dist/runtime/tailwind/engine/types.d.ts +25 -0
  144. package/dist/runtime/tailwind/engine/types.js +0 -0
  145. package/dist/runtime/tailwind/errors.d.ts +7 -0
  146. package/dist/runtime/tailwind/errors.js +9 -0
  147. package/dist/runtime/tailwind/inline-utils.d.ts +19 -0
  148. package/dist/runtime/tailwind/inline-utils.js +24 -0
  149. package/dist/runtime/tailwind/nested.d.ts +59 -0
  150. package/dist/runtime/tailwind/nested.js +47 -0
  151. package/dist/runtime/tailwind/post-render.d.ts +7 -0
  152. package/dist/runtime/tailwind/post-render.js +156 -0
  153. package/dist/runtime/template-registry/errors.d.ts +5 -0
  154. package/dist/runtime/template-registry/errors.js +11 -0
  155. package/dist/runtime/template-registry/registry.d.ts +13 -0
  156. package/dist/runtime/testing/index.d.ts +13 -0
  157. package/dist/runtime/testing/index.js +2 -0
  158. package/dist/types.d.mts +5 -0
  159. package/package.json +122 -0
@@ -0,0 +1,602 @@
1
+ export const PREVIEW_PAGE_CSS = `
2
+ :root {
3
+ color-scheme: light;
4
+ --background: oklch(0.975 0 0);
5
+ --surface: oklch(1 0 0);
6
+ --surface-subtle: oklch(0.955 0.008 160);
7
+ --ink: oklch(0.19 0.018 160);
8
+ --muted: oklch(0.44 0.025 160);
9
+ --line: oklch(0.885 0.012 160);
10
+ --line-strong: oklch(0.76 0.025 160);
11
+ --primary: oklch(0.4 0.087 160);
12
+ --primary-hover: oklch(0.34 0.087 160);
13
+ --primary-soft: oklch(0.93 0.035 160);
14
+ --focus: oklch(0.56 0.17 255);
15
+ --danger: oklch(0.49 0.17 28);
16
+ --danger-soft: oklch(0.96 0.025 28);
17
+ --radius: 10px;
18
+ --toolbar-height: 64px;
19
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
20
+ font-synthesis: none;
21
+ }
22
+
23
+ * {
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ html,
28
+ body {
29
+ min-height: 100%;
30
+ }
31
+
32
+ body {
33
+ margin: 0;
34
+ background: var(--background);
35
+ color: var(--ink);
36
+ font-size: 14px;
37
+ line-height: 1.5;
38
+ -webkit-font-smoothing: antialiased;
39
+ }
40
+
41
+ button,
42
+ select,
43
+ a {
44
+ font: inherit;
45
+ }
46
+
47
+ button,
48
+ select {
49
+ color: inherit;
50
+ }
51
+
52
+ button:focus-visible,
53
+ select:focus-visible,
54
+ a:focus-visible {
55
+ outline: 2px solid var(--focus);
56
+ outline-offset: 2px;
57
+ }
58
+
59
+ button {
60
+ border: 0;
61
+ }
62
+
63
+ .shell {
64
+ display: grid;
65
+ grid-template-rows: var(--toolbar-height) minmax(0, 1fr);
66
+ min-height: 100vh;
67
+ }
68
+
69
+ .topbar {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 14px;
73
+ padding: 0 22px;
74
+ background: var(--surface);
75
+ border-bottom: 1px solid var(--line);
76
+ }
77
+
78
+ .mark {
79
+ display: grid;
80
+ width: 32px;
81
+ height: 32px;
82
+ place-items: center;
83
+ border-radius: 8px;
84
+ background: var(--primary);
85
+ color: var(--surface);
86
+ font-weight: 760;
87
+ letter-spacing: -0.02em;
88
+ }
89
+
90
+ .product-name {
91
+ margin: 0;
92
+ font-size: 15px;
93
+ font-weight: 720;
94
+ letter-spacing: -0.015em;
95
+ }
96
+
97
+ .product-context {
98
+ margin: -2px 0 0;
99
+ color: var(--muted);
100
+ font-size: 12px;
101
+ }
102
+
103
+ .status {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 8px;
107
+ min-width: 0;
108
+ margin-left: auto;
109
+ color: var(--muted);
110
+ font-size: 12px;
111
+ }
112
+
113
+ .status-dot {
114
+ width: 7px;
115
+ height: 7px;
116
+ flex: none;
117
+ border-radius: 50%;
118
+ background: var(--primary);
119
+ }
120
+
121
+ .status[data-state="loading"] .status-dot {
122
+ animation: breathe 1.2s ease-in-out infinite;
123
+ }
124
+
125
+ .status[data-state="error"] {
126
+ color: var(--danger);
127
+ }
128
+
129
+ .status[data-state="error"] .status-dot {
130
+ background: var(--danger);
131
+ }
132
+
133
+ @keyframes breathe {
134
+ 50% { opacity: 0.35; transform: scale(0.78); }
135
+ }
136
+
137
+ .workspace {
138
+ display: grid;
139
+ grid-template-columns: 260px minmax(0, 1fr);
140
+ min-height: 0;
141
+ }
142
+
143
+ .sidebar {
144
+ display: flex;
145
+ min-height: 0;
146
+ flex-direction: column;
147
+ gap: 24px;
148
+ padding: 22px 18px;
149
+ background: var(--surface-subtle);
150
+ border-right: 1px solid var(--line);
151
+ }
152
+
153
+ .field-label,
154
+ .section-label {
155
+ display: block;
156
+ margin: 0 0 8px;
157
+ color: var(--muted);
158
+ font-size: 12px;
159
+ font-weight: 650;
160
+ }
161
+
162
+ .template-select {
163
+ width: 100%;
164
+ height: 38px;
165
+ padding: 0 34px 0 11px;
166
+ border: 1px solid var(--line-strong);
167
+ border-radius: 8px;
168
+ background: var(--surface);
169
+ font-weight: 590;
170
+ }
171
+
172
+ .template-select:hover:not(:disabled) {
173
+ border-color: var(--primary);
174
+ }
175
+
176
+ .template-select:disabled {
177
+ cursor: not-allowed;
178
+ opacity: 0.58;
179
+ }
180
+
181
+ .fixture-note {
182
+ min-height: 36px;
183
+ margin: 8px 0 0;
184
+ color: var(--muted);
185
+ font-size: 12px;
186
+ text-wrap: pretty;
187
+ }
188
+
189
+ .tabs {
190
+ display: flex;
191
+ flex-direction: column;
192
+ gap: 4px;
193
+ }
194
+
195
+ .tab {
196
+ display: flex;
197
+ width: 100%;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ min-height: 38px;
201
+ padding: 7px 10px;
202
+ border-radius: 8px;
203
+ background: transparent;
204
+ cursor: pointer;
205
+ text-align: left;
206
+ transition: background-color 160ms ease, color 160ms ease;
207
+ }
208
+
209
+ .tab:hover {
210
+ background: oklch(1 0 0 / 0.72);
211
+ }
212
+
213
+ .tab[aria-selected="true"] {
214
+ background: var(--primary-soft);
215
+ color: var(--primary-hover);
216
+ font-weight: 680;
217
+ }
218
+
219
+ .tab-key {
220
+ color: var(--muted);
221
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
222
+ font-size: 10px;
223
+ }
224
+
225
+ .sidebar-note {
226
+ margin: auto 0 0;
227
+ padding-top: 18px;
228
+ border-top: 1px solid var(--line);
229
+ color: var(--muted);
230
+ font-size: 12px;
231
+ text-wrap: pretty;
232
+ }
233
+
234
+ .main {
235
+ display: grid;
236
+ grid-template-rows: auto auto minmax(0, 1fr);
237
+ min-width: 0;
238
+ min-height: 0;
239
+ padding: 18px;
240
+ gap: 12px;
241
+ }
242
+
243
+ .viewer-toolbar {
244
+ display: flex;
245
+ min-width: 0;
246
+ flex-wrap: wrap;
247
+ align-items: center;
248
+ gap: 10px;
249
+ row-gap: 8px;
250
+ }
251
+
252
+ .viewer-title {
253
+ min-width: 0;
254
+ margin: 0;
255
+ overflow: hidden;
256
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
257
+ font-size: 13px;
258
+ font-weight: 650;
259
+ text-overflow: ellipsis;
260
+ white-space: nowrap;
261
+ }
262
+
263
+ .viewer-actions {
264
+ display: flex;
265
+ flex: none;
266
+ gap: 8px;
267
+ margin-left: auto;
268
+ }
269
+
270
+ .preview-controls {
271
+ display: flex;
272
+ flex: none;
273
+ align-items: center;
274
+ gap: 8px;
275
+ }
276
+
277
+ .preview-controls[hidden] {
278
+ display: none;
279
+ }
280
+
281
+ .viewport-toggle {
282
+ display: inline-flex;
283
+ padding: 2px;
284
+ border: 1px solid var(--line-strong);
285
+ border-radius: 8px;
286
+ background: var(--surface);
287
+ }
288
+
289
+ .segment {
290
+ min-height: 28px;
291
+ padding: 3px 10px;
292
+ border-radius: 6px;
293
+ background: transparent;
294
+ color: var(--muted);
295
+ cursor: pointer;
296
+ font-size: 12px;
297
+ font-weight: 620;
298
+ font-variant-numeric: tabular-nums;
299
+ transition: background-color 160ms ease, color 160ms ease;
300
+ }
301
+
302
+ .segment:hover:not([aria-pressed="true"]) {
303
+ color: var(--ink);
304
+ }
305
+
306
+ .segment[aria-pressed="true"] {
307
+ background: var(--primary-soft);
308
+ color: var(--primary-hover);
309
+ }
310
+
311
+ .meta-bar {
312
+ display: flex;
313
+ min-width: 0;
314
+ align-items: center;
315
+ gap: 12px;
316
+ }
317
+
318
+ .subject {
319
+ display: flex;
320
+ min-width: 0;
321
+ align-items: center;
322
+ gap: 8px;
323
+ margin: 0;
324
+ color: var(--ink);
325
+ font-size: 13px;
326
+ }
327
+
328
+ .subject-label {
329
+ flex: none;
330
+ padding: 2px 7px;
331
+ border-radius: 5px;
332
+ background: var(--primary-soft);
333
+ color: var(--primary-hover);
334
+ font-size: 11px;
335
+ font-weight: 650;
336
+ }
337
+
338
+ .subject-value {
339
+ min-width: 0;
340
+ overflow: hidden;
341
+ font-weight: 600;
342
+ text-overflow: ellipsis;
343
+ white-space: nowrap;
344
+ }
345
+
346
+ .subject[data-state="none"] .subject-label {
347
+ background: var(--surface-subtle);
348
+ color: var(--muted);
349
+ }
350
+
351
+ .subject[data-state="none"] .subject-value {
352
+ color: var(--muted);
353
+ font-weight: 500;
354
+ font-style: italic;
355
+ }
356
+
357
+ .size-badge {
358
+ display: inline-flex;
359
+ flex: none;
360
+ align-items: center;
361
+ margin: 0 0 0 auto;
362
+ padding: 3px 9px;
363
+ border: 1px solid var(--line-strong);
364
+ border-radius: 999px;
365
+ color: var(--muted);
366
+ font-size: 12px;
367
+ font-variant-numeric: tabular-nums;
368
+ font-weight: 600;
369
+ }
370
+
371
+ .size-badge[data-level="warn"] {
372
+ border-color: oklch(0.78 0.1 85);
373
+ background: oklch(0.96 0.05 85);
374
+ color: oklch(0.42 0.1 75);
375
+ }
376
+
377
+ .size-badge[data-level="over"] {
378
+ border-color: oklch(0.75 0.09 28);
379
+ background: var(--danger-soft);
380
+ color: oklch(0.4 0.13 28);
381
+ }
382
+
383
+ .size-badge[hidden] {
384
+ display: none;
385
+ }
386
+
387
+ .action {
388
+ display: inline-flex;
389
+ min-height: 34px;
390
+ align-items: center;
391
+ justify-content: center;
392
+ padding: 6px 11px;
393
+ border: 1px solid var(--line-strong);
394
+ border-radius: 8px;
395
+ background: var(--surface);
396
+ color: var(--ink);
397
+ cursor: pointer;
398
+ font-weight: 620;
399
+ text-decoration: none;
400
+ transition: border-color 160ms ease, background-color 160ms ease;
401
+ }
402
+
403
+ .action:hover:not(:disabled):not([aria-disabled="true"]) {
404
+ border-color: var(--primary);
405
+ background: var(--primary-soft);
406
+ }
407
+
408
+ .action:active:not(:disabled):not([aria-disabled="true"]) {
409
+ background: oklch(0.9 0.04 160);
410
+ }
411
+
412
+ .action:disabled,
413
+ .action[aria-disabled="true"] {
414
+ cursor: not-allowed;
415
+ opacity: 0.52;
416
+ }
417
+
418
+ .viewer {
419
+ position: relative;
420
+ display: flex;
421
+ justify-content: center;
422
+ min-width: 0;
423
+ min-height: 460px;
424
+ overflow: hidden;
425
+ border: 1px solid var(--line);
426
+ border-radius: var(--radius);
427
+ background: var(--surface-subtle);
428
+ }
429
+
430
+ .panel {
431
+ width: 100%;
432
+ height: 100%;
433
+ min-height: inherit;
434
+ border: 0;
435
+ }
436
+
437
+ .panel[hidden] {
438
+ display: none;
439
+ }
440
+
441
+ iframe.panel {
442
+ display: block;
443
+ background: var(--surface);
444
+ }
445
+
446
+ iframe.panel[data-width="600"] {
447
+ width: 600px;
448
+ max-width: 100%;
449
+ border-inline: 1px solid var(--line);
450
+ }
451
+
452
+ iframe.panel[data-width="375"] {
453
+ width: 375px;
454
+ max-width: 100%;
455
+ border-inline: 1px solid var(--line);
456
+ }
457
+
458
+ iframe.panel[data-width="full"] {
459
+ width: 100%;
460
+ }
461
+
462
+ pre.panel {
463
+ margin: 0;
464
+ overflow: auto;
465
+ padding: 22px;
466
+ background: var(--surface);
467
+ color: oklch(0.26 0.03 160);
468
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
469
+ font-size: 12px;
470
+ line-height: 1.65;
471
+ tab-size: 2;
472
+ white-space: pre-wrap;
473
+ overflow-wrap: anywhere;
474
+ }
475
+
476
+ .empty {
477
+ position: absolute;
478
+ inset: 0;
479
+ display: grid;
480
+ place-content: center;
481
+ padding: 28px;
482
+ color: var(--muted);
483
+ text-align: center;
484
+ }
485
+
486
+ .empty[hidden] {
487
+ display: none;
488
+ }
489
+
490
+ .empty strong {
491
+ display: block;
492
+ margin-bottom: 4px;
493
+ color: var(--ink);
494
+ font-size: 14px;
495
+ }
496
+
497
+ .error {
498
+ position: absolute;
499
+ inset: 16px;
500
+ z-index: 1;
501
+ margin: 0;
502
+ overflow: auto;
503
+ padding: 16px;
504
+ border: 1px solid oklch(0.75 0.09 28);
505
+ border-radius: 9px;
506
+ background: var(--danger-soft);
507
+ color: oklch(0.34 0.12 28);
508
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
509
+ font-size: 12px;
510
+ line-height: 1.6;
511
+ white-space: pre-wrap;
512
+ }
513
+
514
+ .error[hidden] {
515
+ display: none;
516
+ }
517
+
518
+ @media (max-width: 760px) {
519
+ .shell {
520
+ display: block;
521
+ }
522
+
523
+ .topbar {
524
+ min-height: var(--toolbar-height);
525
+ }
526
+
527
+ .workspace {
528
+ display: block;
529
+ }
530
+
531
+ .sidebar {
532
+ display: grid;
533
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
534
+ border-right: 0;
535
+ border-bottom: 1px solid var(--line);
536
+ }
537
+
538
+ .tabs {
539
+ flex-direction: row;
540
+ flex-wrap: wrap;
541
+ }
542
+
543
+ .tab {
544
+ width: auto;
545
+ }
546
+
547
+ .tab-key,
548
+ .sidebar-note {
549
+ display: none;
550
+ }
551
+
552
+ .main {
553
+ min-height: 640px;
554
+ padding: 14px;
555
+ }
556
+ }
557
+
558
+ @media (max-width: 500px) {
559
+ .topbar {
560
+ padding: 0 14px;
561
+ }
562
+
563
+ .product-context {
564
+ display: none;
565
+ }
566
+
567
+ .status {
568
+ max-width: 42%;
569
+ }
570
+
571
+ .sidebar {
572
+ display: flex;
573
+ gap: 14px;
574
+ padding: 16px 14px;
575
+ }
576
+
577
+ .viewer-toolbar {
578
+ align-items: flex-start;
579
+ flex-direction: column;
580
+ }
581
+
582
+ .viewer-actions {
583
+ width: 100%;
584
+ margin-left: 0;
585
+ }
586
+
587
+ .action {
588
+ flex: 1;
589
+ }
590
+ }
591
+
592
+ @media (prefers-reduced-motion: reduce) {
593
+ *,
594
+ *::before,
595
+ *::after {
596
+ scroll-behavior: auto !important;
597
+ transition-duration: 0.01ms !important;
598
+ animation-duration: 0.01ms !important;
599
+ animation-iteration-count: 1 !important;
600
+ }
601
+ }
602
+ `;
@@ -0,0 +1,4 @@
1
+ export declare const PREVIEW_PAGE_CSP: string;
2
+ export declare const PREVIEW_PAGE_HTML: string;
3
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, string>;
4
+ export default _default;
@@ -0,0 +1,119 @@
1
+ import { defineEventHandler, setHeaders } from "h3";
2
+ import { PREVIEW_PAGE_CSS } from "./page.css.js";
3
+ import { PREVIEW_PAGE_CLIENT } from "./page-script.js";
4
+ export const PREVIEW_PAGE_CSP = [
5
+ "default-src 'none'",
6
+ "script-src 'unsafe-inline'",
7
+ "style-src 'unsafe-inline'",
8
+ "connect-src 'self'",
9
+ "frame-src 'self'",
10
+ "frame-ancestors 'none'",
11
+ "img-src data:",
12
+ "base-uri 'none'",
13
+ "object-src 'none'",
14
+ "form-action 'none'"
15
+ ].join("; ");
16
+ export const PREVIEW_PAGE_HTML = `<!doctype html>
17
+ <html lang="en" data-nuxt-email-preview="NUXT_EMAIL_PREVIEW_PAGE_V01">
18
+ <head>
19
+ <meta charset="utf-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1">
21
+ <title>Nuxt Email \xB7 Preview</title>
22
+ <style>
23
+ ${PREVIEW_PAGE_CSS}
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <div class="shell">
28
+ <header class="topbar">
29
+ <div class="mark" aria-hidden="true">E</div>
30
+ <div>
31
+ <h1 class="product-name">Nuxt Email</h1>
32
+ <p class="product-context">Canonical development preview</p>
33
+ </div>
34
+ <div id="status" class="status" data-state="loading" role="status" aria-live="polite">
35
+ <span class="status-dot" aria-hidden="true"></span>
36
+ <span id="status-text">Connecting\u2026</span>
37
+ </div>
38
+ </header>
39
+
40
+ <div class="workspace">
41
+ <aside class="sidebar" aria-label="Preview controls">
42
+ <div>
43
+ <label class="field-label" for="template-select">Template</label>
44
+ <select id="template-select" class="template-select" disabled></select>
45
+ <p id="fixture-note" class="fixture-note">Looking for colocated fixtures\u2026</p>
46
+ </div>
47
+
48
+ <div>
49
+ <p class="section-label">Representation</p>
50
+ <div id="tabs" class="tabs" role="tablist" aria-label="Email representation">
51
+ <button id="tab-preview" class="tab" type="button" role="tab" aria-selected="true" aria-controls="panel-preview" data-view="preview">
52
+ <span>Preview</span><span class="tab-key">1</span>
53
+ </button>
54
+ <button id="tab-html" class="tab" type="button" role="tab" aria-selected="false" aria-controls="panel-html" tabindex="-1" data-view="html">
55
+ <span>HTML</span><span class="tab-key">2</span>
56
+ </button>
57
+ <button id="tab-text" class="tab" type="button" role="tab" aria-selected="false" aria-controls="panel-text" tabindex="-1" data-view="text">
58
+ <span>Plain text</span><span class="tab-key">3</span>
59
+ </button>
60
+ </div>
61
+ </div>
62
+
63
+ <p class="sidebar-note">Fixtures stay in development. The preview refreshes automatically and uses the same server renderer as <code>renderEmail()</code>.</p>
64
+ </aside>
65
+
66
+ <main class="main">
67
+ <div class="viewer-toolbar">
68
+ <h2 id="viewer-title" class="viewer-title">No template selected</h2>
69
+ <div id="preview-controls" class="preview-controls" data-active="true">
70
+ <div class="viewport-toggle" role="group" aria-label="Preview viewport width">
71
+ <button id="viewport-600" class="segment" type="button" data-width="600" aria-pressed="true" title="Standard email width">600</button>
72
+ <button id="viewport-375" class="segment" type="button" data-width="375" aria-pressed="false" title="Mobile width">375</button>
73
+ <button id="viewport-full" class="segment" type="button" data-width="full" aria-pressed="false" title="Full available width">Full</button>
74
+ </div>
75
+ </div>
76
+ <div class="viewer-actions">
77
+ <button id="copy-button" class="action" type="button" disabled>Copy HTML</button>
78
+ <a id="open-link" class="action" aria-disabled="true" aria-label="Open rendered email in a new tab" rel="noopener noreferrer" target="_blank">Open</a>
79
+ </div>
80
+ </div>
81
+
82
+ <div class="meta-bar">
83
+ <p id="subject" class="subject" data-state="none" aria-live="polite">
84
+ <span class="subject-label" aria-hidden="true">Subject</span>
85
+ <span id="subject-value" class="subject-value">No template selected</span>
86
+ </p>
87
+ <p id="size-badge" class="size-badge" data-level="ok" role="status" aria-live="polite" title="Rendered HTML size. Gmail clips messages larger than 102 KB.">
88
+ <span id="size-value" class="size-value">\u2014</span>
89
+ </p>
90
+ </div>
91
+
92
+ <section class="viewer" aria-label="Selected email output">
93
+ <iframe id="panel-preview" class="panel" role="tabpanel" aria-labelledby="tab-preview" sandbox data-width="600" title="Email preview"></iframe>
94
+ <pre id="panel-html" class="panel" role="tabpanel" aria-labelledby="tab-html" hidden></pre>
95
+ <pre id="panel-text" class="panel" role="tabpanel" aria-labelledby="tab-text" hidden></pre>
96
+ <div id="empty" class="empty">
97
+ <p><strong>Waiting for a fixture</strong>Select a fixture-backed template to render its exact output.</p>
98
+ </div>
99
+ <pre id="error" class="error" role="alert" hidden></pre>
100
+ </section>
101
+ </main>
102
+ </div>
103
+ </div>
104
+
105
+ <script>
106
+ ${PREVIEW_PAGE_CLIENT}
107
+ <\/script>
108
+ </body>
109
+ </html>`;
110
+ export default defineEventHandler((event) => {
111
+ setHeaders(event, {
112
+ "cache-control": "no-store",
113
+ "content-security-policy": PREVIEW_PAGE_CSP,
114
+ "content-type": "text/html; charset=utf-8",
115
+ "referrer-policy": "no-referrer",
116
+ "x-content-type-options": "nosniff"
117
+ });
118
+ return PREVIEW_PAGE_HTML;
119
+ });