Skip to content

Commit c63c8a0

Browse files
authored
Allow markdown preview in code browser (#2417)
1 parent 571f176 commit c63c8a0

7 files changed

Lines changed: 356 additions & 231 deletions

File tree

components/Icons/index.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,3 +1740,47 @@ export function FolderOpenIcon({ width = 24, height = 24, style }: IconProps) {
17401740
</Svg>
17411741
);
17421742
}
1743+
1744+
export function MarkdownPreviewIcon({ width = 24, height = 24, style }: IconProps) {
1745+
return (
1746+
<Svg width={width} height={height} viewBox="0 0 256 256" style={style}>
1747+
<path
1748+
d="M200,224H56a8,8,0,0,1-8-8V40a8,8,0,0,1,8-8h96l56,56V216A8,8,0,0,1,200,224Z"
1749+
fill="none"
1750+
stroke="currentColor"
1751+
strokeLinecap="round"
1752+
strokeLinejoin="round"
1753+
strokeWidth="16"
1754+
/>
1755+
<polyline
1756+
points="152 32 152 88 208 88"
1757+
fill="none"
1758+
stroke="currentColor"
1759+
strokeLinecap="round"
1760+
strokeLinejoin="round"
1761+
strokeWidth="16"
1762+
/>
1763+
<circle
1764+
cx="124"
1765+
cy="148"
1766+
r="28"
1767+
fill="none"
1768+
stroke="currentColor"
1769+
strokeLinecap="round"
1770+
strokeLinejoin="round"
1771+
strokeWidth="16"
1772+
/>
1773+
<line
1774+
x1="143.8"
1775+
y1="167.8"
1776+
x2="160"
1777+
y2="184"
1778+
fill="none"
1779+
stroke="currentColor"
1780+
strokeLinecap="round"
1781+
strokeLinejoin="round"
1782+
strokeWidth="16"
1783+
/>
1784+
</Svg>
1785+
);
1786+
}

components/Package/CodeBrowser/CodeBrowserContent.tsx

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ import { type Theme } from 'react-shiki';
44
import useSWR from 'swr';
55

