ANSI escapes working in some semblence
This commit is contained in:
parent
a698136493
commit
19cf7246e8
8 changed files with 184 additions and 23 deletions
|
@ -2,15 +2,15 @@
|
|||
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import Error from './types/error';
|
||||
import NetworkError from './types/error/network';
|
||||
import ContentTypeError from './types/error/content_type';
|
||||
import Image from './types/image';
|
||||
import Terminal from './types/terminal';
|
||||
import Text from './types/text';
|
||||
|
||||
export default function Content({ src }: { src: string}) {
|
||||
const [content, set_content] = useState();
|
||||
|
||||
type ContentType<T> = {
|
||||
matcher: RegExp,
|
||||
content_type: RegExp,
|
||||
extension: RegExp,
|
||||
emit: () => undefined | Processor<T>,
|
||||
};
|
||||
type Processor<T> = {
|
||||
|
@ -18,13 +18,42 @@ export default function Content({ src }: { src: string}) {
|
|||
postprocess: (data: T) => undefined,
|
||||
};
|
||||
|
||||
function not_match(regex: RegExp | undefined, str: string) {
|
||||
return !(regex ?? /(?:)/).test(str);
|
||||
}
|
||||
|
||||
function is_type(response: Response, type: ContentType<Any>) {
|
||||
if (not_match(type.content_type, response.headers.get('Content-Type').split(';')[0])) {
|
||||
return false;
|
||||
} else if (not_match(type.path, window.location.pathname)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export default function Content({ src }: { src: string}) {
|
||||
const [content, set_content] = useState();
|
||||
|
||||
const recognized_types: ContentType<Any>[] = [{
|
||||
matcher: /image\/\w+/,
|
||||
content_type: /image\/\w+/,
|
||||
emit: () => {
|
||||
set_content(<Image src={src} />);
|
||||
},
|
||||
}, {
|
||||
matcher: /text\/\w+/,
|
||||
content_type: /application\/octet-stream/,
|
||||
path: /.*\.term/,
|
||||
emit: () => {
|
||||
return {
|
||||
process: (response: Response) => {
|
||||
return response.text();
|
||||
},
|
||||
postprocess: (data: string) => {
|
||||
set_content(<Terminal text={data} />);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, {
|
||||
content_type: /(text\/\w+)|(application\/octet-stream)/,
|
||||
emit: () => {
|
||||
return {
|
||||
process: (response: Response) => {
|
||||
|
@ -42,8 +71,8 @@ export default function Content({ src }: { src: string}) {
|
|||
const result = fetch(src)
|
||||
.then(response => {
|
||||
const content_type = response.headers.get('Content-Type').split(';')[0];
|
||||
for (let type of recognized_types) {
|
||||
if (type.matcher.test(content_type)) {
|
||||
for (const type of recognized_types) {
|
||||
if (type.content_type.test(content_type)) {
|
||||
const emitted = type.emit();
|
||||
if (emitted != undefined) {
|
||||
result.then(emitted.postprocess);
|
||||
|
@ -52,7 +81,10 @@ export default function Content({ src }: { src: string}) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
set_content(<Error content_type={content_type} />);
|
||||
set_content(<ContentTypeError content_type={content_type} />);
|
||||
})
|
||||
.catch(err => {
|
||||
set_content(<NetworkError err={err} />);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export default function Error({ content_type }: { content_type: string }) {
|
||||
return <p>{`Unrecognised MIME type: ${content_type}`}</p>
|
||||
}
|
3
src/app/[...file]/types/error/content_type.tsx
Normal file
3
src/app/[...file]/types/error/content_type.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function ContentTypeError({ content_type }: { content_type: string }) {
|
||||
return <p>{`Unrecognised MIME type: ${content_type}`}</p>
|
||||
}
|
3
src/app/[...file]/types/error/network.tsx
Normal file
3
src/app/[...file]/types/error/network.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function NetworkError({ err }: { err: string }) {
|
||||
return <p>{`Error: ${err}`}</p>;
|
||||
}
|
13
src/app/[...file]/types/terminal.css.ts
Normal file
13
src/app/[...file]/types/terminal.css.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
import * as colors from '../../colors.css'
|
||||
|
||||
|
||||
export const content = style({
|
||||
margin: 0,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '1.1em',
|
||||
lineHeight: '1.4em',
|
||||
whiteSpace: 'pre-wrap',
|
||||
tabSize: 4,
|
||||
});
|
66
src/app/[...file]/types/terminal.tsx
Normal file
66
src/app/[...file]/types/terminal.tsx
Normal file
|
@ -0,0 +1,66 @@
|
|||
import * as style from './terminal.css';
|
||||
|
||||
const ansi_normal_colors = [
|
||||
"#000000",
|
||||
"#bc2232",
|
||||
"#2e9662",
|
||||
"#a55d14",
|
||||
"#2f5dab",
|
||||
"#694Caa",
|
||||
"#006080",
|
||||
"#a5a2af",
|
||||
];
|
||||
|
||||
const ansi_bright_colors = [
|
||||
"#44444f",
|
||||
"#f22c40",
|
||||
"#39bb7a",
|
||||
"#d6781a",
|
||||
"#407ee7",
|
||||
"#8466ea",
|
||||
"#0082ad",
|
||||
"#FFFFFF",
|
||||
];
|
||||
|
||||
function ansi(text: string) {
|
||||
const segments = text.split(/(?=\033)/g);
|
||||
const spans = [];
|
||||
const style = {
|
||||
color: undefined,
|
||||
backgroundColor: undefined,
|
||||
fontWeight: undefined,
|
||||
}
|
||||
for (const [index, segment] of segments.entries()) {
|
||||
const ansi_segment = segment.match(/\033\[(\d+;)*(\d+)m/g)[0]
|
||||
console.log(ansi_segment);
|
||||
const escape_codes = [
|
||||
...
|
||||
ansi_segment.matchAll(/\d+/g)
|
||||
].map(element => element[0]);
|
||||
for (const code of escape_codes) {
|
||||
if (code == 0) {
|
||||
style.color = undefined;
|
||||
style.backgroundColor = undefined;
|
||||
style.fontWeight = undefined;
|
||||
} else if (code == 1) {
|
||||
style.fontWeight = 'bold';
|
||||
} else if (30 <= code && code <= 37) {
|
||||
style.color = ansi_normal_colors[code - 30];
|
||||
} else if (90 <= code && code <= 97) {
|
||||
style.color = ansi_bright_colors[code - 90];
|
||||
} else if (40 <= code && code <= 47) {
|
||||
style.backgroundColor = ansi_bright_colors[code - 40];
|
||||
} else if (100 <= code && code <= 107) {
|
||||
style.backgroundColor = ansi_bright_colors[code - 100];
|
||||
}
|
||||
}
|
||||
spans.push(<span key={index} style={{...style}}>{segment.substring(ansi_segment.length)}</span>);
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
|
||||
export default function Terminal({ text }: { text: string }) {
|
||||
return (
|
||||
<p className={style.content}>{ansi(text)}</p>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,27 @@
|
|||
import { style } from '@vanilla-extract/css'
|
||||
|
||||
export const text = style({
|
||||
import * as colors from '../../colors.css'
|
||||
|
||||
export const group = style({
|
||||
display: 'flex',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '1.1em',
|
||||
lineHeight: '1.4em',
|
||||
whiteSpace: 'pre-wrap',
|
||||
tabSize: 4,
|
||||
});
|
||||
|
||||
export const line_numbers = style({
|
||||
margin: 0,
|
||||
marginRight: '0.5em',
|
||||
borderRight: `1px solid ${colors.background2}`,
|
||||
paddingRight: '0.5em',
|
||||
color: colors.foreground2,
|
||||
});
|
||||
|
||||
export const content = style({
|
||||
});
|
||||
|
||||
export const text = style({
|
||||
margin: 0,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,33 @@
|
|||
import * as style from './text.css'
|
||||
import { useLayoutEffect, useRef } from 'react';
|
||||
|
||||
import * as style from './text.css';
|
||||
|
||||
export default function Text({ text }: { text: string }) {
|
||||
const lines = text.split('\n');
|
||||
const refs = lines.map(() => useRef(null));
|
||||
|
||||
useLayoutEffect(() => {
|
||||
for (let line of lines) {
|
||||
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<p className={style.text}>{text}</p>
|
||||
<div className={style.group}>
|
||||
<div className={style.line_numbers}>
|
||||
{lines.map((_, index) => {
|
||||
return <p key={index} className={style.text}>{index}</p>;
|
||||
})}
|
||||
</div>
|
||||
<div className={style.content}>
|
||||
{lines.map((line, index) => {
|
||||
if (line != '') {
|
||||
return <p ref={refs[index]} key={index} className={style.text}>{line}</p>;
|
||||
} else {
|
||||
return <p ref={refs[index]} key={index} className={style.text}>{'\n'}</p>;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue