react-email 4.1.0-canary.7 → 4.1.0-canary.9

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 (282) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/{cli/index.mjs → index.js} +445 -422
  3. package/package.json +11 -46
  4. package/src/commands/build.ts +306 -0
  5. package/src/commands/dev.ts +27 -0
  6. package/src/commands/export.ts +204 -0
  7. package/src/commands/start.ts +38 -0
  8. package/src/index.ts +55 -0
  9. package/src/utils/__snapshots__/tree.spec.ts.snap +27 -0
  10. package/src/utils/esbuild/renderring-utilities-exporter.ts +1 -1
  11. package/src/utils/get-emails-directory-metadata.spec.ts +1 -1
  12. package/src/utils/get-preview-server-location.ts +51 -0
  13. package/src/utils/index.ts +2 -6
  14. package/src/utils/packageJson.ts +4 -0
  15. package/src/utils/preview/get-env-variables-for-preview-app.ts +14 -0
  16. package/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts +281 -0
  17. package/src/utils/preview/hot-reloading/create-dependency-graph.ts +321 -0
  18. package/src/utils/preview/hot-reloading/get-imported-modules.spec.ts +151 -0
  19. package/src/utils/preview/hot-reloading/get-imported-modules.ts +49 -0
  20. package/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts +11 -0
  21. package/src/utils/preview/hot-reloading/resolve-path-aliases.ts +32 -0
  22. package/src/utils/preview/hot-reloading/setup-hot-reloading.ts +121 -0
  23. package/src/utils/preview/hot-reloading/test/tsconfig.json +8 -0
  24. package/src/utils/preview/index.ts +2 -0
  25. package/src/utils/preview/serve-static-file.ts +51 -0
  26. package/src/utils/preview/start-dev-server.ts +234 -0
  27. package/src/utils/tree.spec.ts +5 -0
  28. package/src/utils/tree.ts +76 -0
  29. package/src/utils/types/hot-reload-change.ts +1 -1
  30. package/src/utils/types/hot-reload-event.ts +1 -1
  31. package/tsconfig.json +4 -10
  32. package/dist/preview/.next/BUILD_ID +0 -1
  33. package/dist/preview/.next/app-build-manifest.json +0 -44
  34. package/dist/preview/.next/app-path-routes-manifest.json +0 -6
  35. package/dist/preview/.next/build-manifest.json +0 -33
  36. package/dist/preview/.next/diagnostics/build-diagnostics.json +0 -6
  37. package/dist/preview/.next/diagnostics/framework.json +0 -1
  38. package/dist/preview/.next/export-marker.json +0 -6
  39. package/dist/preview/.next/images-manifest.json +0 -57
  40. package/dist/preview/.next/next-minimal-server.js.nft.json +0 -1
  41. package/dist/preview/.next/next-server.js.nft.json +0 -1
  42. package/dist/preview/.next/package.json +0 -1
  43. package/dist/preview/.next/prerender-manifest.json +0 -41
  44. package/dist/preview/.next/react-loadable-manifest.json +0 -1
  45. package/dist/preview/.next/required-server-files.json +0 -311
  46. package/dist/preview/.next/routes-manifest.json +0 -64
  47. package/dist/preview/.next/server/app/_not-found/page.js +0 -1
  48. package/dist/preview/.next/server/app/_not-found/page.js.nft.json +0 -1
  49. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  50. package/dist/preview/.next/server/app/favicon.ico/route.js +0 -1
  51. package/dist/preview/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  52. package/dist/preview/.next/server/app/favicon.ico.body +0 -0
  53. package/dist/preview/.next/server/app/favicon.ico.meta +0 -1
  54. package/dist/preview/.next/server/app/page.js +0 -1
  55. package/dist/preview/.next/server/app/page.js.nft.json +0 -1
  56. package/dist/preview/.next/server/app/page_client-reference-manifest.js +0 -1
  57. package/dist/preview/.next/server/app/preview/[...slug]/page.js +0 -321
  58. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +0 -1
  59. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +0 -1
  60. package/dist/preview/.next/server/app-paths-manifest.json +0 -6
  61. package/dist/preview/.next/server/chunks/134.js +0 -6
  62. package/dist/preview/.next/server/chunks/235.js +0 -15
  63. package/dist/preview/.next/server/chunks/343.js +0 -20
  64. package/dist/preview/.next/server/chunks/425.js +0 -1
  65. package/dist/preview/.next/server/chunks/428.js +0 -14
  66. package/dist/preview/.next/server/chunks/963.js +0 -1
  67. package/dist/preview/.next/server/functions-config-manifest.json +0 -4
  68. package/dist/preview/.next/server/interception-route-rewrite-manifest.js +0 -1
  69. package/dist/preview/.next/server/middleware-build-manifest.js +0 -1
  70. package/dist/preview/.next/server/middleware-manifest.json +0 -6
  71. package/dist/preview/.next/server/middleware-react-loadable-manifest.js +0 -1
  72. package/dist/preview/.next/server/next-font-manifest.js +0 -1
  73. package/dist/preview/.next/server/next-font-manifest.json +0 -1
  74. package/dist/preview/.next/server/pages/500.html +0 -1
  75. package/dist/preview/.next/server/pages/_app.js +0 -1
  76. package/dist/preview/.next/server/pages/_app.js.nft.json +0 -1
  77. package/dist/preview/.next/server/pages/_document.js +0 -1
  78. package/dist/preview/.next/server/pages/_document.js.nft.json +0 -1
  79. package/dist/preview/.next/server/pages/_error.js +0 -1
  80. package/dist/preview/.next/server/pages/_error.js.nft.json +0 -1
  81. package/dist/preview/.next/server/pages-manifest.json +0 -5
  82. package/dist/preview/.next/server/server-reference-manifest.js +0 -1
  83. package/dist/preview/.next/server/server-reference-manifest.json +0 -1
  84. package/dist/preview/.next/server/webpack-runtime.js +0 -1
  85. package/dist/preview/.next/static/4K22R8mt8Z5akBgUuivvR/_buildManifest.js +0 -1
  86. package/dist/preview/.next/static/4K22R8mt8Z5akBgUuivvR/_ssgManifest.js +0 -1
  87. package/dist/preview/.next/static/chunks/107-3043079e7cb8bcae.js +0 -1
  88. package/dist/preview/.next/static/chunks/293-297b1eb2241f9a70.js +0 -1
  89. package/dist/preview/.next/static/chunks/3bd82e28-cda2c00a924937c5.js +0 -1
  90. package/dist/preview/.next/static/chunks/45-1021fac82f766268.js +0 -1
  91. package/dist/preview/.next/static/chunks/484-25cf313c25750c6a.js +0 -1
  92. package/dist/preview/.next/static/chunks/589-817d8691661d370e.js +0 -1
  93. package/dist/preview/.next/static/chunks/902-c34acb56733e0ce1.js +0 -1
  94. package/dist/preview/.next/static/chunks/app/_not-found/page-4cbc7dce3ad33336.js +0 -1
  95. package/dist/preview/.next/static/chunks/app/layout-ce14b7ba365bfddc.js +0 -1
  96. package/dist/preview/.next/static/chunks/app/page-65fd67d48528e2ba.js +0 -1
  97. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-35fab824504104aa.js +0 -1
  98. package/dist/preview/.next/static/chunks/f33a14d2-ec7c5f0b91818561.js +0 -6
  99. package/dist/preview/.next/static/chunks/framework-b887e9fc751a9906.js +0 -1
  100. package/dist/preview/.next/static/chunks/main-9a03e7ba8acb1900.js +0 -1
  101. package/dist/preview/.next/static/chunks/main-app-dbd8e1ec12eabb66.js +0 -1
  102. package/dist/preview/.next/static/chunks/pages/_app-542a93a5a214e1c0.js +0 -1
  103. package/dist/preview/.next/static/chunks/pages/_error-d5fe1b1612642f76.js +0 -1
  104. package/dist/preview/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  105. package/dist/preview/.next/static/chunks/webpack-31c45daa2bd82a7b.js +0 -1
  106. package/dist/preview/.next/static/css/6f42d128f111d7fa.css +0 -3
  107. package/dist/preview/.next/static/media/05613964ce6c782e-s.p.otf +0 -0
  108. package/dist/preview/.next/static/media/11c6126b9369e85e-s.p.otf +0 -0
  109. package/dist/preview/.next/static/media/26a46d62cd723877-s.woff2 +0 -0
  110. package/dist/preview/.next/static/media/26cb97734d8cb717-s.p.otf +0 -0
  111. package/dist/preview/.next/static/media/55c55f0601d81cf3-s.woff2 +0 -0
  112. package/dist/preview/.next/static/media/581909926a08bbc8-s.woff2 +0 -0
  113. package/dist/preview/.next/static/media/6d93bde91c0c2823-s.woff2 +0 -0
  114. package/dist/preview/.next/static/media/97e0cb1ae144a2a9-s.woff2 +0 -0
  115. package/dist/preview/.next/static/media/a34f9d1faa5f3315-s.p.woff2 +0 -0
  116. package/dist/preview/.next/static/media/bb6462617151f6b7-s.p.otf +0 -0
  117. package/dist/preview/.next/static/media/cf6daef822ab0142-s.p.otf +0 -0
  118. package/dist/preview/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  119. package/dist/preview/.next/static/media/e4051546b3043204-s.p.otf +0 -0
  120. package/dist/preview/.next/static/media/logo.2ce2a759.png +0 -0
  121. package/dist/preview/.next/trace +0 -28
  122. package/dist/preview/.next/types/app/layout.ts +0 -84
  123. package/dist/preview/.next/types/app/page.ts +0 -84
  124. package/dist/preview/.next/types/app/preview/[...slug]/page.ts +0 -84
  125. package/dist/preview/.next/types/cache-life.d.ts +0 -141
  126. package/dist/preview/.next/types/package.json +0 -1
  127. package/module-punycode.d.ts +0 -3
  128. package/next-env.d.ts +0 -5
  129. package/next.config.js +0 -22
  130. package/postcss.config.js +0 -8
  131. package/scripts/build-preview-server.mjs +0 -33
  132. package/scripts/fill-caniemail-data.mjs +0 -36
  133. package/src/actions/email-validation/caniemail-data.ts +0 -85993
  134. package/src/actions/email-validation/check-compatibility.ts +0 -333
  135. package/src/actions/email-validation/check-images.spec.tsx +0 -100
  136. package/src/actions/email-validation/check-images.ts +0 -160
  137. package/src/actions/email-validation/check-links.spec.tsx +0 -113
  138. package/src/actions/email-validation/check-links.ts +0 -113
  139. package/src/actions/email-validation/get-code-location-from-ast-element.ts +0 -18
  140. package/src/actions/email-validation/quick-fetch.ts +0 -14
  141. package/src/actions/get-email-path-from-slug.ts +0 -32
  142. package/src/actions/get-emails-directory-metadata-action.ts +0 -19
  143. package/src/actions/render-email-by-path.tsx +0 -121
  144. package/src/animated-icons-data/help.json +0 -1082
  145. package/src/animated-icons-data/link.json +0 -1309
  146. package/src/animated-icons-data/load.json +0 -443
  147. package/src/animated-icons-data/mail.json +0 -1320
  148. package/src/app/env.ts +0 -15
  149. package/src/app/favicon.ico +0 -0
  150. package/src/app/fonts/SFMono/SFMonoBold.otf +0 -0
  151. package/src/app/fonts/SFMono/SFMonoBoldItalic.otf +0 -0
  152. package/src/app/fonts/SFMono/SFMonoHeavy.otf +0 -0
  153. package/src/app/fonts/SFMono/SFMonoHeavyItalic.otf +0 -0
  154. package/src/app/fonts/SFMono/SFMonoLight.otf +0 -0
  155. package/src/app/fonts/SFMono/SFMonoLightItalic.otf +0 -0
  156. package/src/app/fonts/SFMono/SFMonoMedium.otf +0 -0
  157. package/src/app/fonts/SFMono/SFMonoMediumItalic.otf +0 -0
  158. package/src/app/fonts/SFMono/SFMonoRegular.otf +0 -0
  159. package/src/app/fonts/SFMono/SFMonoRegularItalic.otf +0 -0
  160. package/src/app/fonts/SFMono/SFMonoSemibold.otf +0 -0
  161. package/src/app/fonts/SFMono/SFMonoSemiboldItalic.otf +0 -0
  162. package/src/app/fonts.ts +0 -39
  163. package/src/app/globals.css +0 -15
  164. package/src/app/layout.tsx +0 -45
  165. package/src/app/logo.png +0 -0
  166. package/src/app/page.tsx +0 -46
  167. package/src/app/preview/[...slug]/page.tsx +0 -157
  168. package/src/app/preview/[...slug]/preview.tsx +0 -234
  169. package/src/app/preview/[...slug]/rendering-error.tsx +0 -40
  170. package/src/components/button.tsx +0 -101
  171. package/src/components/code-container.tsx +0 -164
  172. package/src/components/code-snippet.tsx +0 -9
  173. package/src/components/code.tsx +0 -184
  174. package/src/components/heading.tsx +0 -113
  175. package/src/components/icons/icon-arrow-down.tsx +0 -16
  176. package/src/components/icons/icon-base.tsx +0 -26
  177. package/src/components/icons/icon-bug.tsx +0 -19
  178. package/src/components/icons/icon-button.tsx +0 -23
  179. package/src/components/icons/icon-check.tsx +0 -19
  180. package/src/components/icons/icon-clipboard.tsx +0 -40
  181. package/src/components/icons/icon-download.tsx +0 -19
  182. package/src/components/icons/icon-email.tsx +0 -18
  183. package/src/components/icons/icon-file.tsx +0 -19
  184. package/src/components/icons/icon-folder-open.tsx +0 -19
  185. package/src/components/icons/icon-folder.tsx +0 -18
  186. package/src/components/icons/icon-hide-sidebar.tsx +0 -23
  187. package/src/components/icons/icon-image.tsx +0 -19
  188. package/src/components/icons/icon-info.tsx +0 -18
  189. package/src/components/icons/icon-link.tsx +0 -14
  190. package/src/components/icons/icon-monitor.tsx +0 -19
  191. package/src/components/icons/icon-moon.tsx +0 -16
  192. package/src/components/icons/icon-phone.tsx +0 -26
  193. package/src/components/icons/icon-reload.tsx +0 -18
  194. package/src/components/icons/icon-source.tsx +0 -19
  195. package/src/components/icons/icon-stamp.tsx +0 -14
  196. package/src/components/icons/icon-sun.tsx +0 -16
  197. package/src/components/icons/icon-warning.tsx +0 -31
  198. package/src/components/index.ts +0 -7
  199. package/src/components/logo.tsx +0 -63
  200. package/src/components/resizable-wrapper.tsx +0 -173
  201. package/src/components/send.tsx +0 -134
  202. package/src/components/shell.tsx +0 -92
  203. package/src/components/sidebar/file-tree-directory-children.tsx +0 -139
  204. package/src/components/sidebar/file-tree-directory.tsx +0 -92
  205. package/src/components/sidebar/file-tree.tsx +0 -31
  206. package/src/components/sidebar/index.ts +0 -1
  207. package/src/components/sidebar/sidebar.tsx +0 -43
  208. package/src/components/text.tsx +0 -99
  209. package/src/components/toolbar/checking-results.tsx +0 -150
  210. package/src/components/toolbar/code-preview-line-link.tsx +0 -40
  211. package/src/components/toolbar/compatibility.tsx +0 -113
  212. package/src/components/toolbar/linter.tsx +0 -278
  213. package/src/components/toolbar/results.tsx +0 -51
  214. package/src/components/toolbar/spam-assassin.tsx +0 -155
  215. package/src/components/toolbar/toolbar-button.tsx +0 -52
  216. package/src/components/toolbar/use-cached-state.ts +0 -33
  217. package/src/components/toolbar.tsx +0 -349
  218. package/src/components/tooltip-content.tsx +0 -31
  219. package/src/components/tooltip.tsx +0 -19
  220. package/src/components/topbar/active-view-toggle-group.tsx +0 -86
  221. package/src/components/topbar/theme-toggle-group.tsx +0 -87
  222. package/src/components/topbar/view-size-controls.tsx +0 -247
  223. package/src/components/topbar.tsx +0 -59
  224. package/src/contexts/emails.tsx +0 -59
  225. package/src/contexts/fragment-identifier.tsx +0 -48
  226. package/src/contexts/preview.tsx +0 -79
  227. package/src/hooks/use-clamped-state.ts +0 -24
  228. package/src/hooks/use-email-rendering-result.ts +0 -58
  229. package/src/hooks/use-fragment-identifier.ts +0 -14
  230. package/src/hooks/use-hot-reload.ts +0 -31
  231. package/src/hooks/use-icon-animation.ts +0 -41
  232. package/src/hooks/use-iframe-color-scheme.ts +0 -35
  233. package/src/hooks/use-rendering-metadata.ts +0 -36
  234. package/src/utils/__snapshots__/get-email-component.spec.ts.snap +0 -3
  235. package/src/utils/caniemail/all-css-properties.ts +0 -358
  236. package/src/utils/caniemail/ast/__snapshots__/get-object-variables.spec.ts.snap +0 -74
  237. package/src/utils/caniemail/ast/__snapshots__/get-used-style-properties.spec.ts.snap +0 -24
  238. package/src/utils/caniemail/ast/get-object-variables.spec.ts +0 -19
  239. package/src/utils/caniemail/ast/get-object-variables.ts +0 -61
  240. package/src/utils/caniemail/ast/get-used-style-properties.spec.ts +0 -23
  241. package/src/utils/caniemail/ast/get-used-style-properties.ts +0 -91
  242. package/src/utils/caniemail/get-compatibility-stats-for-entry.ts +0 -118
  243. package/src/utils/caniemail/get-css-functions.ts +0 -25
  244. package/src/utils/caniemail/get-css-property-names.ts +0 -32
  245. package/src/utils/caniemail/get-css-property-with-value.ts +0 -14
  246. package/src/utils/caniemail/get-css-unit.ts +0 -3
  247. package/src/utils/caniemail/get-element-attributes.ts +0 -7
  248. package/src/utils/caniemail/get-element-names.ts +0 -20
  249. package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +0 -30
  250. package/src/utils/caniemail/tailwind/get-tailwind-config.ts +0 -187
  251. package/src/utils/caniemail/tailwind/get-tailwind-metadata.spec.ts +0 -25
  252. package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +0 -45
  253. package/src/utils/caniemail/tailwind/setup-tailwind-context.ts +0 -15
  254. package/src/utils/cn.ts +0 -6
  255. package/src/utils/constants.ts +0 -6
  256. package/src/utils/contains-email-template.spec.ts +0 -107
  257. package/src/utils/contains-email-template.ts +0 -33
  258. package/src/utils/copy-text-to-clipboard.ts +0 -7
  259. package/src/utils/get-email-component.spec.ts +0 -41
  260. package/src/utils/get-email-component.ts +0 -134
  261. package/src/utils/get-line-and-column-from-offset.spec.ts +0 -11
  262. package/src/utils/get-line-and-column-from-offset.ts +0 -11
  263. package/src/utils/improve-error-with-sourcemap.ts +0 -85
  264. package/src/utils/js-email-detection.spec.ts +0 -24
  265. package/src/utils/language-map.ts +0 -7
  266. package/src/utils/linting.ts +0 -60
  267. package/src/utils/load-stream.ts +0 -15
  268. package/src/utils/result.ts +0 -49
  269. package/src/utils/run-bundled-code.ts +0 -64
  270. package/src/utils/sanitize.ts +0 -6
  271. package/src/utils/static-node-modules-for-vm.ts +0 -93
  272. package/src/utils/testing/js-email-export-default.js +0 -17
  273. package/src/utils/testing/js-email-test.js +0 -18
  274. package/src/utils/testing/mdx-email-test.js +0 -128
  275. package/src/utils/testing/request-response-email.tsx +0 -9
  276. package/src/utils/types/as.ts +0 -26
  277. package/src/utils/types/email-template.ts +0 -8
  278. package/src/utils/types/error-object.ts +0 -11
  279. package/src/utils/unreachable.ts +0 -8
  280. package/tailwind-internals.d.ts +0 -133
  281. package/tailwind.config.ts +0 -99
  282. /package/src/{components/toolbar/results-table.tsx → utils/preview/hot-reloading/test/some-file.ts} +0 -0
@@ -1,101 +0,0 @@
1
- 'use client';
2
- import { DotLottieReact } from '@lottiefiles/dotlottie-react';
3
- import * as SlotPrimitive from '@radix-ui/react-slot';
4
- import type * as React from 'react';
5
- import animatedLoadIcon from '../animated-icons-data/load.json';
6
- import { cn } from '../utils/cn';
7
- import { unreachable } from '../utils/unreachable';
8
-
9
- type RootProps = React.ComponentProps<'button'>;
10
-
11
- type Appearance = 'white' | 'gradient';
12
- type Size = '1' | '2' | '3' | '4';
13
-
14
- interface ButtonProps extends RootProps {
15
- asChild?: boolean;
16
- appearance?: Appearance;
17
- size?: Size;
18
- loading?: boolean;
19
- }
20
-
21
- export const Button = ({
22
- asChild,
23
- appearance = 'white',
24
- className,
25
- children,
26
- size = '2',
27
- loading,
28
- ref,
29
- ...props
30
- }: ButtonProps) => {
31
- const Root = asChild ? SlotPrimitive.Slot : 'button';
32
-
33
- return (
34
- <Root
35
- ref={ref}
36
- type="button"
37
- {...props}
38
- className={cn(
39
- getSize(size),
40
- getAppearance(appearance),
41
- 'inline-flex items-center justify-center gap-2 border font-medium',
42
- className,
43
- )}
44
- aria-disabled={loading}
45
- >
46
- <span
47
- className={cn(
48
- '-ml-7 opacity-0 transition-opacity duration-200',
49
- loading && 'opacity-100',
50
- )}
51
- >
52
- <DotLottieReact
53
- data={animatedLoadIcon}
54
- autoplay={false}
55
- className="h-5 w-5"
56
- loop={true}
57
- />
58
- </span>
59
- <SlotPrimitive.Slottable>{children}</SlotPrimitive.Slottable>
60
- </Root>
61
- );
62
- };
63
-
64
- Button.displayName = 'Button';
65
-
66
- const getAppearance = (appearance: Appearance | undefined) => {
67
- switch (appearance) {
68
- case undefined:
69
- case 'white':
70
- return [
71
- 'border-white bg-white text-black transition-colors duration-200 ease-in-out',
72
- 'hover:bg-white/90',
73
- 'focus:bg-white/90 focus:outline-none focus:ring-2 focus:ring-white/20',
74
- 'mt-2 mb-4 aria-disabled:border-transparent aria-disabled:bg-slate-11',
75
- ];
76
- case 'gradient':
77
- return [
78
- 'bg-gradient border-[#34343A] backdrop-blur-[1.25rem]',
79
- 'hover:bg-gradientHover',
80
- 'focus:bg-gradientHover focus:outline-none focus:ring-2 focus:ring-white/20',
81
- ];
82
- default:
83
- unreachable(appearance);
84
- }
85
- };
86
-
87
- const getSize = (size: Size | undefined) => {
88
- switch (size) {
89
- case '1':
90
- return '';
91
- case undefined:
92
- case '2':
93
- return 'text-[.875rem] h-8 px-3 rounded-md gap-2';
94
- case '3':
95
- return 'text-[.875rem] h-10 px-4 rounded-md gap-2';
96
- case '4':
97
- return 'text-base h-11 px-4 rounded-md gap-2';
98
- default:
99
- unreachable(size);
100
- }
101
- };
@@ -1,164 +0,0 @@
1
- import { LayoutGroup, motion } from 'framer-motion';
2
- import type { Language } from 'prism-react-renderer';
3
- import * as React from 'react';
4
- import { copyTextToClipboard } from '../utils';
5
- import { tabTransition } from '../utils/constants';
6
- import languageMap from '../utils/language-map';
7
- import { Code } from './code';
8
- import { IconButton } from './icons/icon-button';
9
- import { IconCheck } from './icons/icon-check';
10
- import { IconClipboard } from './icons/icon-clipboard';
11
- import { IconDownload } from './icons/icon-download';
12
- import { Tooltip } from './tooltip';
13
-
14
- interface CodeContainerProps {
15
- markups: MarkupProps[];
16
- activeLang: string;
17
- setActiveLang: (lang: string) => void;
18
- }
19
-
20
- interface MarkupProps {
21
- language: Language;
22
- content: string;
23
- }
24
-
25
- export const CodeContainer: React.FC<Readonly<CodeContainerProps>> = ({
26
- markups,
27
- activeLang,
28
- setActiveLang,
29
- }) => {
30
- const activeMarkup = markups.find(({ language }) => activeLang === language);
31
- if (!activeMarkup) {
32
- throw new Error('No markup found for the active language!', {
33
- cause: {
34
- activeLang,
35
- markups,
36
- },
37
- });
38
- }
39
-
40
- return (
41
- <div
42
- className="relative max-h-[650px] w-full h-full whitespace-pre rounded-md border border-slate-6 text-sm"
43
- style={{
44
- lineHeight: '130%',
45
- background:
46
- 'linear-gradient(145.37deg, rgba(255, 255, 255, 0.09) -8.75%, rgba(255, 255, 255, 0.027) 83.95%)',
47
- boxShadow: 'rgb(0 0 0 / 10%) 0px 5px 30px -5px',
48
- }}
49
- >
50
- <div className="h-9 border-b border-slate-6">
51
- <div className="flex">
52
- <LayoutGroup id="code">
53
- {markups.map(({ language }) => {
54
- const isCurrentLang = activeLang === language;
55
- return (
56
- <motion.button
57
- className={`relative px-4 py-[8px] font-sans text-sm font-medium transition duration-200 ease-in-out hover:text-slate-12 ${
58
- activeLang !== language ? 'text-slate-11' : 'text-slate-12'
59
- }`}
60
- key={language}
61
- onClick={() => {
62
- setActiveLang(language);
63
- }}
64
- >
65
- {isCurrentLang ? (
66
- <motion.span
67
- animate={{ opacity: 1 }}
68
- className="absolute bottom-0 left-0 right-0 top-0 bg-slate-4"
69
- exit={{ opacity: 0 }}
70
- initial={{ opacity: 0 }}
71
- layoutId="code"
72
- transition={tabTransition}
73
- />
74
- ) : null}
75
- {languageMap[language]}
76
- </motion.button>
77
- );
78
- })}
79
- </LayoutGroup>
80
- </div>
81
- <CopyToClipboardButton content={activeMarkup.content} />
82
- <DownloadButton
83
- content={activeMarkup.content}
84
- filename={`email.${activeMarkup.language}`}
85
- />
86
- </div>
87
- <div className="h-[calc(100%-2.25rem)]">
88
- <Code language={activeLang}>{activeMarkup.content}</Code>
89
- </div>
90
- </div>
91
- );
92
- };
93
-
94
- interface CopyToClipboardButtonProps {
95
- content: string;
96
- }
97
-
98
- const CopyToClipboardButton = ({ content }: CopyToClipboardButtonProps) => {
99
- const [isCopied, setIsCopied] = React.useState(false);
100
-
101
- const unsetIsCopiedTimeout = React.useRef<NodeJS.Timeout>(undefined);
102
- React.useEffect(() => {
103
- setIsCopied(false);
104
- clearTimeout(unsetIsCopiedTimeout.current);
105
- unsetIsCopiedTimeout.current = undefined;
106
- }, [content]);
107
-
108
- return (
109
- <Tooltip>
110
- <Tooltip.Trigger
111
- asChild
112
- className="absolute right-2 top-2 hidden md:block"
113
- >
114
- <IconButton
115
- onClick={async () => {
116
- setIsCopied(true);
117
- await copyTextToClipboard(content);
118
- unsetIsCopiedTimeout.current = setTimeout(() => {
119
- setIsCopied(false);
120
- }, 3000);
121
- }}
122
- >
123
- {isCopied ? <IconCheck /> : <IconClipboard />}
124
- </IconButton>
125
- </Tooltip.Trigger>
126
- <Tooltip.Content>Copy to Clipboard</Tooltip.Content>
127
- </Tooltip>
128
- );
129
- };
130
-
131
- interface DownloadButtonProps {
132
- content: string;
133
- filename: string;
134
- }
135
-
136
- const DownloadButton = ({ content, filename }: DownloadButtonProps) => {
137
- const generatedUrl = React.useMemo(() => {
138
- const file = new File([content], filename);
139
- return URL.createObjectURL(file);
140
- }, [content, filename]);
141
- const url = React.useSyncExternalStore(
142
- () => () => {},
143
- () => generatedUrl,
144
- () => undefined,
145
- );
146
-
147
- return (
148
- <Tooltip>
149
- <Tooltip.Trigger
150
- asChild
151
- className="text-gray-11 absolute right-8 top-2 hidden md:block"
152
- >
153
- <a
154
- className="text-slate-11 transition duration-200 ease-in-out hover:text-slate-12"
155
- download={filename}
156
- href={url}
157
- >
158
- <IconDownload />
159
- </a>
160
- </Tooltip.Trigger>
161
- <Tooltip.Content>Download</Tooltip.Content>
162
- </Tooltip>
163
- );
164
- };
@@ -1,9 +0,0 @@
1
- const CodeSnippet = ({ children }) => {
2
- return (
3
- <code className="m-0.5 inline-block rounded-md bg-white/10 p-1 font-mono leading-none text-slate-12">
4
- {children}
5
- </code>
6
- );
7
- };
8
-
9
- export default CodeSnippet;
@@ -1,184 +0,0 @@
1
- 'use client';
2
- import Link from 'next/link';
3
- import { useSearchParams } from 'next/navigation';
4
- import type { Language } from 'prism-react-renderer';
5
- import { Highlight } from 'prism-react-renderer';
6
- import { Fragment, useEffect, useRef } from 'react';
7
- import { useFragmentIdentifier } from '../hooks/use-fragment-identifier';
8
- import { cn } from '../utils';
9
-
10
- interface CodeProps {
11
- children: string;
12
- className?: string;
13
- language?: Language;
14
- }
15
-
16
- const theme = {
17
- plain: {
18
- color: '#EDEDEF',
19
- fontSize: 13,
20
- fontFamily: 'MonoLisa, Menlo, monospace',
21
- },
22
- styles: [
23
- {
24
- types: ['comment'],
25
- style: {
26
- color: '#706F78',
27
- },
28
- },
29
- {
30
- types: ['atrule', 'keyword', 'attr-name', 'selector'],
31
- style: {
32
- color: '#7E7D86',
33
- },
34
- },
35
- {
36
- types: ['punctuation', 'operator'],
37
- style: {
38
- color: '#706F78',
39
- },
40
- },
41
- {
42
- types: ['class-name', 'function', 'tag', 'key-white'],
43
- style: {
44
- color: '#EDEDEF',
45
- },
46
- },
47
- ],
48
- };
49
-
50
- const lineHashRegex = /#L(?<start>\d+)(?:,(?<end>\d+))?/;
51
-
52
- export const Code: React.FC<Readonly<CodeProps>> = ({
53
- children,
54
- language = 'html',
55
- }) => {
56
- const locationHash = useFragmentIdentifier();
57
- const highlight = (() => {
58
- if (locationHash) {
59
- const match = locationHash.match(lineHashRegex);
60
- if (match?.groups?.start) {
61
- const start = Number.parseInt(match.groups.start);
62
- const end = match.groups.end
63
- ? Number.parseInt(match.groups.end)
64
- : start;
65
- return [start, end] as const;
66
- }
67
- }
68
- })();
69
-
70
- const isHighlighting = (line: number) => {
71
- if (!highlight) return false;
72
-
73
- return highlight[0] <= line && highlight[1] >= line;
74
- };
75
-
76
- const scrollerRef = useRef<HTMLDivElement>(null);
77
-
78
- useEffect(() => {
79
- const scroller = scrollerRef.current;
80
- if (highlight && scroller) {
81
- const lineElement = scroller.querySelector(`#L${highlight[0]}`);
82
- if (lineElement instanceof HTMLAnchorElement) {
83
- scroller.scrollTo({
84
- top: Math.max(lineElement.offsetTop - 325, 0),
85
- behavior: 'smooth',
86
- });
87
- }
88
- }
89
- }, [highlight]);
90
-
91
- const searchParams = useSearchParams();
92
-
93
- const value = children.trim();
94
-
95
- return (
96
- <Highlight code={value} language={language} theme={theme}>
97
- {({ tokens, getLineProps, getTokenProps }) => (
98
- <>
99
- <div
100
- className="absolute right-0 top-0 h-px w-[200px]"
101
- style={{
102
- background:
103
- 'linear-gradient(90deg, rgba(56, 189, 248, 0) 0%, rgba(56, 189, 248, 0) 0%, rgba(232, 232, 232, 0.2) 33.02%, rgba(143, 143, 143, 0.6719) 64.41%, rgba(236, 72, 153, 0) 98.93%)',
104
- }}
105
- />
106
- <div
107
- ref={scrollerRef}
108
- className="flex max-h-[650px] h-full p-4 after:w-full after:static after:block after:h-4 after:content-[''] overflow-auto"
109
- >
110
- <div className="text-[#49494f] text-[13px] font-light font-[MonoLisa,_Menlo,_monospace]">
111
- {tokens.map((_, i) => (
112
- <Link
113
- id={`L${i + 1}`}
114
- key={i}
115
- href={{
116
- hash: `#L${i + 1}`,
117
- search: searchParams.toString(),
118
- }}
119
- scroll={false}
120
- className={cn(
121
- 'align-middle block scroll-mt-[325px] rounded-l-sm select-none pr-3 cursor-pointer hover:text-slate-12',
122
- isHighlighting(i + 1) &&
123
- 'text-cyan-11 hover:text-cyan-11 bg-cyan-5',
124
- )}
125
- type="button"
126
- >
127
- {i + 1}
128
- </Link>
129
- ))}
130
- </div>
131
- <pre>
132
- {tokens.map((line, i) => {
133
- const lineProps = getLineProps({
134
- line,
135
- key: i,
136
- });
137
- return (
138
- <div
139
- {...lineProps}
140
- className={cn(
141
- 'whitespace-pre flex transition-colors rounded-r-sm',
142
- isHighlighting(i + 1) && 'bg-cyan-5',
143
- {
144
- "before:mr-2 before:text-slate-11 before:content-['$']":
145
- language === 'bash' && tokens.length === 1,
146
- },
147
- )}
148
- key={i}
149
- >
150
- {line.map((token, key) => {
151
- const tokenProps = getTokenProps({
152
- token,
153
- });
154
- const isException =
155
- token.content === 'from' &&
156
- line[key + 1]?.content === ':';
157
- const newTypes = isException
158
- ? [...token.types, 'key-white']
159
- : token.types;
160
- token.types = newTypes;
161
-
162
- return (
163
- <Fragment key={key}>
164
- <span {...tokenProps} />
165
- </Fragment>
166
- );
167
- })}
168
- </div>
169
- );
170
- })}
171
- </pre>
172
- <div
173
- className="absolute bottom-0 left-0 h-px w-[200px]"
174
- style={{
175
- background:
176
- 'linear-gradient(90deg, rgba(56, 189, 248, 0) 0%, rgba(56, 189, 248, 0) 0%, rgba(232, 232, 232, 0.2) 33.02%, rgba(143, 143, 143, 0.6719) 64.41%, rgba(236, 72, 153, 0) 98.93%)',
177
- }}
178
- />
179
- </div>
180
- </>
181
- )}
182
- </Highlight>
183
- );
184
- };
@@ -1,113 +0,0 @@
1
- import * as SlotPrimitive from '@radix-ui/react-slot';
2
- import * as React from 'react';
3
- import { type As, cn, unreachable } from '../utils';
4
-
5
- export type HeadingSize =
6
- | '1'
7
- | '2'
8
- | '3'
9
- | '4'
10
- | '5'
11
- | '6'
12
- | '7'
13
- | '8'
14
- | '9'
15
- | '10';
16
- export type HeadingColor = 'white' | 'gray';
17
- export type HeadingWeight = 'medium' | 'bold';
18
-
19
- interface HeadingOwnProps {
20
- size?: HeadingSize;
21
- color?: HeadingColor;
22
- weight?: HeadingWeight;
23
- }
24
-
25
- type HeadingProps = As<'h1', 'h2', 'h3', 'h4', 'h5', 'h6'> & HeadingOwnProps;
26
-
27
- export const Heading = React.forwardRef<
28
- HTMLHeadingElement,
29
- Readonly<HeadingProps>
30
- >(
31
- (
32
- {
33
- as: Tag = 'h1',
34
- size = '3',
35
- className,
36
- color = 'white',
37
- children,
38
- weight = 'bold',
39
- ...props
40
- },
41
- forwardedRef,
42
- ) => (
43
- <SlotPrimitive.Slot
44
- className={cn(
45
- className,
46
- getSizesClassNames(size),
47
- getColorClassNames(color),
48
- getWeightClassNames(weight),
49
- )}
50
- ref={forwardedRef}
51
- {...props}
52
- >
53
- <Tag>{children}</Tag>
54
- </SlotPrimitive.Slot>
55
- ),
56
- );
57
-
58
- const getSizesClassNames = (size: HeadingSize | undefined) => {
59
- switch (size) {
60
- case '1':
61
- return 'text-xs';
62
- case '2':
63
- return 'text-sm';
64
- case undefined:
65
- case '3':
66
- return 'text-base';
67
- case '4':
68
- return 'text-lg';
69
- case '5':
70
- return 'text-xl tracking-[-0.16px]';
71
- case '6':
72
- return 'text-2xl tracking-[-0.288px]';
73
- case '7':
74
- return 'text-[28px] leading-[34px] tracking-[-0.416px]';
75
- case '8':
76
- return 'text-[35px] leading-[42px] tracking-[-0.64px]';
77
- case '9':
78
- return 'text-6xl leading-[73px] tracking-[-0.896px]';
79
- case '10':
80
- return [
81
- 'text-[38px] leading-[46px]',
82
- 'md:text-[70px] md:leading-[85px] tracking-[-1.024px;]',
83
- ];
84
- default:
85
- return unreachable(size);
86
- }
87
- };
88
-
89
- const getColorClassNames = (color: HeadingColor | undefined) => {
90
- switch (color) {
91
- case 'gray':
92
- return 'text-slate-11';
93
- case 'white':
94
- case undefined:
95
- return 'text-slate-12';
96
- default:
97
- return unreachable(color);
98
- }
99
- };
100
-
101
- const getWeightClassNames = (weight: HeadingWeight | undefined) => {
102
- switch (weight) {
103
- case 'medium':
104
- return 'font-medium';
105
- case 'bold':
106
- case undefined:
107
- return 'font-bold';
108
- default:
109
- return unreachable(weight);
110
- }
111
- };
112
-
113
- Heading.displayName = 'Heading';
@@ -1,16 +0,0 @@
1
- import * as React from 'react';
2
- import type { IconElement, IconProps } from './icon-base';
3
- import { IconBase } from './icon-base';
4
-
5
- export const IconArrowDown = React.forwardRef<IconElement, Readonly<IconProps>>(
6
- ({ ...props }, forwardedRef) => (
7
- <IconBase ref={forwardedRef} {...props}>
8
- <path
9
- d="M12 16L6 9.85966L6.84 9L12 14.2808L17.16 9L18 9.85966L12 16Z"
10
- fill="currentColor"
11
- />
12
- </IconBase>
13
- ),
14
- );
15
-
16
- IconArrowDown.displayName = 'IconArrowDown';
@@ -1,26 +0,0 @@
1
- import * as React from 'react';
2
-
3
- export type IconElement = React.ElementRef<'svg'>;
4
- export type RootProps = React.ComponentPropsWithoutRef<'svg'>;
5
-
6
- export interface IconProps extends RootProps {
7
- size?: number;
8
- }
9
-
10
- export const IconBase = React.forwardRef<IconElement, Readonly<IconProps>>(
11
- ({ size = 20, children, ...props }, forwardedRef) => (
12
- <svg
13
- fill="none"
14
- height={size}
15
- ref={forwardedRef}
16
- viewBox="0 0 24 24"
17
- width={size}
18
- xmlns="http://www.w3.org/2000/svg"
19
- {...props}
20
- >
21
- {children}
22
- </svg>
23
- ),
24
- );
25
-
26
- IconBase.displayName = 'IconBase';
@@ -1,19 +0,0 @@
1
- import { forwardRef } from 'react';
2
- import type { IconElement, IconProps } from './icon-base';
3
- import { IconBase } from './icon-base';
4
-
5
- export const IconBug = forwardRef<IconElement, IconProps>((props, ref) => (
6
- <IconBase {...props} ref={ref}>
7
- <g
8
- fill="none"
9
- stroke="currentColor"
10
- strokeLinecap="round"
11
- strokeLinejoin="round"
12
- strokeWidth="2"
13
- >
14
- <path d="m8 2l1.88 1.88m4.24 0L16 2M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" />
15
- <path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6m0 0v-9" />
16
- <path d="M6.53 9C4.6 8.8 3 7.1 3 5m3 8H2m1 8c0-2.1 1.7-3.9 3.8-4M20.97 5c0 2.1-1.6 3.8-3.5 4M22 13h-4m-.8 4c2.1.1 3.8 1.9 3.8 4" />
17
- </g>
18
- </IconBase>
19
- ));
@@ -1,23 +0,0 @@
1
- import * as React from 'react';
2
- import { cn } from '../../utils';
3
-
4
- export type IconButtonProps = React.ComponentPropsWithoutRef<'button'>;
5
-
6
- export const IconButton = React.forwardRef<
7
- HTMLButtonElement,
8
- Readonly<IconButtonProps>
9
- >(({ children, className, ...props }, forwardedRef) => (
10
- <button
11
- type="button"
12
- {...props}
13
- className={cn(
14
- 'focus:ring-gray-8 rounded text-slate-11 transition duration-200 ease-in-out hover:text-slate-12 focus:text-slate-12 focus:outline-none focus:ring-2',
15
- className,
16
- )}
17
- ref={forwardedRef}
18
- >
19
- {children}
20
- </button>
21
- ));
22
-
23
- IconButton.displayName = 'IconButton';
@@ -1,19 +0,0 @@
1
- import * as React from 'react';
2
- import type { IconElement, IconProps } from './icon-base';
3
- import { IconBase } from './icon-base';
4
-
5
- export const IconCheck = React.forwardRef<IconElement, Readonly<IconProps>>(
6
- ({ ...props }, forwardedRef) => (
7
- <IconBase ref={forwardedRef} {...props}>
8
- <path
9
- d="M16.25 8.75L10.406 15.25L7.75 12.75"
10
- stroke="currentColor"
11
- strokeLinecap="round"
12
- strokeLinejoin="round"
13
- strokeWidth="1.5"
14
- />
15
- </IconBase>
16
- ),
17
- );
18
-
19
- IconCheck.displayName = 'IconCheck';