66
import { Label, P } from '~/common/styleguide';
7-
import { CodeIcon, ImageFileIcon, TempFileIcon } from '~/components/Icons';
7+
import {
8+
CodeIcon,
9+
FileIcon,
10+
ImageFileIcon,
11+
MarkdownPreviewIcon,
12+
TempFileIcon,
13+
} from '~/components/Icons';
814
import CopyButton from '~/components/Package/CopyButton';
15+
import MarkdownRenderer from '~/components/Package/MarkdownContentBox/MarkdownRenderer';
916
import ThreeDotsLoader from '~/components/Package/ThreeDotsLoader';
1017
import Tooltip from '~/components/Tooltip';
1118
import rndDark from '~/styles/shiki/rnd-dark.json';
@@ -27,6 +34,7 @@ type Props = {
2734
packageName: string;
2835
isBrowserMaximized: boolean;
2936
toggleMaximized: () => void;
37+
repoUrl: string;
3038
filePath: string;
3139
fileData?: UnpkgMeta['files'][number];
3240
};
@@ -35,22 +43,27 @@ export default function CodeBrowserContent({
3543
packageName,
3644
isBrowserMaximized,
3745
toggleMaximized,
46+
repoUrl,
3847
filePath,
3948
fileData,
4049
}: Props) {
4150
const [rawPreview, setRawPreview] = useState(false);
51+
const [markdownPreview, setMarkdownPreview] = useState(false);
4252
const [imageData, setImageData] = useState<
4353
SyntheticEvent<HTMLImageElement>['currentTarget'] | null
4454
>(null);
4555

4656
useEffect(() => {
4757
setImageData(null);
58+
setRawPreview(false);
59+
setMarkdownPreview(false);
4860
}, [filePath]);
4961

5062
const fileExtension = filePath.split('.').at(-1) ?? 'text';
5163
const isTooBig = Boolean(fileData?.size && fileData.size > 1024 * 1024 * 4);
5264
const isPreviewDisabled = PREVIEW_DISABLED_FILES.includes(fileExtension) || isTooBig;
5365
const isImageFile = IMAGE_FILES.includes(fileExtension);
66+
const isMarkdownFile = ['md', 'mdx'].includes(fileExtension);
5467
const allowRawPreview = isImageFile && filePath.endsWith('.svg');
5568

5669
const { data, isLoading } = useSWR<string>(
@@ -116,6 +129,20 @@ export default function CodeBrowserContent({
116129
Show image preview
117130
</Tooltip>
118131
)}
132+
{isMarkdownFile && (
133+
<Tooltip
134+
trigger={
135+
<Pressable onPress={() => setMarkdownPreview(previewMode => !previewMode)}>
136+
{markdownPreview ? (
137+
<FileIcon style={tw`size-5 text-palette-gray4 dark:text-pewter`} />
138+
) : (
139+
<MarkdownPreviewIcon style={tw`size-5 text-palette-gray4 dark:text-pewter`} />
140+
)}
141+
</Pressable>
142+
}>
143+
{markdownPreview ? 'Markdown code' : 'Markdown preview'}
144+
</Tooltip>
145+
)}
119146
<DownloadFileButton filePath={filePath} packageName={packageName} />
120147
<CopyButton
121148
data={data}
@@ -129,26 +156,39 @@ export default function CodeBrowserContent({
129156
/>
130157
</View>
131158
</CodeBrowserContentHeader>
132-
<CodeBrowserContentHighlighter
133-
code={data}
134-
lang={filePath.split('.').at(-1) ?? 'text'}
135-
theme={(tw.prefixMatch('dark') ? rndDark : rndLight) as Theme}
136-
/>
137-
<CodeBrowserContentFooter
138-
leftSlot={
139-
<Label style={tw`font-light text-secondary`}>
140-
<span style={tw`font-medium`}>{data.split('\n').length}</span>{' '}
141-
{pluralize('line', data.split('\n').length)}
142-
</Label>
143-
}
144-
rightSlot={
145-
fileData && (
159+
{markdownPreview ? (
160+
<View
161+
id="markdownContentWrapper"
162+
style={[
163+
tw`bg-default px-6 pb-6 pt-4`,
164+
{ overflowY: 'auto', maxHeight: 'calc(100% - 46px)' },
165+
]}>
166+
<MarkdownRenderer data={data} repoUrl={repoUrl} linkableHeaders={false} />
167+
</View>
168+
) : (
169+
<CodeBrowserContentHighlighter
170+
code={data}
171+
lang={filePath.split('.').at(-1) ?? 'text'}
172+
theme={(tw.prefixMatch('dark') ? rndDark : rndLight) as Theme}
173+
/>
174+
)}
175+
{!markdownPreview && (
176+
<CodeBrowserContentFooter
177+
leftSlot={
146178
<Label style={tw`font-light text-secondary`}>
147-
<span style={tw`font-medium`}>{formatBytes(fileData.size)}</span>
179+
<span style={tw`font-medium`}>{data.split('\n').length}</span>{' '}
180+
{pluralize('line', data.split('\n').length)}
148181
</Label>
149-
)
150-
}
151-
/>
182+
}
183+
rightSlot={
184+
fileData && (
185+
<Label style={tw`font-light text-secondary`}>
186+
<span style={tw`font-medium`}>{formatBytes(fileData.size)}</span>
187+
</Label>
188+
)
189+
}
190+
/>
191+
)}
152192
</>
153193
);
154194
}

components/Package/CodeBrowser/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ export default function CodeBrowser({
247247
{activeFile && activeFileData ? (
248248
<CodeBrowserContent
249249
packageName={library.npmPkg}
250+
repoUrl={library.github.urls.repo}
250251
filePath={activeFile}
251252
fileData={activeFileData}
252253
isBrowserMaximized={isBrowserMaximized}

components/Package/MarkdownContentBox/MarkdownHeading.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import tw from '~/util/tailwind';
77

88
type Props = PropsWithChildren<{
99
tagName: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
10+
linkableHeaders?: boolean;
1011
}>;
1112

12-
export default function MarkdownHeading({ children, tagName }: Props) {
13+
export default function MarkdownHeading({ children, tagName, linkableHeaders }: Props) {
1314
const Heading = tagName;
1415
const slug = typeof children === 'string' ? kebabCase(children) : undefined;
1516

16-
if (!slug) {
17+
if (!slug || !linkableHeaders) {
1718
return <Heading>{children}</Heading>;
1819
}
1920

0 commit comments

Comments
 (0)