Compare commits

..

No commits in common. "bf5686f46208b8c499c1c6449a9939769b011332" and "dfcb95bd0dcfe99f2c4886ad23d407a8e255ad44" have entirely different histories.

11 changed files with 45 additions and 91 deletions

View file

@ -1,6 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
}
"extends": ["next/core-web-vitals", "next/typescript"]
}

View file

@ -1,55 +0,0 @@
FROM chimeralinux/chimera:latest AS base
RUN apk add libgcc-chimera yarn
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* ./
RUN yarn --frozen-lockfile;
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED=1
RUN yarn run build;
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN apk add shadow
RUN groupadd --system --gid 1001 nodejs
RUN useradd --system --uid 1001 nextjs
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nextjs /app/.next/static ./.next/static
USER nextjs
EXPOSE 4730
ENV PORT=4730
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View file

@ -1,9 +1,7 @@
import { createVanillaExtractPlugin } from '@vanilla-extract/next-plugin'
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
}
const nextConfig = {}
const withVanillaExtract = createVanillaExtractPlugin()

View file

@ -4,14 +4,14 @@ import { useState, useEffect } from 'react'
import NetworkError from './types/error/network';
import ContentTypeError from './types/error/content_type';
import ImageContent from './types/image';
import Image from './types/image';
import Terminal from './types/terminal';
import Text from './types/text';
type ContentType<T> = {
content_type: RegExp,
path?: RegExp,
emit: () => void | Processor<T>,
extension: RegExp,
emit: () => undefined | Processor<T>,
};
type Processor<T> = {
process: (response: Response) => Promise<T>,
@ -22,8 +22,8 @@ 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])) {
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;
@ -32,12 +32,12 @@ function is_type(response: Response, type: ContentType<any>) {
}
export default function Content({ src }: { src: string}) {
const [content, set_content] = useState<JSX.Element>();
const [content, set_content] = useState();
const recognized_types: ContentType<any>[] = [{
const recognized_types: ContentType<Any>[] = [{
content_type: /image\/\w+/,
emit: () => {
set_content(<ImageContent src={src} />);
set_content(<Image src={src} />);
},
}, {
content_type: /application\/octet-stream/,
@ -70,8 +70,9 @@ export default function Content({ src }: { src: string}) {
if (content == undefined) {
const result = fetch(src)
.then(response => {
const content_type = response.headers.get('Content-Type').split(';')[0];
for (const type of recognized_types) {
if (is_type(response, type)) {
if (type.content_type.test(content_type)) {
const emitted = type.emit();
if (emitted != undefined) {
result.then(emitted.postprocess);
@ -80,13 +81,13 @@ export default function Content({ src }: { src: string}) {
return;
}
}
set_content(<ContentTypeError content_type={response.headers.get('Content-Type')!.split(';')[0]} />);
set_content(<ContentTypeError content_type={content_type} />);
})
.catch(err => {
set_content(<NetworkError err={err} />);
});
}
});
}, []);
return content ?? <p>Loading...</p>;
}

View file

@ -1,15 +1,15 @@
'use client'
// import { useState } from 'react';
import { useState } from 'react';
import * as style from './download_tty.css';
export default function DownloadTTY({ text }: { text: string }) {
// const [copied, set_copied] = useState(false);
function make_copy_text(text: string) {
return () => {
const [copied, set_copied] = useState(false);
function make_copy_text(text) {
return (event) => {
navigator.clipboard.writeText(text);
// set_copied(true);
set_copied(true);
// setTimeout(() => { set_copied(false); }, 5000);
};
}

View file

@ -61,6 +61,7 @@ export const download_button = style({
width: 'auto',
height: '100%',
marginLeft: '2em',
border: 0,
padding: 0,
border: `1px solid ${colors.background2}`,
backgroundColor: colors.background,

View file

@ -1,4 +1,4 @@
import type { Metadata } from 'next';
import type { Metadata, ResolvingMetadata } from 'next';
import Image from 'next/image';
import DownloadTTY from './download_tty';
@ -10,7 +10,7 @@ import download_image_light from './download_light.svg';
type SearchParams = { [key: string]: string | string[] | undefined };
type Props = {
params: { file: string[] },
params: { file: string },
searchParams: SearchParams,
};
@ -24,6 +24,7 @@ function get_root(search_params: SearchParams) {
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
return {
title: `${get_path(params.file)} | ${get_root(searchParams)}`,
@ -51,7 +52,7 @@ export default async function Page({
<p className={style.title}>{path}</p>
</div>
<button className={style.download_button}>
<a href={full} download>
<a className={style.download_link} href={full} download>
<Image
className={style.download_button_image_dark}
src={download_image_dark}

View file

@ -1,5 +1,8 @@
import { style } from '@vanilla-extract/css'
import * as colors from '../../colors.css'
export const content = style({
margin: 0,
fontFamily: 'monospace',

View file

@ -23,19 +23,19 @@ const ansi_bright_colors = [
];
function ansi(text: string) {
const segments = text.split(/(?=\x1B)/g);
const segments = text.split(/(?=\033)/g);
const spans = [];
const style: {
color?: string,
backgroundColor?: string,
fontWeight?: string,
} = {};
const style = {
color: undefined,
backgroundColor: undefined,
fontWeight: undefined,
}
for (const [index, segment] of segments.entries()) {
const ansi_segment = segment.substring(0, segment.indexOf('m') + 1);
const escape_codes = [
...
ansi_segment.matchAll(/\d+/g)
].map(element => Number(element[0]));
].map(element => element[0]);
if (escape_codes.length == 0) {
style.color = undefined;
style.backgroundColor = undefined;

View file

@ -1,7 +1,16 @@
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 (
<div className={style.group}>
@ -13,9 +22,9 @@ export default function Text({ text }: { text: string }) {
<div className={style.content}>
{lines.map((line, index) => {
if (line != '') {
return <p key={index} className={style.text}>{line}</p>;
return <p ref={refs[index]} key={index} className={style.text}>{line}</p>;
} else {
return <p key={index} className={style.text}>{'\n'}</p>;
return <p ref={refs[index]} key={index} className={style.text}>{'\n'}</p>;
}
})}
</div>

View file

@ -1,7 +1,6 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"target": "es6",
"allowJs": true,
"skipLibCheck": true,
"strict": true,