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",
|
"next": "^14.2.12",
|
||||||
"prism-react-renderer": "^2.4.0",
|
"prism-react-renderer": "^2.4.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18"
|
"react-dom": "^18",
|
||||||
|
"react-markdown": "^9.0.1",
|
||||||
|
"rehype-highlight": "^7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|
|
@ -5,8 +5,10 @@ import { useState, useEffect } from 'react'
|
||||||
import NetworkError from './types/error/network';
|
import NetworkError from './types/error/network';
|
||||||
import ContentTypeError from './types/error/content_type';
|
import ContentTypeError from './types/error/content_type';
|
||||||
import ImageContent from './types/image';
|
import ImageContent from './types/image';
|
||||||
|
import MarkdownContent from './types/markdown';
|
||||||
import Terminal from './types/terminal';
|
import Terminal from './types/terminal';
|
||||||
import Text from './types/text';
|
import Text from './types/text';
|
||||||
|
import { map_to_type } from './types/text';
|
||||||
|
|
||||||
type ContentType<T> = {
|
type ContentType<T> = {
|
||||||
content_type: RegExp,
|
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)/,
|
content_type: /(text\/\w+)|(application\/octet-stream)/,
|
||||||
emit: () => {
|
emit: () => {
|
||||||
|
@ -60,36 +75,8 @@ export default function Content({ src }: { src: string}) {
|
||||||
return response.text();
|
return response.text();
|
||||||
},
|
},
|
||||||
postprocess: (data: string) => {
|
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('.');
|
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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,12 +3,14 @@ import { style } from '@vanilla-extract/css'
|
||||||
import * as colors from '../../../colors.css'
|
import * as colors from '../../../colors.css'
|
||||||
|
|
||||||
export const group = style({
|
export const group = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
fontSize: '1em',
|
fontSize: '1em',
|
||||||
lineHeight: '1.4em',
|
lineHeight: '1.4em',
|
||||||
whiteSpace: 'pre',
|
whiteSpace: 'pre',
|
||||||
tabSize: 4,
|
tabSize: 4,
|
||||||
|
overflowX: 'scroll',
|
||||||
|
paddingBottom: '1em',
|
||||||
'@media': {
|
'@media': {
|
||||||
'screen and (min-width: 768px)': {
|
'screen and (min-width: 768px)': {
|
||||||
fontSize: '1.1em',
|
fontSize: '1.1em',
|
||||||
|
|
|
@ -3,8 +3,51 @@ import { Highlight, themes } from 'prism-react-renderer';
|
||||||
|
|
||||||
import * as style from './text.css';
|
import * as style from './text.css';
|
||||||
|
|
||||||
export default function Text({ language, text }: { language: string, text: string }) {
|
const type_map = {
|
||||||
console.log(language);
|
// '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 lines = text.split('\n');
|
||||||
|
|
||||||
const media_matcher = window.matchMedia('(prefers-color-scheme: light)');
|
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}>
|
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
|
<Highlight
|
||||||
theme={themes[light_theme ? 'nightOwlLight' : 'oneDark']}
|
theme={themes[light_theme ? 'nightOwlLight' : 'oneDark']}
|
||||||
code={text}
|
code={text}
|
||||||
|
|
|
@ -107,7 +107,3 @@ export const download_tty_group = style({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const wrapper = style({
|
|
||||||
overflowX: 'scroll',
|
|
||||||
paddingBottom: '1em',
|
|
||||||
});
|
|
||||||
|
|
|
@ -68,10 +68,7 @@ export default async function Page({
|
||||||
<div className={style.download_tty_group}>
|
<div className={style.download_tty_group}>
|
||||||
{download_ttys.map((text, index) => <DownloadTTY key={index} text={text} />)}
|
{download_ttys.map((text, index) => <DownloadTTY key={index} text={text} />)}
|
||||||
</div>
|
</div>
|
||||||
|
<Content src={`${root}${path}`} />
|
||||||
<div className={style.wrapper}>
|
|
||||||
<Content src={`${root}${path}`} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue