omnibot3000 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +18 -0
- package/README.md +9 -0
- package/api/server.ts +153 -0
- package/eslint.config.js +103 -0
- package/index.html +22 -0
- package/netlify.toml +9 -0
- package/nodemon.json +4 -0
- package/omnibot3000.code-workspace +55 -0
- package/package.json +58 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/fonts/vt220.woff2 +0 -0
- package/src/App.module.css +128 -0
- package/src/App.tsx +193 -0
- package/src/Error.tsx +80 -0
- package/src/commons/OmnibotSpeak.module.css +43 -0
- package/src/commons/OmnibotSpeak.tsx +31 -0
- package/src/commons/api/api.ts +150 -0
- package/src/commons/constants.ts +34 -0
- package/src/commons/favicon.ts +69 -0
- package/src/commons/hooks/useConfig.tsx +50 -0
- package/src/commons/hooks/useKeyPress.tsx +76 -0
- package/src/commons/hooks/useStorage.tsx +38 -0
- package/src/commons/layout/Background.module.css +47 -0
- package/src/commons/layout/Background.tsx +138 -0
- package/src/commons/layout/Breadcrumb.module.css +28 -0
- package/src/commons/layout/Breadcrumb.tsx +54 -0
- package/src/commons/layout/Container.module.css +51 -0
- package/src/commons/layout/Container.tsx +60 -0
- package/src/commons/layout/Footer.module.css +36 -0
- package/src/commons/layout/Footer.tsx +74 -0
- package/src/commons/layout/Header.module.css +73 -0
- package/src/commons/layout/Header.tsx +102 -0
- package/src/commons/layout/Menu.module.css +36 -0
- package/src/commons/layout/Menu.tsx +37 -0
- package/src/commons/persona.txt +38 -0
- package/src/commons/styles/debug.css +71 -0
- package/src/commons/styles/main.css +221 -0
- package/src/commons/styles/vt220.css +69 -0
- package/src/commons/ui/Button.tsx +22 -0
- package/src/commons/ui/Caret.tsx +20 -0
- package/src/commons/ui/Line.tsx +64 -0
- package/src/commons/ui/ScrollSnap.tsx +51 -0
- package/src/commons/ui/Separator.tsx +19 -0
- package/src/commons/ui/Spacer.tsx +12 -0
- package/src/commons/utils/canvas.ts +20 -0
- package/src/commons/utils/color.ts +4 -0
- package/src/commons/utils/math.ts +43 -0
- package/src/commons/utils/strings.ts +47 -0
- package/src/commons/utils/styles.ts +11 -0
- package/src/commons/utils/system.ts +6 -0
- package/src/commons/utils/version.ts +24 -0
- package/src/features/chat/Chat.module.css +8 -0
- package/src/features/chat/Chat.tsx +188 -0
- package/src/features/chat/commons/strings.ts +6 -0
- package/src/features/chat/components/Message.module.css +28 -0
- package/src/features/chat/components/Message.tsx +45 -0
- package/src/features/chat/components/Toolbar.module.css +19 -0
- package/src/features/chat/components/Toolbar.tsx +44 -0
- package/src/features/chat/hooks/useChatCompletionStore.tsx +160 -0
- package/src/features/cli/Cli.module.css +75 -0
- package/src/features/cli/Cli.tsx +303 -0
- package/src/features/console/cmd.ts +93 -0
- package/src/features/console/config.ts +106 -0
- package/src/features/help/Help.module.css +8 -0
- package/src/features/help/Help.tsx +78 -0
- package/src/features/history/History.module.css +77 -0
- package/src/features/history/History.tsx +92 -0
- package/src/features/home/Home.module.css +26 -0
- package/src/features/home/Home.tsx +101 -0
- package/src/features/life/Life.module.css +8 -0
- package/src/features/life/Life.tsx +16 -0
- package/src/features/life/generation.ts +103 -0
- package/src/features/life/lifeforms.ts +138 -0
- package/src/features/life/types.ts +5 -0
- package/src/features/version/Version.module.css +8 -0
- package/src/features/version/Version.tsx +70 -0
- package/src/global.d.ts +10 -0
- package/src/main.tsx +32 -0
- package/src/vite-env.d.ts +16 -0
- package/tsconfig.api.json +16 -0
- package/tsconfig.json +47 -0
- package/upgrade.sh +22 -0
- package/vite.config.ts +51 -0
package/.prettierrc
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"useTabs": false,
|
|
3
|
+
"bracketSpacing": false,
|
|
4
|
+
"bracketSameLine": true,
|
|
5
|
+
"semi": true,
|
|
6
|
+
"trailingComma": "all",
|
|
7
|
+
"singleQuote": false,
|
|
8
|
+
"printWidth": 80,
|
|
9
|
+
"tabWidth": 2,
|
|
10
|
+
"proseWrap": "always",
|
|
11
|
+
"endOfLine": "lf",
|
|
12
|
+
"overrides": [
|
|
13
|
+
{
|
|
14
|
+
"files": ".prettierrc",
|
|
15
|
+
"options": {"parser": "json"}
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/README.md
ADDED
package/api/server.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {exec} from "child_process";
|
|
2
|
+
import {
|
|
3
|
+
accessSync,
|
|
4
|
+
constants as FS,
|
|
5
|
+
Dirent,
|
|
6
|
+
readdirSync,
|
|
7
|
+
statSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "fs";
|
|
10
|
+
import {createServer, IncomingMessage, ServerResponse} from "http";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
import "dotenv/config";
|
|
14
|
+
import OpenAI from "openai";
|
|
15
|
+
import type {ChatCompletionChunk} from "openai/resources/index.mjs";
|
|
16
|
+
import type {Stream} from "openai/streaming";
|
|
17
|
+
|
|
18
|
+
type Package = {
|
|
19
|
+
name: string;
|
|
20
|
+
version: [number, number, number];
|
|
21
|
+
size: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const DOMAIN = process.env.DOMAIN || "localhost";
|
|
25
|
+
const API_PATH = process.env.API_PATH || "/api";
|
|
26
|
+
const API_PORT = process.env.API_PORT || 3001;
|
|
27
|
+
const BASE_PATH = process.cwd();
|
|
28
|
+
const JSON_PATH = path.join(BASE_PATH, "dist", "packages.json");
|
|
29
|
+
|
|
30
|
+
const getFolderSize = (folder: string): number => {
|
|
31
|
+
let total = 0;
|
|
32
|
+
try {
|
|
33
|
+
accessSync(folder, FS.R_OK);
|
|
34
|
+
} catch {
|
|
35
|
+
return total;
|
|
36
|
+
}
|
|
37
|
+
const files: Dirent[] = readdirSync(folder, {withFileTypes: true});
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
const fullPath = path.join(folder, file.name);
|
|
40
|
+
if (file.isDirectory()) total += getFolderSize(fullPath);
|
|
41
|
+
else total += statSync(fullPath).size;
|
|
42
|
+
}
|
|
43
|
+
return total;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const server = createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
47
|
+
/* CORS headers */
|
|
48
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
49
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
50
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
51
|
+
|
|
52
|
+
/* preflight requests */
|
|
53
|
+
if (req.method === "OPTIONS") {
|
|
54
|
+
res.writeHead(204);
|
|
55
|
+
res.end();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const url = req.url || "";
|
|
60
|
+
|
|
61
|
+
if (url.startsWith(`${API_PATH}/completion`)) {
|
|
62
|
+
let body = "";
|
|
63
|
+
req.on("data", (chunk) => {
|
|
64
|
+
body += chunk.toString();
|
|
65
|
+
});
|
|
66
|
+
req.on("end", async () => {
|
|
67
|
+
try {
|
|
68
|
+
const {messages, stream} = JSON.parse(body);
|
|
69
|
+
|
|
70
|
+
const openai = new OpenAI({
|
|
71
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
72
|
+
organization: process.env.OPENAI_ORG_ID,
|
|
73
|
+
project: process.env.OPENAI_PROJECT_ID,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const response = await openai.chat.completions.create({
|
|
77
|
+
/* https://openai.com/api/pricing/ */
|
|
78
|
+
model: "gpt-4.1-mini",
|
|
79
|
+
messages,
|
|
80
|
+
max_completion_tokens: 1000,
|
|
81
|
+
temperature: 1.0, // lower temperature to get stricter completion (good for code)
|
|
82
|
+
//reasoning: {effort: "high"},
|
|
83
|
+
stream,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (stream) {
|
|
87
|
+
/* server-sent events headers */
|
|
88
|
+
res.writeHead(200, {
|
|
89
|
+
"Content-Type": "text/event-stream",
|
|
90
|
+
"Cache-Control": "no-cache",
|
|
91
|
+
Connection: "keep-alive",
|
|
92
|
+
});
|
|
93
|
+
/* forward chunks to browser as SSE */
|
|
94
|
+
for await (const chunk of response as unknown as Stream<ChatCompletionChunk>) {
|
|
95
|
+
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
96
|
+
}
|
|
97
|
+
/* end the SSE stream */
|
|
98
|
+
res.write("data: [DONE]\n\n");
|
|
99
|
+
res.end();
|
|
100
|
+
} else {
|
|
101
|
+
res.writeHead(200, {"Content-Type": "application/json"});
|
|
102
|
+
res.end(JSON.stringify(response));
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
const error = err instanceof Error ? err.message : "unknown error";
|
|
106
|
+
res.writeHead(500, {"Content-Type": "application/json"});
|
|
107
|
+
res.end(JSON.stringify({error}));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
} else if (url.startsWith(`${API_PATH}/packages`)) {
|
|
111
|
+
exec("npm list --json --depth=0 --silent", (err, stdout) => {
|
|
112
|
+
if (err) {
|
|
113
|
+
const error = err instanceof Error ? err.message : "unknown error";
|
|
114
|
+
res.writeHead(500, {"Content-Type": "application/json"});
|
|
115
|
+
res.end(JSON.stringify({error}));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const json = JSON.parse(stdout);
|
|
119
|
+
const data = json?.dependencies || {};
|
|
120
|
+
const list = Object.keys(data)
|
|
121
|
+
.map((key) => {
|
|
122
|
+
const dir = data[key].resolved.replace("file:", "");
|
|
123
|
+
return {
|
|
124
|
+
name: key,
|
|
125
|
+
version: data[key].version.split(".") as Package["version"],
|
|
126
|
+
size: getFolderSize(path.join(BASE_PATH, "node_modules", dir)),
|
|
127
|
+
};
|
|
128
|
+
})
|
|
129
|
+
.sort((a, b) => (a.name < b.name ? -1 : 1));
|
|
130
|
+
res.writeHead(200, {"Content-Type": "application/json"});
|
|
131
|
+
res.end(JSON.stringify(list));
|
|
132
|
+
writeFileSync(JSON_PATH, JSON.stringify(list, null, 2));
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
res.writeHead(404);
|
|
136
|
+
res.end("nothing to see here");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
server.listen(API_PORT, () => {
|
|
141
|
+
console.log(
|
|
142
|
+
"\n\x1b[1m\x1b[32m%s\x1b[0m %s \x1b[36m%s\x1b[0m",
|
|
143
|
+
" →",
|
|
144
|
+
"API running at",
|
|
145
|
+
`http://${DOMAIN}${API_PATH}/`,
|
|
146
|
+
);
|
|
147
|
+
console.log(
|
|
148
|
+
"\x1b[1m\x1b[32m%s\x1b[0m %s \x1b[36m%s\x1b[0m\n",
|
|
149
|
+
" →",
|
|
150
|
+
"path:",
|
|
151
|
+
BASE_PATH,
|
|
152
|
+
);
|
|
153
|
+
});
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
+
import reactRefresh from "eslint-plugin-react-refresh";
|
|
5
|
+
import tseslint from "typescript-eslint";
|
|
6
|
+
import importPlugin from "eslint-plugin-import";
|
|
7
|
+
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
|
8
|
+
|
|
9
|
+
export default tseslint.config(
|
|
10
|
+
{ignores: ["dist", ".vite", "node_modules"]},
|
|
11
|
+
js.configs.recommended,
|
|
12
|
+
tseslint.configs.recommended,
|
|
13
|
+
{
|
|
14
|
+
extends: [
|
|
15
|
+
importPlugin.flatConfigs.recommended,
|
|
16
|
+
importPlugin.flatConfigs.typescript,
|
|
17
|
+
],
|
|
18
|
+
files: ["**/*.{ts,tsx}"],
|
|
19
|
+
languageOptions: {
|
|
20
|
+
globals: globals.browser,
|
|
21
|
+
parserOptions: {
|
|
22
|
+
sourceType: "module",
|
|
23
|
+
ecmaVersion: "latest",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
plugins: {
|
|
27
|
+
"react-hooks": reactHooks,
|
|
28
|
+
"react-refresh": reactRefresh,
|
|
29
|
+
"simple-import-sort": simpleImportSort,
|
|
30
|
+
},
|
|
31
|
+
rules: {
|
|
32
|
+
...reactHooks.configs.recommended.rules,
|
|
33
|
+
"no-undef": "off", // typescript handles this
|
|
34
|
+
"@typescript-eslint/no-unused-vars": ["warn", {argsIgnorePattern: "^_"}],
|
|
35
|
+
"react-hooks/exhaustive-deps": "off", // to fix later
|
|
36
|
+
"react-hooks/set-state-in-effect": "off",
|
|
37
|
+
"import/first": "error",
|
|
38
|
+
"import/no-unresolved": "off",
|
|
39
|
+
"import/newline-after-import": "error",
|
|
40
|
+
"import/no-duplicates": "error",
|
|
41
|
+
"no-console": ["error", {allow: ["info", "warn", "error"]}],
|
|
42
|
+
"simple-import-sort/imports": [
|
|
43
|
+
"error",
|
|
44
|
+
{
|
|
45
|
+
groups: [
|
|
46
|
+
["^(vite.*)(/.*|$)"],
|
|
47
|
+
["^(react.*|zustand)(/.*|$)"],
|
|
48
|
+
["^(openai)(/.*|$)"],
|
|
49
|
+
["^(@mui.*)(/.*|$)"],
|
|
50
|
+
["^(@root)(/.*|$)"],
|
|
51
|
+
["^(@api|@commons|@layout|@ui|@utils)(/.*|$)"],
|
|
52
|
+
["^(@hooks)(/.*|$)"],
|
|
53
|
+
["^(@styles)(/.*|$)"],
|
|
54
|
+
["^(@home|@chat|@history|@console|@help)(/.*|$)"],
|
|
55
|
+
["^(@)(/.*|$)"],
|
|
56
|
+
["^\\u0000"],
|
|
57
|
+
// parent imports. put ".." last
|
|
58
|
+
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
|
|
59
|
+
// other relative imports. put same folder imports and "." last
|
|
60
|
+
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
|
61
|
+
// style imports
|
|
62
|
+
["^.+\\.s?css$"],
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
extends: [
|
|
70
|
+
importPlugin.flatConfigs.recommended,
|
|
71
|
+
importPlugin.flatConfigs.typescript,
|
|
72
|
+
],
|
|
73
|
+
files: ["api/**/*.ts"],
|
|
74
|
+
languageOptions: {
|
|
75
|
+
globals: globals.node,
|
|
76
|
+
parserOptions: {
|
|
77
|
+
sourceType: "module",
|
|
78
|
+
ecmaVersion: "latest",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
plugins: {
|
|
82
|
+
"simple-import-sort": simpleImportSort,
|
|
83
|
+
},
|
|
84
|
+
rules: {
|
|
85
|
+
"no-console": "off",
|
|
86
|
+
"import/no-unresolved": "off",
|
|
87
|
+
"import/first": "error",
|
|
88
|
+
"import/newline-after-import": "error",
|
|
89
|
+
"import/no-duplicates": "error",
|
|
90
|
+
"simple-import-sort/imports": [
|
|
91
|
+
"error",
|
|
92
|
+
{
|
|
93
|
+
groups: [
|
|
94
|
+
// node built-in modules
|
|
95
|
+
["^(child_process|fs|http|path)(/.*|$)"],
|
|
96
|
+
// relative imports
|
|
97
|
+
["^\\./"],
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
);
|
package/index.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<title>OMNIBOT 3000</title>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta name="robots" content="follow index archive" />
|
|
8
|
+
<meta name="author" content="REZ" />
|
|
9
|
+
<meta name="description" content="YOUR OMNISCIENT SOURCE OF TRUTH" />
|
|
10
|
+
<link rel="icon" href="data:," />
|
|
11
|
+
<link
|
|
12
|
+
rel="preload"
|
|
13
|
+
href="/fonts/vt220.woff2"
|
|
14
|
+
as="font"
|
|
15
|
+
type="font/woff2"
|
|
16
|
+
crossorigin="anonymous" />
|
|
17
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<div id="root"></div>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
package/netlify.toml
ADDED
package/nodemon.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"folders": [
|
|
3
|
+
{
|
|
4
|
+
"path": ".",
|
|
5
|
+
"name": "root",
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"path": "src",
|
|
9
|
+
"name": "source",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "api",
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
"launch": {
|
|
16
|
+
"configurations": [
|
|
17
|
+
{
|
|
18
|
+
"name": "run start dev",
|
|
19
|
+
"type": "node",
|
|
20
|
+
"request": "launch",
|
|
21
|
+
"runtimeExecutable": "pnpm",
|
|
22
|
+
"runtimeArgs": ["run", "start", "dev"],
|
|
23
|
+
"console": "integratedTerminal",
|
|
24
|
+
"cwd": "${workspaceFolder}",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "run start api",
|
|
28
|
+
"type": "node",
|
|
29
|
+
"request": "launch",
|
|
30
|
+
"runtimeExecutable": "pnpm",
|
|
31
|
+
"runtimeArgs": ["run", "start", "api"],
|
|
32
|
+
"console": "integratedTerminal",
|
|
33
|
+
"cwd": "${workspaceFolder}",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "run build",
|
|
37
|
+
"type": "node",
|
|
38
|
+
"request": "launch",
|
|
39
|
+
"runtimeExecutable": "pnpm",
|
|
40
|
+
"runtimeArgs": ["run", "build"],
|
|
41
|
+
"console": "integratedTerminal",
|
|
42
|
+
"cwd": "${workspaceFolder}",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "upgrade",
|
|
46
|
+
"type": "node",
|
|
47
|
+
"request": "launch",
|
|
48
|
+
"runtimeExecutable": "/bin/bash",
|
|
49
|
+
"args": ["-c", "./upgrade.sh"],
|
|
50
|
+
"console": "integratedTerminal",
|
|
51
|
+
"cwd": "${workspaceFolder}",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "omnibot3000",
|
|
3
|
+
"x-display-name": "OMNIBOT 3000",
|
|
4
|
+
"description": "your omniscient source of truth",
|
|
5
|
+
"private": false,
|
|
6
|
+
"version": "1.8.2",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "rez",
|
|
10
|
+
"email": "rez@lol.pm",
|
|
11
|
+
"url": "https://github.com/chiptune"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://www.omnibot3000.com",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "pnpm run-p start:dev start:api",
|
|
16
|
+
"start:dev": "pnpm run dev",
|
|
17
|
+
"start:api": "nodemon api/server.ts",
|
|
18
|
+
"start:prod": "pnpm run build && pnpm run-p prod api",
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"prod": "vite preview --port 4173",
|
|
21
|
+
"api": "node dist/api/server.js",
|
|
22
|
+
"build": "pnpm run build:client && pnpm run build:api",
|
|
23
|
+
"build:client": "tsc -b && vite build",
|
|
24
|
+
"build:api": "tsc --project tsconfig.api.json",
|
|
25
|
+
"lint": "eslint --fix .",
|
|
26
|
+
"prettify": "prettier --write ."
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"openai": "^6.10.0",
|
|
30
|
+
"react": "^19.2.3",
|
|
31
|
+
"react-dom": "^19.2.3",
|
|
32
|
+
"react-markdown": "^10.1.0",
|
|
33
|
+
"react-router-dom": "^7.10.1",
|
|
34
|
+
"zustand": "^5.0.9"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@eslint/js": "^9.39.2",
|
|
38
|
+
"@types/node": "^25.0.1",
|
|
39
|
+
"@types/react": "^19.2.7",
|
|
40
|
+
"@types/react-dom": "^19.2.3",
|
|
41
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
42
|
+
"classnames": "^2.5.1",
|
|
43
|
+
"dotenv": "^17.2.3",
|
|
44
|
+
"eslint": "^9.39.2",
|
|
45
|
+
"eslint-plugin-import": "^2.32.0",
|
|
46
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
47
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
48
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
49
|
+
"globals": "^16.5.0",
|
|
50
|
+
"nodemon": "^3.1.11",
|
|
51
|
+
"npm-run-all": "^4.1.5",
|
|
52
|
+
"prettier": "^3.7.4",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"typescript-eslint": "^8.49.0",
|
|
55
|
+
"vite": "^7.2.7",
|
|
56
|
+
"vite-tsconfig-paths": "^5.1.4"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
position: relative;
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 0;
|
|
5
|
+
border-radius: var(--margin);
|
|
6
|
+
width: 100vw;
|
|
7
|
+
height: 100vh;
|
|
8
|
+
filter: brightness(1.25) contrast(1.5) saturate(1.5) blur(0.025rem)
|
|
9
|
+
drop-shadow(0 0 0.25rem var(--color-background));
|
|
10
|
+
opacity: var(--opacity-primary);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.screen {
|
|
14
|
+
position: relative;
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: var(--margin);
|
|
17
|
+
border-radius: var(--margin);
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
box-shadow: inset 0 0 0.2rem #000;
|
|
21
|
+
background:
|
|
22
|
+
linear-gradient(
|
|
23
|
+
0deg,
|
|
24
|
+
var(--color-background) 0%,
|
|
25
|
+
var(--color-background) 100%
|
|
26
|
+
),
|
|
27
|
+
linear-gradient(
|
|
28
|
+
30deg,
|
|
29
|
+
hsla(calc(var(--h) - 90) 30% 30% / var(--opacity-background)) 0%,
|
|
30
|
+
hsla(calc(var(--h) - 45) 50% 20% / var(--opacity-background)) 25%,
|
|
31
|
+
hsla(calc(var(--h) + 0) 70% 10% / var(--opacity-background)) 50%,
|
|
32
|
+
hsla(calc(var(--h) + 45) 50% 20% / var(--opacity-background)) 75%,
|
|
33
|
+
hsla(calc(var(--h) + 90) 30% 30% / var(--opacity-background)) 100%
|
|
34
|
+
),
|
|
35
|
+
radial-gradient(
|
|
36
|
+
circle,
|
|
37
|
+
hsla(calc(var(--h) - 270) 30% 30% / var(--opacity-background)) 0%,
|
|
38
|
+
hsla(calc(var(--h) - 180) 30% 30% / var(--opacity-background)) 100%
|
|
39
|
+
),
|
|
40
|
+
radial-gradient(
|
|
41
|
+
circle,
|
|
42
|
+
hsla(0 100% 100% / 0.0125) 0%,
|
|
43
|
+
hsla(0 0% 0% / 0.25) 80%,
|
|
44
|
+
hsla(0 0% 0% / 0.5) 100%
|
|
45
|
+
);
|
|
46
|
+
animation: fade-tty var(--duration-fade) linear;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.tty {
|
|
50
|
+
position: absolute;
|
|
51
|
+
top: 0;
|
|
52
|
+
left: 0;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: row;
|
|
55
|
+
place-items: start;
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: var(--margin);
|
|
58
|
+
z-index: var(--z-index-screen);
|
|
59
|
+
animation: fade-tty var(--duration-fade) linear;
|
|
60
|
+
background-image: repeating-linear-gradient(
|
|
61
|
+
0deg,
|
|
62
|
+
#0000 0rem,
|
|
63
|
+
hsla(0 0% 0% / 0.3) 0.02rem,
|
|
64
|
+
hsla(0 0% 0% / 0.3) 0.12rem,
|
|
65
|
+
#0000 0.14rem,
|
|
66
|
+
#0000 0.2rem
|
|
67
|
+
);
|
|
68
|
+
background-size: 100%, 100%;
|
|
69
|
+
background-repeat: no-repeat, repeat;
|
|
70
|
+
background-position: 0rem -0.0333rem;
|
|
71
|
+
background-blend-mode: overlay;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.content {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
flex-grow: 1;
|
|
78
|
+
align-items: center;
|
|
79
|
+
align-self: stretch;
|
|
80
|
+
height: 100%;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.body {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
flex-grow: 1;
|
|
87
|
+
align-items: center;
|
|
88
|
+
align-self: stretch;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.v-line {
|
|
93
|
+
flex-grow: 0;
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
align-self: start;
|
|
96
|
+
width: var(--font-width);
|
|
97
|
+
height: 100%;
|
|
98
|
+
opacity: var(--opacity-secondary);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.h-line {
|
|
102
|
+
flex-grow: 0;
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
align-self: start;
|
|
105
|
+
width: 100%;
|
|
106
|
+
height: var(--line-height);
|
|
107
|
+
opacity: var(--opacity-secondary);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.error {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
text-wrap: wrap;
|
|
114
|
+
width: 100%;
|
|
115
|
+
height: 100%;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@keyframes fade-tty {
|
|
119
|
+
0% {
|
|
120
|
+
opacity: 0;
|
|
121
|
+
}
|
|
122
|
+
20% {
|
|
123
|
+
opacity: 0;
|
|
124
|
+
}
|
|
125
|
+
100% {
|
|
126
|
+
opacity: 1;
|
|
127
|
+
}
|
|
128
|
+
}
|