Added markdown render

This commit is contained in:
Gnarwhal 2024-09-19 19:21:58 +01:00
parent ce9a9c5915
commit 951760b619
Signed by: Gnarwhal
GPG key ID: 80DB5B37E4C96776
8 changed files with 130 additions and 47 deletions

View file

@ -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",

View file

@ -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} />);
} }
}; };
} }

View 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,
},
});

View 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>
);
}

View file

@ -9,6 +9,8 @@ export const group = style({
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',

View file

@ -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}

View file

@ -107,7 +107,3 @@ export const download_tty_group = style({
}, },
}); });
export const wrapper = style({
overflowX: 'scroll',
paddingBottom: '1em',
});

View file

@ -68,11 +68,8 @@ 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>
<div className={style.wrapper}>
<Content src={`${root}${path}`} /> <Content src={`${root}${path}`} />
</div> </div>
</div> </div>
</div>
); );
} }