promote-email-templates 0.1.6 → 0.1.7

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 (71) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +15 -14
  3. package/.react-email/.eslintrc.js +0 -52
  4. package/.react-email/.prettierignore +0 -3
  5. package/.react-email/.prettierrc.js +0 -8
  6. package/.react-email/license.md +0 -7
  7. package/.react-email/next.config.js +0 -36
  8. package/.react-email/package.json +0 -1
  9. package/.react-email/postcss.config.js +0 -8
  10. package/.react-email/readme.md +0 -44
  11. package/.react-email/src/actions/get-email-path-from-slug.ts +0 -26
  12. package/.react-email/src/actions/get-emails-directory-metadata.spec.ts +0 -73
  13. package/.react-email/src/actions/get-emails-directory-metadata.ts +0 -91
  14. package/.react-email/src/actions/render-email-by-path.tsx +0 -59
  15. package/.react-email/src/app/favicon.ico +0 -0
  16. package/.react-email/src/app/globals.css +0 -35
  17. package/.react-email/src/app/inter.ts +0 -7
  18. package/.react-email/src/app/layout.tsx +0 -36
  19. package/.react-email/src/app/logo.png +0 -0
  20. package/.react-email/src/app/page.tsx +0 -47
  21. package/.react-email/src/app/preview/[...slug]/page.tsx +0 -65
  22. package/.react-email/src/app/preview/[...slug]/preview.tsx +0 -141
  23. package/.react-email/src/app/preview/[...slug]/rendering-error.tsx +0 -40
  24. package/.react-email/src/components/button.tsx +0 -90
  25. package/.react-email/src/components/code-container.tsx +0 -145
  26. package/.react-email/src/components/code.tsx +0 -112
  27. package/.react-email/src/components/heading.tsx +0 -113
  28. package/.react-email/src/components/icons/icon-arrow-down.tsx +0 -16
  29. package/.react-email/src/components/icons/icon-base.tsx +0 -24
  30. package/.react-email/src/components/icons/icon-button.tsx +0 -23
  31. package/.react-email/src/components/icons/icon-check.tsx +0 -19
  32. package/.react-email/src/components/icons/icon-clipboard.tsx +0 -40
  33. package/.react-email/src/components/icons/icon-download.tsx +0 -19
  34. package/.react-email/src/components/icons/icon-file.tsx +0 -19
  35. package/.react-email/src/components/icons/icon-folder-open.tsx +0 -19
  36. package/.react-email/src/components/icons/icon-folder.tsx +0 -18
  37. package/.react-email/src/components/icons/icon-hide-sidebar.tsx +0 -23
  38. package/.react-email/src/components/icons/icon-monitor.tsx +0 -19
  39. package/.react-email/src/components/icons/icon-phone.tsx +0 -26
  40. package/.react-email/src/components/icons/icon-source.tsx +0 -19
  41. package/.react-email/src/components/index.ts +0 -7
  42. package/.react-email/src/components/logo.tsx +0 -64
  43. package/.react-email/src/components/send.tsx +0 -135
  44. package/.react-email/src/components/shell.tsx +0 -115
  45. package/.react-email/src/components/sidebar/index.ts +0 -1
  46. package/.react-email/src/components/sidebar/sidebar-directory-children.tsx +0 -134
  47. package/.react-email/src/components/sidebar/sidebar-directory.tsx +0 -106
  48. package/.react-email/src/components/sidebar/sidebar.tsx +0 -45
  49. package/.react-email/src/components/text.tsx +0 -99
  50. package/.react-email/src/components/tooltip-content.tsx +0 -32
  51. package/.react-email/src/components/tooltip.tsx +0 -19
  52. package/.react-email/src/components/topbar.tsx +0 -161
  53. package/.react-email/src/contexts/emails.tsx +0 -127
  54. package/.react-email/src/hooks/use-hot-reload.ts +0 -35
  55. package/.react-email/src/hooks/use-rendering-metadata.ts +0 -36
  56. package/.react-email/src/utils/cn.ts +0 -6
  57. package/.react-email/src/utils/constants.ts +0 -6
  58. package/.react-email/src/utils/copy-text-to-clipboard.ts +0 -7
  59. package/.react-email/src/utils/emails-directory-absolute-path.ts +0 -34
  60. package/.react-email/src/utils/get-email-component.ts +0 -108
  61. package/.react-email/src/utils/improve-error-with-sourcemap.ts +0 -55
  62. package/.react-email/src/utils/index.ts +0 -5
  63. package/.react-email/src/utils/language-map.ts +0 -7
  64. package/.react-email/src/utils/static-node-modules-for-vm.ts +0 -92
  65. package/.react-email/src/utils/types/as.ts +0 -26
  66. package/.react-email/src/utils/types/email-template.ts +0 -8
  67. package/.react-email/src/utils/types/error-object.ts +0 -11
  68. package/.react-email/src/utils/types/hot-reload-change.ts +0 -6
  69. package/.react-email/src/utils/types/hot-reload-event.ts +0 -6
  70. package/.react-email/src/utils/unreachable.ts +0 -8
  71. package/.react-email/tailwind.config.ts +0 -94
