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/src/App.tsx
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type {ProfilerOnRenderCallback} from "react";
|
|
2
|
+
import {Profiler, useEffect, useLayoutEffect, useRef, useState} from "react";
|
|
3
|
+
import {
|
|
4
|
+
BrowserRouter as Router,
|
|
5
|
+
Navigate,
|
|
6
|
+
Outlet,
|
|
7
|
+
Route,
|
|
8
|
+
Routes,
|
|
9
|
+
} from "react-router-dom";
|
|
10
|
+
|
|
11
|
+
import Background from "@layout/Background";
|
|
12
|
+
import Footer from "@layout/Footer";
|
|
13
|
+
import Header from "@layout/Header";
|
|
14
|
+
import Menu from "@layout/Menu";
|
|
15
|
+
import Line from "@ui/Line";
|
|
16
|
+
import {format} from "@utils/math";
|
|
17
|
+
import {getCharWidth, getLineHeight} from "@utils/strings";
|
|
18
|
+
import {isSystemDarkModeOn} from "@utils/system";
|
|
19
|
+
|
|
20
|
+
import useConfig from "@hooks/useConfig";
|
|
21
|
+
import useStorage from "@hooks/useStorage";
|
|
22
|
+
|
|
23
|
+
import Chat from "@chat/Chat";
|
|
24
|
+
import Help from "@help/Help";
|
|
25
|
+
import Home from "@home/Home";
|
|
26
|
+
|
|
27
|
+
import styles from "@/App.module.css";
|
|
28
|
+
|
|
29
|
+
import "@styles/debug.css";
|
|
30
|
+
import "@styles/main.css";
|
|
31
|
+
import "@styles/vt220.css";
|
|
32
|
+
|
|
33
|
+
import Life from "@life/Life";
|
|
34
|
+
import Version from "@version/Version";
|
|
35
|
+
import cls from "classnames";
|
|
36
|
+
|
|
37
|
+
export interface RenderTime {
|
|
38
|
+
phase: string;
|
|
39
|
+
duration: number;
|
|
40
|
+
base: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Layout = () => {
|
|
44
|
+
const config = useConfig();
|
|
45
|
+
const storage = useStorage();
|
|
46
|
+
|
|
47
|
+
const {debug} = config.getConfig();
|
|
48
|
+
|
|
49
|
+
const beforeUnloadHandler = () => {
|
|
50
|
+
storage.save();
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
storage.load();
|
|
55
|
+
|
|
56
|
+
window.addEventListener("beforeunload", beforeUnloadHandler);
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
window.removeEventListener("beforeunload", beforeUnloadHandler);
|
|
60
|
+
};
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
const [darkMode, toggleDarkMode] = useState(isSystemDarkModeOn());
|
|
64
|
+
|
|
65
|
+
const themeSwitchHandler = () => {
|
|
66
|
+
toggleDarkMode(!darkMode);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const rootRef = useRef<HTMLDivElement | null>(null);
|
|
70
|
+
const bodyRef = useRef<HTMLDivElement | null>(null);
|
|
71
|
+
|
|
72
|
+
const [cw, setCharWidth] = useState<number>(getCharWidth());
|
|
73
|
+
const [lh, setLineHeight] = useState<number>(getLineHeight());
|
|
74
|
+
const [w, setWidth] = useState<number>(window.innerWidth);
|
|
75
|
+
const [h, setHeight] = useState<number>(window.innerHeight);
|
|
76
|
+
|
|
77
|
+
const update = () => {
|
|
78
|
+
const root = rootRef.current;
|
|
79
|
+
if (!root) return;
|
|
80
|
+
|
|
81
|
+
setCharWidth(getCharWidth());
|
|
82
|
+
setLineHeight(getLineHeight());
|
|
83
|
+
|
|
84
|
+
const vw = window.innerWidth;
|
|
85
|
+
const vh = window.innerHeight;
|
|
86
|
+
|
|
87
|
+
setWidth(Math.floor((vw - cw * 2) / cw) * cw);
|
|
88
|
+
setHeight(Math.floor((vh - cw * 4) / lh) * lh + cw * 2);
|
|
89
|
+
|
|
90
|
+
let el = document.getElementById("debug-screen-size");
|
|
91
|
+
if (!el) {
|
|
92
|
+
el = document.createElement("div");
|
|
93
|
+
el.id = "debug-screen-size";
|
|
94
|
+
el.className = "debug-info";
|
|
95
|
+
document.body.appendChild(el);
|
|
96
|
+
}
|
|
97
|
+
el.innerHTML = `viewport: ${vw}x${vh} |\
|
|
98
|
+
char: ${format(cw)}x${format(lh)} |\
|
|
99
|
+
w: ${w} | h: ${h}`;
|
|
100
|
+
el.style.display = debug ? "block" : "none";
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
useLayoutEffect(() => {
|
|
104
|
+
const resizeObserver = new ResizeObserver(update);
|
|
105
|
+
if (rootRef.current) resizeObserver.observe(rootRef.current);
|
|
106
|
+
return () => {
|
|
107
|
+
resizeObserver.disconnect();
|
|
108
|
+
};
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
const renderTime = useRef<RenderTime>({
|
|
112
|
+
phase: "none",
|
|
113
|
+
duration: 0,
|
|
114
|
+
base: 0,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const profilerCallback: ProfilerOnRenderCallback = (
|
|
118
|
+
_, //id,
|
|
119
|
+
phase,
|
|
120
|
+
actualDuration,
|
|
121
|
+
baseDuration,
|
|
122
|
+
) => {
|
|
123
|
+
renderTime.current = {phase, duration: actualDuration, base: baseDuration};
|
|
124
|
+
const favicon = document.getElementById("favicon");
|
|
125
|
+
if (favicon) favicon.style.display = debug ? "block" : "none";
|
|
126
|
+
const screenSize = document.getElementById("debug-screen-size");
|
|
127
|
+
if (screenSize) screenSize.style.display = debug ? "block" : "none";
|
|
128
|
+
/*if (debug)
|
|
129
|
+
console.info(
|
|
130
|
+
`%c${id} ${phase}: ${Math.round(actualDuration)} ms / ${Math.round(baseDuration)} ms`,
|
|
131
|
+
"color:#999",
|
|
132
|
+
);*/
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<Profiler id="app" onRender={profilerCallback}>
|
|
137
|
+
<div
|
|
138
|
+
ref={rootRef}
|
|
139
|
+
className={cls(styles.root, !darkMode || "dark", !debug || "debug")}
|
|
140
|
+
style={{
|
|
141
|
+
marginTop: `${(window.innerHeight - h) / 2}px`,
|
|
142
|
+
marginLeft: `${(window.innerWidth - w) / 2}px`,
|
|
143
|
+
}}>
|
|
144
|
+
<div
|
|
145
|
+
className={cls("ascii", styles.screen)}
|
|
146
|
+
style={{
|
|
147
|
+
width: `${w - cw * 2}px`,
|
|
148
|
+
height: `${h - cw * 2}px`,
|
|
149
|
+
}}>
|
|
150
|
+
<Background
|
|
151
|
+
w={Math.floor((w - cw * 2) / cw)}
|
|
152
|
+
h={Math.floor((h - cw * 2) / lh)}
|
|
153
|
+
/>
|
|
154
|
+
<div
|
|
155
|
+
className={styles.tty}
|
|
156
|
+
style={{
|
|
157
|
+
width: `${w - cw * 2}px`,
|
|
158
|
+
height: `${h - cw * 2}px`,
|
|
159
|
+
}}>
|
|
160
|
+
<Menu />
|
|
161
|
+
<Line variant="vertical" className={styles["v-line"]} />
|
|
162
|
+
<div className={styles.content}>
|
|
163
|
+
<Header darkMode={darkMode} onThemeToggle={themeSwitchHandler} />
|
|
164
|
+
<Line variant="horizontal" className={styles["h-line"]} />
|
|
165
|
+
<div ref={bodyRef} className={styles.body}>
|
|
166
|
+
<Outlet />
|
|
167
|
+
</div>
|
|
168
|
+
<Line variant="horizontal" className={styles["h-line"]} />
|
|
169
|
+
<Footer renderTime={renderTime} />
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</Profiler>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const App = () => (
|
|
179
|
+
<Router>
|
|
180
|
+
<Routes>
|
|
181
|
+
<Route path="/" element={<Layout />}>
|
|
182
|
+
<Route path="/" element={<Navigate to="/home" replace />} />
|
|
183
|
+
<Route path="/home" element={<Home />} />
|
|
184
|
+
<Route path="/chat/:id?" element={<Chat />} />
|
|
185
|
+
<Route path="/help" element={<Help />} />
|
|
186
|
+
<Route path="/life" element={<Life />} />
|
|
187
|
+
<Route path="/version" element={<Version />} />
|
|
188
|
+
</Route>
|
|
189
|
+
</Routes>
|
|
190
|
+
</Router>
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
export default App;
|
package/src/Error.tsx
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, {ReactNode} from "react";
|
|
2
|
+
|
|
3
|
+
import {NAME} from "@commons/constants";
|
|
4
|
+
import Line from "@ui/Line";
|
|
5
|
+
|
|
6
|
+
import styles from "@/App.module.css";
|
|
7
|
+
|
|
8
|
+
import cls from "classnames";
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type State = {
|
|
15
|
+
hasError: boolean;
|
|
16
|
+
error?: Error;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class ErrorBoundary extends React.Component<Props, State> {
|
|
20
|
+
constructor(props: Props) {
|
|
21
|
+
super(props);
|
|
22
|
+
this.state = {hasError: false};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static getDerivedStateFromError(error: Error): State {
|
|
26
|
+
return {hasError: true, error};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
30
|
+
console.error(`YOU DECEIVED ${NAME}`, error, errorInfo);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render() {
|
|
34
|
+
if (this.state.hasError) {
|
|
35
|
+
return (
|
|
36
|
+
<div className={styles.root}>
|
|
37
|
+
<div
|
|
38
|
+
className={styles.screen}
|
|
39
|
+
style={{
|
|
40
|
+
margin: "var(--margin)",
|
|
41
|
+
padding: 0,
|
|
42
|
+
width: "calc(100% - var(--margin) * 2)",
|
|
43
|
+
height: "calc(100% - var(--margin) * 2)",
|
|
44
|
+
}}>
|
|
45
|
+
<div
|
|
46
|
+
className={styles.tty}
|
|
47
|
+
style={{
|
|
48
|
+
padding: "var(--margin)",
|
|
49
|
+
width: "calc(100% - var(--margin) * 2)",
|
|
50
|
+
height: "calc(100% - var(--margin) * 2)",
|
|
51
|
+
overflow: "hidden auto",
|
|
52
|
+
}}>
|
|
53
|
+
<div className={cls("text", "ascii", styles.error)}>
|
|
54
|
+
<div>
|
|
55
|
+
<span style={{opacity: "var(--opacity-tertiary)"}}>% </span>
|
|
56
|
+
<span>error :(</span>
|
|
57
|
+
</div>
|
|
58
|
+
<Line className={styles["h-line"]} />
|
|
59
|
+
<div>{this.state.error?.message}</div>
|
|
60
|
+
<Line className={styles["h-line"]} />
|
|
61
|
+
<div
|
|
62
|
+
style={{
|
|
63
|
+
opacity: "var(--opacity-tertiary)",
|
|
64
|
+
textTransform: "none",
|
|
65
|
+
}}>
|
|
66
|
+
{this.state.error?.stack}
|
|
67
|
+
</div>
|
|
68
|
+
<Line className={styles["h-line"]} />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return this.props.children;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default ErrorBoundary;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
word-break: break-word;
|
|
5
|
+
opacity: var(--opacity-secondary);
|
|
6
|
+
p {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
padding-bottom: var(--line-height);
|
|
9
|
+
em {
|
|
10
|
+
opacity: var(--opacity-primary);
|
|
11
|
+
color: var(--color-highlight);
|
|
12
|
+
}
|
|
13
|
+
a {
|
|
14
|
+
opacity: var(--opacity-primary);
|
|
15
|
+
color: var(--color-highlight);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
ul {
|
|
19
|
+
list-style-position: outside;
|
|
20
|
+
margin-left: calc(var(--tab-size) * var(--font-width));
|
|
21
|
+
}
|
|
22
|
+
ul > :last-child {
|
|
23
|
+
padding-bottom: 0 !important;
|
|
24
|
+
p {
|
|
25
|
+
padding-bottom: 0 !important;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
ol {
|
|
29
|
+
list-style-position: outside;
|
|
30
|
+
padding-left: calc(var(--tab-size) * var(--font-width));
|
|
31
|
+
margin-left: calc(var(--tab-size) * var(--font-width));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.hr {
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 0;
|
|
38
|
+
opacity: var(--opacity-tertiary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.has-caret > * > :last-child {
|
|
42
|
+
padding-bottom: 0 !important;
|
|
43
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {memo} from "react";
|
|
2
|
+
import Markdown from "react-markdown";
|
|
3
|
+
|
|
4
|
+
import styles from "@commons/OmnibotSpeak.module.css";
|
|
5
|
+
import Caret from "@ui/Caret";
|
|
6
|
+
import Line from "@ui/Line";
|
|
7
|
+
import {formatText, sanitizeHTML} from "@utils/strings";
|
|
8
|
+
|
|
9
|
+
import cls from "classnames";
|
|
10
|
+
|
|
11
|
+
export const OmnibotSpeak = (props: {truth: string; hasCaret?: boolean}) => (
|
|
12
|
+
<div className={styles["has-caret"]}>
|
|
13
|
+
<div className={cls("text", styles["root"])}>
|
|
14
|
+
<Markdown
|
|
15
|
+
components={{
|
|
16
|
+
code(props) {
|
|
17
|
+
const {children, className} = props;
|
|
18
|
+
return <code className={className}>{children}</code>;
|
|
19
|
+
},
|
|
20
|
+
hr() {
|
|
21
|
+
return <Line char="*" className={styles["hr"]} />;
|
|
22
|
+
},
|
|
23
|
+
}}>
|
|
24
|
+
{formatText(sanitizeHTML(props.truth))}
|
|
25
|
+
</Markdown>
|
|
26
|
+
</div>
|
|
27
|
+
{props.hasCaret && <Caret />}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default memo(OmnibotSpeak);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatCompletion,
|
|
3
|
+
ChatCompletionChunk,
|
|
4
|
+
ChatCompletionMessageParam,
|
|
5
|
+
} from "openai/resources/index.mjs";
|
|
6
|
+
import {Stream} from "openai/streaming.mjs";
|
|
7
|
+
|
|
8
|
+
import {NAME, VERSION} from "@commons/constants";
|
|
9
|
+
import persona from "@commons/persona.txt?raw";
|
|
10
|
+
import {getVariableFromCSS} from "@utils/styles";
|
|
11
|
+
|
|
12
|
+
export const getData = async (
|
|
13
|
+
messages: ChatCompletionMessageParam[],
|
|
14
|
+
stream: boolean = true,
|
|
15
|
+
): Promise<ChatCompletion | Stream<ChatCompletionChunk>> => {
|
|
16
|
+
const response = await fetch("/api/completion", {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: {
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
messages,
|
|
23
|
+
stream,
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`API request failed: ${response.statusText}`);
|
|
28
|
+
}
|
|
29
|
+
if (stream) {
|
|
30
|
+
if (!response.body) {
|
|
31
|
+
throw new Error("Response body is not readable");
|
|
32
|
+
}
|
|
33
|
+
return Stream.fromSSEResponse(response, new AbortController());
|
|
34
|
+
} else {
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
return data as ChatCompletion;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const getSystemConfig = (): ChatCompletionMessageParam => {
|
|
41
|
+
const size = getVariableFromCSS("base-size");
|
|
42
|
+
const height = getVariableFromCSS("base-height");
|
|
43
|
+
const systemConfig = `\
|
|
44
|
+
current date: ${new Date().toLocaleDateString()}. \
|
|
45
|
+
current time: ${new Date().toLocaleDateString()}. \
|
|
46
|
+
current unix EPOCH time: ${Math.floor(Date.now() / 1000)}. \
|
|
47
|
+
a list of random number: ${Array.from({length: 32}, () =>
|
|
48
|
+
Math.round(Math.random() * 100),
|
|
49
|
+
).join(", ")}. \
|
|
50
|
+
current user agent: ${navigator.userAgent}. \
|
|
51
|
+
current color hue: ${getVariableFromCSS("h")}°. \
|
|
52
|
+
current color saturation: ${getVariableFromCSS("s")}%. \
|
|
53
|
+
current color lightness: ${getVariableFromCSS("l")}%. \
|
|
54
|
+
current font base size: ${getVariableFromCSS("BASE-SIZE")}. \
|
|
55
|
+
user can change the color with the "/color [h|s|l] number" command. \
|
|
56
|
+
user can change the font size with the "/size number" command. \
|
|
57
|
+
the "/size" command without parameter will reset the value to ${size}. \
|
|
58
|
+
user can change the line height with the "/height number" command. \
|
|
59
|
+
the "/height" command without parameter will reset the value to ${height}. \
|
|
60
|
+
user can reset the settings with the "/reset" command. \
|
|
61
|
+
${formatting}. \
|
|
62
|
+
your name is ${NAME} and your version is ${VERSION.join(".")}. \
|
|
63
|
+
${persona}`;
|
|
64
|
+
return {role: "developer", content: systemConfig};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const formatting = `\
|
|
68
|
+
generate markdown text only, no HTML please! never. \
|
|
69
|
+
use only the 256 first ASCII character in your answers, no unicode. \
|
|
70
|
+
do not use any special characters or emojis or unicode > 0x00ff. \
|
|
71
|
+
make all links you provide clickable, give them a human readable name. \
|
|
72
|
+
very important: output only markdown text, no HTML please. \
|
|
73
|
+
answer with the language used the most by the user in the chat.`;
|
|
74
|
+
|
|
75
|
+
export const smallQueryFormatting = (max: number): string => `\
|
|
76
|
+
no more than ${max} characters (including spaces)! NO MORE. \
|
|
77
|
+
do not add any comments or punctuations. \
|
|
78
|
+
there is no need to capitalize the first letter of every words. \
|
|
79
|
+
do not add any bullet point or numbered list, just plain text. \
|
|
80
|
+
it's not an answer to a query, make it compact and catchy!`;
|
|
81
|
+
|
|
82
|
+
export const getChatTitle = async (
|
|
83
|
+
messages: ChatCompletionMessageParam[],
|
|
84
|
+
): Promise<string> => {
|
|
85
|
+
const updatedMessages: ChatCompletionMessageParam[] = [
|
|
86
|
+
getSystemConfig(),
|
|
87
|
+
...messages,
|
|
88
|
+
{
|
|
89
|
+
role: "developer",
|
|
90
|
+
content: `\
|
|
91
|
+
make a title for this chat, excluding this request. \
|
|
92
|
+
keep it as simple, short and descriptive as possible. \
|
|
93
|
+
do not mention your name in the result. \`
|
|
94
|
+
${smallQueryFormatting(28)}`,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
const response = (await getData(updatedMessages, false)) as ChatCompletion;
|
|
98
|
+
return response.choices[0].message.content || "?";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const getSubtitle = async (): Promise<string> => {
|
|
102
|
+
const messages: ChatCompletionMessageParam[] = [
|
|
103
|
+
getSystemConfig(),
|
|
104
|
+
{
|
|
105
|
+
role: "developer",
|
|
106
|
+
content: `\
|
|
107
|
+
make a list of 5 catch phrase to present you to the user. \
|
|
108
|
+
do not mention your name in the result, it's a motto. \
|
|
109
|
+
emphasize on your infinite source of knowledge. \
|
|
110
|
+
boast yourself to the maximum, demonstrate that your are the best. \
|
|
111
|
+
${smallQueryFormatting(32)}`,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
const response = (await getData(messages, false)) as ChatCompletion;
|
|
115
|
+
return response.choices[0].message.content || "?";
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const getPromptPlaceholder = async (): Promise<string> => {
|
|
119
|
+
const messages: ChatCompletionMessageParam[] = [
|
|
120
|
+
getSystemConfig(),
|
|
121
|
+
{
|
|
122
|
+
role: "developer",
|
|
123
|
+
content: `\
|
|
124
|
+
make a list of 10 imperatives input placeholder. \
|
|
125
|
+
this input is where the user is asking you question. \
|
|
126
|
+
you are not inviting, you are imposing, user must comply. \
|
|
127
|
+
${smallQueryFormatting(25)}`,
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
const response = (await getData(messages, false)) as ChatCompletion;
|
|
131
|
+
return response.choices[0].message.content || "?";
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const getStartButton = async (): Promise<string> => {
|
|
135
|
+
const messages: ChatCompletionMessageParam[] = [
|
|
136
|
+
getSystemConfig(),
|
|
137
|
+
{
|
|
138
|
+
role: "developer",
|
|
139
|
+
content: `\
|
|
140
|
+
make a name for a button that start a chat in few words. \
|
|
141
|
+
this button bring users to the page where they can make a query. \
|
|
142
|
+
you are not inviting, you are imposing, user must comply. \
|
|
143
|
+
${smallQueryFormatting(25)}`,
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
const response = (await getData(messages, false)) as ChatCompletion;
|
|
147
|
+
return response.choices[0].message.content || "?";
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default getData;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import pkg from "@root/package.json";
|
|
2
|
+
|
|
3
|
+
export const NAME = String(pkg["x-display-name"] || pkg.name);
|
|
4
|
+
export const VERSION = String(pkg.version)
|
|
5
|
+
.split(".")
|
|
6
|
+
.map((v) => Number(v));
|
|
7
|
+
export const AUTHOR = pkg.author;
|
|
8
|
+
export const SESSION_KEY = String(pkg.name).toLowerCase().replace(/\s/g, "_");
|
|
9
|
+
|
|
10
|
+
export const COMPLETION_ID_WILDCARD = "chatcmpl-";
|
|
11
|
+
|
|
12
|
+
export const ASCII_SPACE = "\u0020";
|
|
13
|
+
export const ASCII_LOSANGE = "\u00ac";
|
|
14
|
+
export const ASCII_BLOCK1 = "\u00fe";
|
|
15
|
+
export const ASCII_BLOCK2 = "\u00ae";
|
|
16
|
+
export const ASCII_BLOCK3 = "\u00b8";
|
|
17
|
+
export const ASCII_RECTANGLE = "\u00ff";
|
|
18
|
+
export const ASCII_VLINE = "\u00af";
|
|
19
|
+
export const ASCII_HLINE = "-";
|
|
20
|
+
export const ASCII_POINT = "\u00a0";
|
|
21
|
+
export const ASCII_PI = "π";
|
|
22
|
+
export const ASCII_CURRENCY = "\u00a8";
|
|
23
|
+
export const ASCII_COPYRIGHT = "©";
|
|
24
|
+
export const ASCII_LDAB = "«";
|
|
25
|
+
export const ASCII_RDAB = "»";
|
|
26
|
+
export const ASCII_DOT = "·";
|
|
27
|
+
export const ASCII_PARAGRAPH = "¶";
|
|
28
|
+
|
|
29
|
+
export const BUTTON_CREATE = "[+]";
|
|
30
|
+
export const BUTTON_DELETE = "[-]";
|
|
31
|
+
export const BUTTON_SUBMIT = "[ASK]";
|
|
32
|
+
export const BUTTON_PREVIOUS = "<";
|
|
33
|
+
export const BUTTON_NEXT = ">";
|
|
34
|
+
export const BUTTON_LIFE = `[${ASCII_CURRENCY}]`;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {AVATAR_1, AVATAR_2} from "@layout/Header";
|
|
2
|
+
import {squircle} from "@utils/canvas";
|
|
3
|
+
import {getColorFromCSS} from "@utils/color";
|
|
4
|
+
|
|
5
|
+
const favIcon = () => {
|
|
6
|
+
const dpr = window.devicePixelRatio;
|
|
7
|
+
const canvas = document.createElement("canvas");
|
|
8
|
+
const size = 72 * dpr;
|
|
9
|
+
const w = 128 * dpr;
|
|
10
|
+
const h = 128 * dpr;
|
|
11
|
+
const r = 8 * dpr;
|
|
12
|
+
canvas.width = w;
|
|
13
|
+
canvas.height = h;
|
|
14
|
+
const ctx = canvas.getContext("2d", {alpha: true});
|
|
15
|
+
if (!ctx) return;
|
|
16
|
+
|
|
17
|
+
squircle(ctx, 0, 0, w, h, r);
|
|
18
|
+
ctx.clip();
|
|
19
|
+
|
|
20
|
+
ctx.globalCompositeOperation = "hard-light";
|
|
21
|
+
|
|
22
|
+
ctx.fillStyle = "#000";
|
|
23
|
+
ctx.fill();
|
|
24
|
+
ctx.fillStyle = getColorFromCSS("background");
|
|
25
|
+
ctx.fill();
|
|
26
|
+
ctx.fill();
|
|
27
|
+
|
|
28
|
+
ctx.translate(3 * dpr, -2 * dpr);
|
|
29
|
+
ctx.font = `normal ${size}px "VT220"`;
|
|
30
|
+
ctx.textAlign = "center";
|
|
31
|
+
ctx.textBaseline = "top";
|
|
32
|
+
|
|
33
|
+
ctx.shadowColor = getColorFromCSS("primary");
|
|
34
|
+
ctx.shadowBlur = 8 * dpr;
|
|
35
|
+
ctx.lineWidth = 2 * dpr;
|
|
36
|
+
ctx.strokeStyle = getColorFromCSS("secondary");
|
|
37
|
+
ctx.strokeText(AVATAR_1, w / 2, 0);
|
|
38
|
+
ctx.strokeText(AVATAR_2, w / 2, size * 0.9);
|
|
39
|
+
|
|
40
|
+
ctx.shadowBlur = 8 * dpr;
|
|
41
|
+
ctx.fillStyle = getColorFromCSS("primary");
|
|
42
|
+
ctx.fillText(AVATAR_1, w / 2, 0);
|
|
43
|
+
ctx.fillText(AVATAR_2, w / 2, size * 0.9);
|
|
44
|
+
ctx.fillStyle = getColorFromCSS("highlight");
|
|
45
|
+
ctx.fillText(AVATAR_1, w / 2, 0);
|
|
46
|
+
ctx.fillText(AVATAR_2, w / 2, size * 0.9);
|
|
47
|
+
|
|
48
|
+
const icon = document.createElement("link");
|
|
49
|
+
icon.rel = "icon";
|
|
50
|
+
icon.type = "image/png";
|
|
51
|
+
icon.href = canvas.toDataURL();
|
|
52
|
+
document.head.appendChild(icon);
|
|
53
|
+
|
|
54
|
+
const preview = document.createElement("img");
|
|
55
|
+
preview.id = "favicon";
|
|
56
|
+
preview.src = canvas.toDataURL();
|
|
57
|
+
preview.style.position = "absolute";
|
|
58
|
+
preview.style.bottom = "0";
|
|
59
|
+
preview.style.left = "0";
|
|
60
|
+
preview.style.width = `8rem`;
|
|
61
|
+
preview.style.height = `8rem`;
|
|
62
|
+
preview.style.border = `0.125rem dashed var(--color-primary)`;
|
|
63
|
+
preview.style.borderRadius = `0.75rem`;
|
|
64
|
+
preview.style.zIndex = "var(--z-index-debug)";
|
|
65
|
+
preview.style.display = "none";
|
|
66
|
+
document.body.appendChild(preview);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default favIcon;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {createContext, FC, ReactNode, useContext, useEffect} from "react";
|
|
2
|
+
|
|
3
|
+
import useKeyPress from "@hooks/useKeyPress";
|
|
4
|
+
|
|
5
|
+
import Config, {ConfigType} from "@console/config";
|
|
6
|
+
|
|
7
|
+
interface ConfigContextType {
|
|
8
|
+
config: Config;
|
|
9
|
+
getConfig: () => ConfigType;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ConfigContext = createContext<ConfigContextType | undefined>(undefined);
|
|
13
|
+
|
|
14
|
+
interface ConfigProviderProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ConfigProvider: FC<ConfigProviderProps> = ({children}) => {
|
|
19
|
+
const config = new Config();
|
|
20
|
+
const getConfig = () => config.config;
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
config.apply();
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
const debugHotKey = useKeyPress("Escape", {shft: true}, "keydown");
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (debugHotKey === 1) config.update("debug", "", !config.config.debug);
|
|
30
|
+
}, [debugHotKey]);
|
|
31
|
+
|
|
32
|
+
const value: ConfigContextType = {
|
|
33
|
+
config,
|
|
34
|
+
getConfig,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<ConfigContext.Provider value={value}>{children}</ConfigContext.Provider>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const useConfig = () => {
|
|
43
|
+
const context = useContext(ConfigContext);
|
|
44
|
+
if (context === undefined) {
|
|
45
|
+
throw new Error("useConfig must be used within a ConfigProvider");
|
|
46
|
+
}
|
|
47
|
+
return context;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default useConfig;
|