MVP
This commit is contained in:
commit
2c2fd045b3
19 changed files with 3898 additions and 0 deletions
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": ["next/core-web-vitals", "next/typescript"]
|
||||||
|
}
|
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Motto
|
||||||
|
|
||||||
|
Static file beautifier
|
8
next.config.mjs
Normal file
8
next.config.mjs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { createVanillaExtractPlugin } from '@vanilla-extract/next-plugin'
|
||||||
|
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {}
|
||||||
|
|
||||||
|
const withVanillaExtract = createVanillaExtractPlugin()
|
||||||
|
|
||||||
|
export default withVanillaExtract(nextConfig)
|
26
package.json
Normal file
26
package.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "motto",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vanilla-extract/css": "^1.15.5",
|
||||||
|
"next": "14.2.9",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"@vanilla-extract/next-plugin": "^2.4.5",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.2.9",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
61
src/app/[file]/content.tsx
Normal file
61
src/app/[file]/content.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
import Error from './types/error';
|
||||||
|
import Image from './types/image';
|
||||||
|
import Text from './types/text';
|
||||||
|
|
||||||
|
export default function Content({ src }: { src: string}) {
|
||||||
|
const [content, set_content] = useState();
|
||||||
|
|
||||||
|
type ContentType<T> = {
|
||||||
|
matcher: RegExp,
|
||||||
|
emit: () => undefined | Processor<T>,
|
||||||
|
};
|
||||||
|
type Processor<T> = {
|
||||||
|
process: (response: Response) => Promise<T>,
|
||||||
|
postprocess: (data: T) => undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const recognized_types: ContentType<Any>[] = [{
|
||||||
|
matcher: /image\/\w+/,
|
||||||
|
emit: () => {
|
||||||
|
set_content(<Image src={src} />);
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
matcher: /text\/\w+/,
|
||||||
|
emit: () => {
|
||||||
|
return {
|
||||||
|
process: (response: Response) => {
|
||||||
|
return response.text();
|
||||||
|
},
|
||||||
|
postprocess: (data: string) => {
|
||||||
|
set_content(<Text text={data} />);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (content == undefined) {
|
||||||
|
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)) {
|
||||||
|
const emitted = type.emit();
|
||||||
|
if (emitted != undefined) {
|
||||||
|
result.then(emitted.postprocess);
|
||||||
|
return emitted.process(response);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_content(<Error content_type={content_type} />);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return content ?? <p>Loading...</p>;
|
||||||
|
}
|
BIN
src/app/[file]/download.png
Normal file
BIN
src/app/[file]/download.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
66
src/app/[file]/download.svg
Normal file
66
src/app/[file]/download.svg
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 135.46666 135.46667"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||||
|
sodipodi:docname="download.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#222222"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:deskcolor="#3d3d3d"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:zoom="1.6531383"
|
||||||
|
inkscape:cx="271.90707"
|
||||||
|
inkscape:cy="288.23964"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1403"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.830298;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4"
|
||||||
|
width="60.391628"
|
||||||
|
height="11.312016"
|
||||||
|
x="66.7099"
|
||||||
|
y="19.924133"
|
||||||
|
transform="rotate(45)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.830298;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4-3"
|
||||||
|
width="60.391628"
|
||||||
|
height="11.312016"
|
||||||
|
x="-29.155479"
|
||||||
|
y="-127.10163"
|
||||||
|
transform="rotate(135)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.977747;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect4-3-2"
|
||||||
|
width="83.745499"
|
||||||
|
height="11.312016"
|
||||||
|
x="22.514505"
|
||||||
|
y="-73.397362"
|
||||||
|
transform="rotate(90)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
67
src/app/[file]/page.css.ts
Normal file
67
src/app/[file]/page.css.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
import * as colors from '../colors.css';
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const center = style({
|
||||||
|
width: '100%',
|
||||||
|
height: 'max-content',
|
||||||
|
padding: '2em',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title_group = style({
|
||||||
|
marginBottom: '1em',
|
||||||
|
width: '100%',
|
||||||
|
height: '4em',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title_text_group = style({
|
||||||
|
flexGrow: 1,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
height: '100%',
|
||||||
|
borderBottom: `1px solid ${colors.background2}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const title = style({
|
||||||
|
margin: 0,
|
||||||
|
fontSize: '2em',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const supertitle = style({
|
||||||
|
margin: 0,
|
||||||
|
color: colors.foreground2
|
||||||
|
});
|
||||||
|
|
||||||
|
export const download_button = style({
|
||||||
|
width: 'auto',
|
||||||
|
height: '100%',
|
||||||
|
marginLeft: '2em',
|
||||||
|
border: 0,
|
||||||
|
padding: 0,
|
||||||
|
backgroundColor: colors.background2,
|
||||||
|
color: colors.foreground,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const download_button_image = style({
|
||||||
|
display: 'block',
|
||||||
|
width: 'auto',
|
||||||
|
height: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const download_text_section = style({
|
||||||
|
marginLeft: '1em',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const download_text = style({
|
||||||
|
margin: '0.5em 0',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: '1.5em',
|
||||||
|
});
|
65
src/app/[file]/page.tsx
Normal file
65
src/app/[file]/page.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import type { Metadata, ResolvingMetadata } from 'next';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
import Content from './content';
|
||||||
|
import {
|
||||||
|
root,
|
||||||
|
center,
|
||||||
|
title_group,
|
||||||
|
title_text_group,
|
||||||
|
supertitle,
|
||||||
|
title,
|
||||||
|
download_button,
|
||||||
|
download_button_image,
|
||||||
|
download_text_section,
|
||||||
|
download_text,
|
||||||
|
} from './page.css';
|
||||||
|
import download_image from './download.png';
|
||||||
|
|
||||||
|
type SearchParams = { [key: string]: string | string[] | undefined };
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
params: { file: string },
|
||||||
|
searchParams: SearchParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
function get_root(search_params: SearchParams) {
|
||||||
|
console.log("HEEEERORORORRO", search_params)
|
||||||
|
return search_params['root'] ?? 'raw.monodon.me';
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateMetadata(
|
||||||
|
{ params, searchParams }: Props,
|
||||||
|
parent: ResolvingMetadata,
|
||||||
|
): Promise<Metadata> {
|
||||||
|
return {
|
||||||
|
title: `${params.file} | ${get_root(searchParams)}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params, searchParams
|
||||||
|
}: Props) {
|
||||||
|
const root_url = `https://${get_root(searchParams)}/`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={root}>
|
||||||
|
<div className={center}>
|
||||||
|
<div className={title_group}>
|
||||||
|
<div className={title_text_group}>
|
||||||
|
<p className={supertitle}>{root_url}</p>
|
||||||
|
<p className={title}>{params.file}</p>
|
||||||
|
</div>
|
||||||
|
<button className={download_button}>
|
||||||
|
<Image
|
||||||
|
className={download_button_image}
|
||||||
|
src={download_image}
|
||||||
|
alt="Download Button"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Content src={`${root_url}${params.file}`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
3
src/app/[file]/types/error.tsx
Normal file
3
src/app/[file]/types/error.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Error({ content_type }: { content_type: string }) {
|
||||||
|
return <p>{`Unrecognised MIME type: ${content_type}`}</p>
|
||||||
|
}
|
5
src/app/[file]/types/image.tsx
Normal file
5
src/app/[file]/types/image.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default function Image({ src }: { src: string }) {
|
||||||
|
return (
|
||||||
|
<img src={src} />
|
||||||
|
);
|
||||||
|
}
|
5
src/app/[file]/types/text.tsx
Normal file
5
src/app/[file]/types/text.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default function Text({ text }: { text: string }) {
|
||||||
|
return (
|
||||||
|
<p>{text}</p>
|
||||||
|
);
|
||||||
|
}
|
28
src/app/colors.css.ts
Normal file
28
src/app/colors.css.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { createVar, globalStyle} from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const background = createVar();
|
||||||
|
export const background2 = createVar();
|
||||||
|
export const foreground = createVar();
|
||||||
|
export const foreground2 = createVar();
|
||||||
|
export const accent = createVar();
|
||||||
|
|
||||||
|
globalStyle(':root', {
|
||||||
|
vars: {
|
||||||
|
[background]: '#000000',
|
||||||
|
[background2]: '#303030',
|
||||||
|
[foreground]: '#F0F0F0',
|
||||||
|
[foreground2]: '#777777',
|
||||||
|
[accent]: '#9834D4',
|
||||||
|
},
|
||||||
|
'@media': {
|
||||||
|
'(prefers-color-scheme: light)': {
|
||||||
|
vars: {
|
||||||
|
[background]: '#FFFFFF',
|
||||||
|
[background2]: '#DADADA',
|
||||||
|
[foreground]: '#171717',
|
||||||
|
[foreground2]: '#333333',
|
||||||
|
[accent]: '#9834D4',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
12
src/app/globals.css.ts
Normal file
12
src/app/globals.css.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { globalStyle } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
import * as colors from './colors.css';
|
||||||
|
|
||||||
|
globalStyle('html, body', {
|
||||||
|
margin: '0',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
color: colors.foreground,
|
||||||
|
fontFamily: 'sans-serif',
|
||||||
|
});
|
15
src/app/layout.tsx
Normal file
15
src/app/layout.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
8
src/app/page.tsx
Normal file
8
src/app/page.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Dev - Motto</h1>
|
||||||
|
<p>Motto is a WIP</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Loading…
Reference in a new issue