@@ -1,65 +0,0 @@
1
- import { Suspense } from 'react';
2
- import { getEmailPathFromSlug } from '../../../actions/get-email-path-from-slug';
3
- import { getEmailsDirectoryMetadata } from '../../../actions/get-emails-directory-metadata';
4
- import { renderEmailByPath } from '../../../actions/render-email-by-path';
5
- import { emailsDirectoryAbsolutePath } from '../../../utils/emails-directory-absolute-path';
6
- import Home from '../../page';
7
- import Preview from './preview';
8
-
9
- export const dynamicParams = true;
10
-
11
- export interface PreviewParams {
12
- slug: string[];
13
- }
14
-
15
- export default async function Page({ params }: { params: PreviewParams }) {
16
- // will come in here as segments of a relative path to the email
17
- // ex: ['authentication', 'verify-password.tsx']
18
- const slug = params.slug.join('/');
19
- const emailsDirMetadata = await getEmailsDirectoryMetadata(
20
- emailsDirectoryAbsolutePath,
21
- );
22
-
23
- if (typeof emailsDirMetadata === 'undefined') {
24
- throw new Error(
25
- `Could not find the emails directory specified under ${emailsDirectoryAbsolutePath}!
26
-
27
- This is most likely not an issue with the preview server. Maybe there was a typo on the "--dir" flag?`,
28
- );
29
- }
30
-
31
- const emailPath = await getEmailPathFromSlug(slug);
32
-
33
- const emailRenderingResult = await renderEmailByPath(emailPath);
34
-
35
- if (
36
- 'error' in emailRenderingResult &&
37
- process.env.NEXT_PUBLIC_IS_BUILDING === 'true'
38
- ) {
39
- throw new Error(emailRenderingResult.error.message, {
40
- cause: emailRenderingResult.error,
41
- });
42
- }
43
-
44
- return (
45
- // This suspense is so that this page doesn't throw warnings
46
- // on the build of the preview server de-opting into
47
- // client-side rendering on build
48
- <Suspense fallback={<Home />}>
49
- <Preview
50
- emailPath={emailPath}
51
- renderingResult={emailRenderingResult}
52
- slug={slug}
53
- />
54
- </Suspense>
55
- );
56
- }
57
-
58
- export function generateMetadata({ params }: { params: PreviewParams }) {
59
- return { title: `${params.slug.join('/')} — React Email` };
60
- }
61
-
62
-
63
- export async function generateStaticParams() {
64
- return [{"slug":["admin","abort-order-request"]},{"slug":["admin","revert-payment-request-admin"]},{"slug":["all","new-message-notification"]},{"slug":["all","welcome"]},{"slug":["brand","evidences-accepted-brand"]},{"slug":["brand","evidences-submitted-brand"]},{"slug":["brand","new-job-application-received-brand"]},{"slug":["brand","new-job-created-brand"]},{"slug":["brand","new-order-created-brand"]},{"slug":["brand","order-accepted-brand"]},{"slug":["brand","order-cancelled-brand"]},{"slug":["brand","order-rejected-brand"]},{"slug":["creator","evidences-approved-creator"]},{"slug":["creator","evidences-rejected-creator"]},{"slug":["creator","new-job-application-unapproved-creator"]},{"slug":["creator","new-order-created-creator"]},{"slug":["creator","order-cancelled-creator"]},{"slug":["creator","order-payment-creator"]},{"slug":["shared","components","base-head"]},{"slug":["shared","components","comment-component"]},{"slug":["shared","components","footer"]},{"slug":["shared","components","header"]},{"slug":["shared","components","new-order-info"]},{"slug":["shared","components","payment-amount"]},{"slug":["shared","components","user-Info"]}];
65
- }
@@ -1,141 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname, useRouter, useSearchParams } from 'next/navigation';
4
- import React from 'react';
5
- import { Toaster } from 'sonner';
6
- import { useHotreload } from '../../../hooks/use-hot-reload';
7
- import type { EmailRenderingResult } from '../../../actions/render-email-by-path';
8
- import { CodeContainer } from '../../../components/code-container';
9
- import { Shell } from '../../../components/shell';
10
- import { Tooltip } from '../../../components/tooltip';
11
- import { useEmails } from '../../../contexts/emails';
12
- import { useRenderingMetadata } from '../../../hooks/use-rendering-metadata';
13
- import { RenderingError } from './rendering-error';
14
-
15
- interface PreviewProps {
16
- slug: string;
17
- emailPath: string;
18
- renderingResult: EmailRenderingResult;
19
- }
20
-
21
- const Preview = ({
22
- slug,
23
- emailPath,
24
- renderingResult: initialRenderingResult,
25
- }: PreviewProps) => {
26
- const router = useRouter();
27
- const pathname = usePathname();
28
- const searchParams = useSearchParams();
29
-
30
- const activeView = searchParams.get('view') ?? 'desktop';
31
- const activeLang = searchParams.get('lang') ?? 'jsx';
32
- const { useEmailRenderingResult } = useEmails();
33
-
34
- const renderingResult = useEmailRenderingResult(
35
- emailPath,
36
- initialRenderingResult,
37
- );
38
-
39
- const renderedEmailMetadata = useRenderingMetadata(
40
- emailPath,
41
- renderingResult,
42
- initialRenderingResult,
43
- );
44
-
45
- if (process.env.NEXT_PUBLIC_IS_BUILDING !== 'true') {
46
- // this will not change on runtime so it doesn't violate
47
- // the rules of hooks
48
- // eslint-disable-next-line react-hooks/rules-of-hooks
49
- useHotreload((changes) => {
50
- const changeForThisEmail = changes.find((change) =>
51
- change.filename.includes(slug),
52
- );
53
-
54
- if (typeof changeForThisEmail !== 'undefined') {
55
- if (changeForThisEmail.event === 'unlink') {
56
- router.push('/');
57
- }
58
- }
59
- });
60
- }
61
-
62
- const handleViewChange = (view: string) => {
63
- const params = new URLSearchParams(searchParams);
64
- params.set('view', view);
65
- router.push(`${pathname}?${params.toString()}`);
66
- };
67
-
68
- const handleLangChange = (lang: string) => {
69
- const params = new URLSearchParams(searchParams);
70
- params.set('view', 'source');
71
- params.set('lang', lang);
72
- router.push(`${pathname}?${params.toString()}`);
73
- };
74
-
75
- const hasNoErrors = typeof renderedEmailMetadata !== 'undefined';
76
-
77
- return (
78
- <Shell
79
- activeView={hasNoErrors ? activeView : undefined}
80
- currentEmailOpenSlug={slug}
81
- markup={renderedEmailMetadata?.markup}
82
- setActiveView={hasNoErrors ? handleViewChange : undefined}
83
- >
84
- {/* This relative is so that when there is any error the user can still switch between emails */}
85
- <div className="relative h-full">
86
- {'error' in renderingResult ? (
87
- <RenderingError error={renderingResult.error} />
88
- ) : null}
89
-
90
- {/* If this is undefined means that the initial server render of the email had errors */}
91
- {hasNoErrors ? (
92
- <>
93
- {activeView === 'desktop' && (
94
- <iframe
95
- className="w-full bg-white h-[calc(100vh_-_140px)] lg:h-[calc(100vh_-_70px)]"
96
- srcDoc={renderedEmailMetadata.markup}
97
- title={slug}
98
- />
99
- )}
100
-
101
- {activeView === 'mobile' && (
102
- <iframe
103
- className="w-[360px] bg-white h-[calc(100vh_-_140px)] lg:h-[calc(100vh_-_70px)] mx-auto"
104
- srcDoc={renderedEmailMetadata.markup}
105
- title={slug}
106
- />
107
- )}
108
-
109
- {activeView === 'source' && (
110
- <div className="flex gap-6 mx-auto p-6 max-w-3xl">
111
- <Tooltip.Provider>
112
- <CodeContainer
113
- activeLang={activeLang}
114
- markups={[
115
- {
116
- language: 'jsx',
117
- content: renderedEmailMetadata.reactMarkup,
118
- },
119
- {
120
- language: 'markup',
121
- content: renderedEmailMetadata.markup,
122
- },
123
- {
124
- language: 'markdown',
125
- content: renderedEmailMetadata.plainText,
126
- },
127
- ]}
128
- setActiveLang={handleLangChange}
129
- />
130
- </Tooltip.Provider>
131
- </div>
132
- )}
133
- </>
134
- ) : null}
135
- <Toaster />
136
- </div>
137
- </Shell>
138
- );
139
- };
140
-
141
- export default Preview;
@@ -1,40 +0,0 @@
1
- 'use client';
2
- import type { ErrorObject } from '../../../utils/types/error-object';
3
-
4
- export const RenderingError = (props: { error: ErrorObject }) => {
5
- return (
6
- <>
7
- <div className="absolute inset-0 z-50 bg-black/80" />
8
- <div className="md:max-w-[568px] lg:max-w-[968px] absolute left-[50%] top-[50%] min-h-[50vh] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-t-4 bg-white text-black p-6 shadow-lg duration-200 sm:rounded-lg rounded-t-sm">
9
- <div className="flex flex-col max-w-full min-w-0 space-y-1.5">
10
- <h2 className="text-lg flex items-center flex-shrink gap-4 font-semibold pb-2 leading-none tracking-tight">
11
- <svg
12
- className="h-6 w-6 text-red-600 font-extrabold"
13
- fill="none"
14
- height="24"
15
- stroke="currentColor"
16
- strokeLinecap="round"
17
- strokeLinejoin="round"
18
- strokeWidth="2"
19
- viewBox="0 0 24 24"
20
- width="24"
21
- xmlns="http://www.w3.org/2000/svg"
22
- >
23
- <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
24
- <path d="M12 9v4" />
25
- <path d="M12 17h.01" />
26
- </svg>
27
- {props.error.name}: {props.error.message}
28
- </h2>
29
- {props.error.stack ? (
30
- <div className="text-sm p-2 flex-grow scroll-px-4 overflow-x-auto bg-red-500 rounded-lg text-gray-100">
31
- <pre className="font-mono w-full min-w-0 leading-7">
32
- {props.error.stack}
33
- </pre>
34
- </div>
35
- ) : undefined}
36
- </div>
37
- </div>
38
- </>
39
- );
40
- };
@@ -1,90 +0,0 @@
1
- import * as SlotPrimitive from '@radix-ui/react-slot';
2
- import * as React from 'react';
3
- import { unreachable } from '../utils/unreachable';
4
- import { cn } from '../utils/cn';
5
-
6
- type ButtonElement = React.ElementRef<'button'>;
7
- type RootProps = React.ComponentPropsWithoutRef<'button'>;
8
-
9
- type Appearance = 'white' | 'gradient';
10
- type Size = '1' | '2' | '3' | '4';
11
-
12
- interface ButtonProps extends RootProps {
13
- asChild?: boolean;
14
- appearance?: Appearance;
15
- size?: Size;
16
- }
17
-
18
- export const Button = React.forwardRef<ButtonElement, Readonly<ButtonProps>>(
19
- (
20
- {
21
- asChild,
22
- appearance = 'white',
23
- className,
24
- children,
25
- size = '2',
26
- ...props
27
- },
28
- forwardedRef,
29
- ) => {
30
- const classNames = cn(
31
- getSize(size),
32
- getAppearance(appearance),
33
- 'inline-flex items-center justify-center border font-medium',
34
- className,
35
- );
36
-
37
- return asChild ? (
38
- <SlotPrimitive.Slot ref={forwardedRef} {...props} className={classNames}>
39
- <SlotPrimitive.Slottable>{children}</SlotPrimitive.Slottable>
40
- </SlotPrimitive.Slot>
41
- ) : (
42
- <button
43
- className={classNames}
44
- ref={forwardedRef}
45
- type="button"
46
- {...props}
47
- >
48
- {children}
49
- </button>
50
- );
51
- },
52
- );
53
-
54
- Button.displayName = 'Button';
55
-
56
- const getAppearance = (appearance: Appearance | undefined) => {
57
- switch (appearance) {
58
- case undefined:
59
- case 'white':
60
- return [
61
- 'bg-white text-black',
62
- 'hover:bg-white/90',
63
- 'focus:ring-2 focus:ring-white/20 focus:outline-none focus:bg-white/90',
64
- ];
65
- case 'gradient':
66
- return [
67
- 'bg-gradient backdrop-blur-[20px] border-[#34343A]',
68
- 'hover:bg-gradientHover',
69
- 'focus:ring-2 focus:ring-white/20 focus:outline-none focus:bg-gradientHover',
70
- ];
71
- default:
72
- unreachable(appearance);
73
- }
74
- };
75
-
76
- const getSize = (size: Size | undefined) => {
77
- switch (size) {
78
- case '1':
79
- return '';
80
- case undefined:
81
- case '2':
82
- return 'text-[14px] h-8 px-3 rounded-md gap-2';
83
- case '3':
84
- return 'text-[14px] h-10 px-4 rounded-md gap-2';
85
- case '4':
86
- return 'text-base h-11 px-4 rounded-md gap-2';
87
- default:
88
- unreachable(size);
89
- }
90
- };
@@ -1,145 +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 languageMap from '../utils/language-map';
6
- import { tabTransition } from '../utils/constants';
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 [isCopied, setIsCopied] = React.useState(false);
31
-
32
- const renderDownloadIcon = () => {
33
- const value = markups.filter((markup) => markup.language === activeLang);
34
- if (typeof value[0] === 'undefined') return;
35
- const file = new File([value[0].content], `email.${value[0].language}`);
36
- const url = URL.createObjectURL(file);
37
-
38
- return (
39
- <a
40
- className="text-slate-11 transition ease-in-out duration-200 hover:text-slate-12"
41
- download={file.name}
42
- href={url}
43
- >
44
- <IconDownload />
45
- </a>
46
- );
47
- };
48
-
49
- const renderClipboardIcon = () => {
50
- const handleClipboard = async () => {
51
- const activeContent = markups.filter(({ language }) => {
52
- return activeLang === language;
53
- });
54
- setIsCopied(true);
55
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
56
- await copyTextToClipboard(activeContent[0]!.content);
57
- setTimeout(() => {
58
- setIsCopied(false);
59
- }, 3000);
60
- };
61
-
62
- return (
63
- <IconButton onClick={() => void handleClipboard()}>
64
- {isCopied ? <IconCheck /> : <IconClipboard />}
65
- </IconButton>
66
- );
67
- };
68
-
69
- React.useEffect(() => {
70
- setIsCopied(false);
71
- }, [activeLang]);
72
-
73
- return (
74
- <div
75
- className="border-slate-6 relative w-full items-center whitespace-pre rounded-md border text-sm backdrop-blur-md"
76
- style={{
77
- lineHeight: '130%',
78
- background:
79
- 'linear-gradient(145.37deg, rgba(255, 255, 255, 0.09) -8.75%, rgba(255, 255, 255, 0.027) 83.95%)',
80
- boxShadow: 'rgb(0 0 0 / 10%) 0px 5px 30px -5px',
81
- }}
82
- >
83
- <div className="h-9 border-b border-slate-6">
84
- <div className="flex">
85
- <LayoutGroup id="code">
86
- {markups.map(({ language }) => {
87
- const isCurrentLang = activeLang === language;
88
- return (
89
- <motion.button
90
- className={`relative py-[8px] px-4 text-sm font-medium font-sans transition ease-in-out duration-200 hover:text-slate-12 ${
91
- activeLang !== language ? 'text-slate-11' : 'text-slate-12'
92
- }`}
93
- key={language}
94
- onClick={() => {
95
- setActiveLang(language);
96
- }}
97
- >
98
- {isCurrentLang ? (
99
- <motion.span
100
- animate={{ opacity: 1 }}
101
- className="absolute left-0 right-0 top-0 bottom-0 bg-slate-4"
102
- exit={{ opacity: 0 }}
103
- initial={{ opacity: 0 }}
104
- layoutId="code"
105
- transition={tabTransition}
106
- />
107
- ) : null}
108
- {languageMap[language]}
109
- </motion.button>
110
- );
111
- })}
112
- </LayoutGroup>
113
- </div>
114
- <Tooltip>
115
- <Tooltip.Trigger
116
- asChild
117
- className="absolute top-2 right-2 hidden md:block"
118
- >
119
- {renderClipboardIcon()}
120
- </Tooltip.Trigger>
121
- <Tooltip.Content>Copy to Clipboard</Tooltip.Content>
122
- </Tooltip>
123
- <Tooltip>
124
- <Tooltip.Trigger
125
- asChild
126
- className="text-gray-11 absolute top-2 right-8 hidden md:block"
127
- >
128
- {renderDownloadIcon()}
129
- </Tooltip.Trigger>
130
- <Tooltip.Content>Download</Tooltip.Content>
131
- </Tooltip>
132
- </div>
133
- {markups.map(({ language, content }) => {
134
- return (
135
- <div
136
- className={`${activeLang !== language && 'hidden'}`}
137
- key={language}
138
- >
139
- <Code language={language}>{content}</Code>
140
- </div>
141
- );
142
- })}
143
- </div>
144
- );
145
- };
@@ -1,112 +0,0 @@
1
- import type { Language } from 'prism-react-renderer';
2
- import { Highlight } from 'prism-react-renderer';
3
- import * as React from 'react';
4
- import { cn } from '../utils';
5
-
6
- interface CodeProps {
7
- children: string;
8
- className?: string;
9
- language?: Language;
10
- }
11
-
12
- const theme = {
13
- plain: {
14
- color: '#EDEDEF',
15
- fontSize: 13,
16
- fontFamily: 'MonoLisa, Menlo, monospace',
17
- },
18
- styles: [
19
- {
20
- types: ['comment'],
21
- style: {
22
- color: '#706F78',
23
- },
24
- },
25
- {
26
- types: ['atrule', 'keyword', 'attr-name', 'selector'],
27
- style: {
28
- color: '#7E7D86',
29
- },
30
- },
31
- {
32
- types: ['punctuation', 'operator'],
33
- style: {
34
- color: '#706F78',
35
- },
36
- },
37
- {
38
- types: ['class-name', 'function', 'tag', 'key-white'],
39
- style: {
40
- color: '#EDEDEF',
41
- },
42
- },
43
- ],
44
- };
45
-
46
- export const Code: React.FC<Readonly<CodeProps>> = ({
47
- children,
48
- language = 'html',
49
- }) => {
50
- const value = children.trim();
51
-
52
- return (
53
- <Highlight code={value} language={language} theme={theme}>
54
- {({ tokens, getLineProps, getTokenProps }) => (
55
- <>
56
- <div
57
- className="absolute right-0 top-0 h-px w-[200px]"
58
- style={{
59
- background:
60
- '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%)',
61
- }}
62
- />
63
- <pre className="p-4 h-[650px] overflow-auto">
64
- {tokens.map((line, i) => {
65
- const lineProps = getLineProps({
66
- line,
67
- key: i,
68
- });
69
- return (
70
- <div
71
- key={i}
72
- {...lineProps}
73
- className={cn('whitespace-pre', {
74
- "before:text-slate-11 before:mr-2 before:content-['$']":
75
- language === 'bash' && tokens.length === 1,
76
- })}
77
- >
78
- {line.map((token, key) => {
79
- const tokenProps = getTokenProps({
80
- token,
81
- key,
82
- });
83
- const isException =
84
- token.content === 'from' &&
85
- line[key + 1]?.content === ':';
86
- const newTypes = isException
87
- ? [...token.types, 'key-white']
88
- : token.types;
89
- token.types = newTypes;
90
-
91
- return (
92
- <React.Fragment key={key}>
93
- <span {...tokenProps} />
94
- </React.Fragment>
95
- );
96
- })}
97
- </div>
98
- );
99
- })}
100
- </pre>
101
- <div
102
- className="absolute left-0 bottom-0 h-px w-[200px]"
103
- style={{
104
- background:
105
- '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%)',
106
- }}
107
- />
108
- </>
109
- )}
110
- </Highlight>
111
- );
112
- };