Added markdown render
This commit is contained in:
parent
ce9a9c5915
commit
951760b619
8 changed files with 130 additions and 47 deletions
|
@ -13,7 +13,9 @@
|
|||
"next": "^14.2.12",
|
||||
"prism-react-renderer": "^2.4.0",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react-dom": "^18",
|
||||
"react-markdown": "^9.0.1",
|
||||
"rehype-highlight": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
|
|
|
@ -5,8 +5,10 @@ import { useState, useEffect } from 'react'
|
|||
import NetworkError from './types/error/network';
|
||||
import ContentTypeError from './types/error/content_type';
|
||||
import ImageContent from './types/image';
|
||||
import MarkdownContent from './types/markdown';
|
||||
import Terminal from './types/terminal';
|
||||
import Text from './types/text';
|
||||
import { map_to_type } from './types/text';
|
||||
|
||||
type ContentType<T> = {
|
||||
content_type: RegExp,
|
||||
|
@ -52,6 +54,19 @@ export default function Content({ src }: { src: string}) {
|
|||
}
|
||||
};
|
||||
}
|
||||
}, {
|
||||
content_type: /application\/octet-stream/,
|
||||
path: /.*\.md/i,
|
||||
emit: () => {
|
||||
return {
|
||||
process: (response: Response) => {
|
||||
return response.text();
|
||||
},
|
||||
postprocess: (data: string) => {
|
||||
set_content(<MarkdownContent text={data} />);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, {
|
||||
content_type: /(text\/\w+)|(application\/octet-stream)/,
|
||||
emit: () => {
|
||||
|
@ -60,36 +75,8 @@ export default function Content({ src }: { src: string}) {
|
|||
return response.text();
|
||||
},
|
||||
postprocess: (data: string) => {
|
||||
const languages = {
|
||||
// "markup",
|
||||
js: "jsx",
|
||||
mjs: "jsx",
|
||||
jsx: "jsx",
|
||||
// "js-extras",
|
||||
ts: "tsx",
|
||||
tsx: "tsx",
|
||||
swift: "swift",
|
||||
kt: "kotlin",
|
||||
kts: "kotlin",
|
||||
ktm: "kotlin",
|
||||
// "objectivec",
|
||||
// "reason",
|
||||
rs: "rust",
|
||||
// "graphql",
|
||||
yml: "yaml",
|
||||
yaml: "yaml",
|
||||
go: "go",
|
||||
c: "cpp",
|
||||
cpp: "cpp",
|
||||
cxx: "cpp",
|
||||
h: "cpp",
|
||||
hpp: "cpp",
|
||||
hxx: "cpp",
|
||||
py: "python",
|
||||
json: "json",
|
||||
}
|
||||
const split = window.location.pathname.split('.');
|
||||
set_content(<Text language={split.length == 0 ? 'none' : (languages[split[split.length - 1] as keyof typeof languages] ?? 'none')} text={data} />);
|
||||
set_content(<Text language={split.length == 0 ? 'none' : map_to_type(split[split.length - 1])} text={data} line_numbers={true} />);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
33
src/app/[...file]/components/types/markdown.css.ts
Normal file
33
src/app/[...file]/components/types/markdown.css.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
import * as colors from '../../../colors.css';
|
||||
|
||||
export const code_bounds = style({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: 'max-content',
|
||||
padding: '0.65em 0 0',
|
||||
borderTop: `1px solid ${colors.background2}`,
|
||||
borderBottom: `1px solid ${colors.background2}`,
|
||||
});
|
||||
|
||||
export const copy = style({
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
border: 'none',
|
||||
padding: '0',
|
||||
outline: 'inherit',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '1.1em',
|
||||
lineHeight: '2em',
|
||||
background: 'none',
|
||||
color: 'inherit',
|
||||
transition: 'color 0.15s',
|
||||
whiteSpace: 'pre-wrap',
|
||||
':hover': {
|
||||
color: colors.accent,
|
||||
},
|
||||
|
||||
});
|
23
src/app/[...file]/components/types/markdown.tsx
Normal file
23
src/app/[...file]/components/types/markdown.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import Markdown from 'react-markdown';
|
||||
|
||||
import * as style from './markdown.css';
|
||||
import Text from './text';
|
||||
import { map_to_type } from './text';
|
||||
|
||||
export default function MarkdownContent({ text }: { text: string }) {
|
||||
return (
|
||||
<Markdown
|
||||
components={{
|
||||
'code': (props) => {
|
||||
const split = props.className.split('-');
|
||||
return (
|
||||
<div className={style.code_bounds}>
|
||||
<button onClick={() => { navigator.clipboard.writeText(props.children); }} className={style.copy}>[ Copy ]</button>
|
||||
<Text language={split.length == 0 ? 'none' : map_to_type(split[split.length - 1])} text={props.children} line_numbers={false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}}
|
||||
>{text}</Markdown>
|
||||
);
|
||||
}
|
|
@ -9,6 +9,8 @@ export const group = style({
|
|||
lineHeight: '1.4em',
|
||||
whiteSpace: 'pre',
|
||||
tabSize: 4,
|
||||
overflowX: 'scroll',
|
||||
paddingBottom: '1em',
|
||||
'@media': {
|
||||
'screen and (min-width: 768px)': {
|
||||
fontSize: '1.1em',
|
||||
|
|
|
@ -3,8 +3,51 @@ import { Highlight, themes } from 'prism-react-renderer';
|
|||
|
||||
import * as style from './text.css';
|
||||
|
||||
export default function Text({ language, text }: { language: string, text: string }) {
|
||||
console.log(language);
|
||||
const type_map = {
|
||||
// 'markup',
|
||||
js: 'jsx',
|
||||
mjs: 'jsx',
|
||||
jsx: 'jsx',
|
||||
javascript: 'javascript',
|
||||
// 'js-extras',
|
||||
ts: 'tsx',
|
||||
tsx: 'tsx',
|
||||
typescript: 'typescript',
|
||||
swift: 'swift',
|
||||
kt: 'kotlin',
|
||||
kts: 'kotlin',
|
||||
ktm: 'kotlin',
|
||||
kotlin: 'kotlin',
|
||||
// 'objectivec',
|
||||
// 'reason',
|
||||
rs: 'rust',
|
||||
rust: 'rust',
|
||||
// 'graphql',
|
||||
yml: 'yaml',
|
||||
yaml: 'yaml',
|
||||
go: 'go',
|
||||
c: 'cpp',
|
||||
cpp: 'cpp',
|
||||
cxx: 'cpp',
|
||||
'c++': 'cpp',
|
||||
h: 'cpp',
|
||||
hpp: 'cpp',
|
||||
hxx: 'cpp',
|
||||
py: 'python',
|
||||
python: 'python',
|
||||
json: 'json',
|
||||
none: 'none',
|
||||
}
|
||||
|
||||
export function map_to_type(extension?: string) {
|
||||
if (extension != undefined) {
|
||||
return type_map[extension as keyof typeof languages] ?? 'none';
|
||||
} else {
|
||||
return 'none';
|
||||
}
|
||||
}
|
||||
|
||||
export default function Text({ language, text, line_numbers }: { language: string, text: string, line_numbers: boolean }) {
|
||||
const lines = text.split('\n');
|
||||
|
||||
const media_matcher = window.matchMedia('(prefers-color-scheme: light)');
|
||||
|
@ -24,7 +67,7 @@ export default function Text({ language, text }: { language: string, text: strin
|
|||
});
|
||||
|
||||
return <div className={style.group}>
|
||||
<p className={style.line_numbers}>{lines.map((_, index) => `${index}\n`)}</p>
|
||||
{line_numbers ? <p className={style.line_numbers}>{lines.map((_, index) => `${index}\n`)}</p> : <></>}
|
||||
<Highlight
|
||||
theme={themes[light_theme ? 'nightOwlLight' : 'oneDark']}
|
||||
code={text}
|
||||
|
|
|
@ -107,7 +107,3 @@ export const download_tty_group = style({
|
|||
},
|
||||
});
|
||||
|
||||
export const wrapper = style({
|
||||
overflowX: 'scroll',
|
||||
paddingBottom: '1em',
|
||||
});
|
||||
|
|
|
@ -68,11 +68,8 @@ export default async function Page({
|
|||
<div className={style.download_tty_group}>
|
||||
{download_ttys.map((text, index) => <DownloadTTY key={index} text={text} />)}
|
||||
</div>
|
||||
|
||||
<div className={style.wrapper}>
|
||||
<Content src={`${root}${path}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue