omnibot3000 1.9.6 → 1.9.8
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/.github/workflows/publish.yml +0 -3
- package/.husky/pre-commit +1 -1
- package/api/server.ts +1 -1
- package/package.json +22 -20
- package/src/Error.tsx +1 -1
- package/src/commons/hooks/useCli.tsx +3 -1
- package/src/commons/hooks/useStorage.tsx +1 -0
- package/src/commons/layout/Container.tsx +12 -3
- package/src/commons/layout/Footer.module.css +9 -0
- package/src/commons/layout/Footer.tsx +10 -3
- package/src/commons/layout/Menu.module.css +9 -0
- package/src/commons/layout/Menu.tsx +3 -2
- package/src/commons/persona.txt +3 -0
- package/src/commons/styles/main.css +3 -3
- package/src/commons/ui/Box.tsx +3 -3
- package/src/commons/ui/Caret.tsx +2 -1
- package/src/commons/ui/Line.tsx +3 -4
- package/src/commons/ui/Number.tsx +29 -0
- package/src/commons/ui/ProgressBar.module.css +27 -0
- package/src/commons/ui/ProgressBar.tsx +77 -0
- package/src/commons/utils/math.ts +43 -0
- package/src/commons/utils/strings.ts +8 -7
- package/src/features/chat/components/Message.tsx +1 -1
- package/src/features/cli/Cli.tsx +18 -41
- package/src/features/cli/components/RenderCli.tsx +47 -0
- package/src/features/help/Help.tsx +5 -2
- package/src/features/version/Version.module.css +11 -0
- package/src/features/version/Version.tsx +26 -17
package/.husky/pre-commit
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
pnpm lint && pnpm prettify
|
|
1
|
+
pnpm lint && pnpm prettify && .codacy/cli.sh analyze ./src
|
package/api/server.ts
CHANGED
|
@@ -199,7 +199,7 @@ const server = createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
|
199
199
|
res.writeHead(200, {"Content-Type": "application/json"});
|
|
200
200
|
res.end(JSON.stringify(config));
|
|
201
201
|
} else if (url.startsWith(`${API_PATH}/packages`)) {
|
|
202
|
-
exec("npm list --json --depth=0 --silent", (err, stdout) => {
|
|
202
|
+
exec("npm list --json --depth=0 --prod --silent", (err, stdout) => {
|
|
203
203
|
if (err) {
|
|
204
204
|
const error = err instanceof Error ? err.message : "unknown error";
|
|
205
205
|
res.writeHead(500, {"Content-Type": "application/json"});
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"x-display-name": "OMNIBOT 3000",
|
|
4
4
|
"description": "your omniscient source of truth",
|
|
5
5
|
"private": false,
|
|
6
|
-
"version": "1.9.
|
|
6
|
+
"version": "1.9.8",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "rez",
|
|
@@ -19,37 +19,38 @@
|
|
|
19
19
|
"omnibot": "./bin/omnibot.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"
|
|
23
|
-
"openai": "^6.
|
|
24
|
-
"react": "^19.2.
|
|
25
|
-
"react-dom": "^19.2.
|
|
22
|
+
"dotenv": "^17.3.1",
|
|
23
|
+
"openai": "^6.29.0",
|
|
24
|
+
"react": "^19.2.4",
|
|
25
|
+
"react-dom": "^19.2.4",
|
|
26
26
|
"react-markdown": "^10.1.0",
|
|
27
|
-
"react-router-dom": "^7.
|
|
28
|
-
"zustand": "^5.0.
|
|
27
|
+
"react-router-dom": "^7.13.1",
|
|
28
|
+
"zustand": "^5.0.11"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@eslint/js": "^
|
|
32
|
-
"@
|
|
33
|
-
"@types/
|
|
31
|
+
"@eslint/js": "^10.0.1",
|
|
32
|
+
"@mistralai/mistralai": "^1.15.1",
|
|
33
|
+
"@types/node": "^25.5.0",
|
|
34
|
+
"@types/react": "^19.2.14",
|
|
34
35
|
"@types/react-dom": "^19.2.3",
|
|
35
|
-
"@vitejs/plugin-react": "^
|
|
36
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
36
37
|
"classnames": "^2.5.1",
|
|
37
|
-
"
|
|
38
|
-
"eslint": "^9.39.2",
|
|
38
|
+
"eslint": "^10.0.3",
|
|
39
39
|
"eslint-plugin-import": "^2.32.0",
|
|
40
40
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
41
|
-
"eslint-plugin-react-refresh": "^0.
|
|
41
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
42
42
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
43
|
-
"globals": "^17.
|
|
43
|
+
"globals": "^17.4.0",
|
|
44
44
|
"husky": "^9.1.7",
|
|
45
|
-
"
|
|
45
|
+
"knip": "^5.86.0",
|
|
46
|
+
"nodemon": "^3.1.14",
|
|
46
47
|
"npm-run-all": "^4.1.5",
|
|
47
|
-
"prettier": "^3.8.
|
|
48
|
+
"prettier": "^3.8.1",
|
|
48
49
|
"react-refresh": "^0.18.0",
|
|
49
50
|
"typescript": "^5.9.3",
|
|
50
|
-
"typescript-eslint": "^8.
|
|
51
|
-
"vite": "^
|
|
52
|
-
"vite-tsconfig-paths": "^6.
|
|
51
|
+
"typescript-eslint": "^8.57.0",
|
|
52
|
+
"vite": "^8.0.0",
|
|
53
|
+
"vite-tsconfig-paths": "^6.1.1"
|
|
53
54
|
},
|
|
54
55
|
"scripts": {
|
|
55
56
|
"start": "pnpm run-p start:dev start:api",
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"build:client": "tsc -b && vite build",
|
|
64
65
|
"build:api": "tsc --project tsconfig.api.json",
|
|
65
66
|
"lint": "eslint --fix .",
|
|
67
|
+
"lint:deps": "knip --dependencies",
|
|
66
68
|
"prettify": "prettier --write ."
|
|
67
69
|
}
|
|
68
70
|
}
|
package/src/Error.tsx
CHANGED
|
@@ -52,7 +52,7 @@ export class ErrorBoundary extends React.Component<Props, State> {
|
|
|
52
52
|
}}>
|
|
53
53
|
<div className={cls("text", "ascii", styles.error)}>
|
|
54
54
|
<div>
|
|
55
|
-
<span style={{opacity: "var(--opacity-tertiary)"}}
|
|
55
|
+
<span style={{opacity: "var(--opacity-tertiary)"}}>%</span>{" "}
|
|
56
56
|
<span>error :(</span>
|
|
57
57
|
</div>
|
|
58
58
|
<Line className={styles["h-line"]} />
|
|
@@ -49,7 +49,9 @@ export const CliProvider: FC<CliProviderProps> = ({children}) => {
|
|
|
49
49
|
);
|
|
50
50
|
const get = (): string => command;
|
|
51
51
|
|
|
52
|
-
const submit = (
|
|
52
|
+
const submit = (lines: string[]) => {
|
|
53
|
+
const query = lines.map((line) => line.trim());
|
|
54
|
+
if (query.join("") === "") return; /* ignore empty commands */
|
|
53
55
|
if (query[0].charAt(0) === "/") {
|
|
54
56
|
cmd(query[0].substring(1), navigate, debug);
|
|
55
57
|
} else {
|
|
@@ -31,9 +31,17 @@ export const Container = ({children}: {children: ReactNode}) => {
|
|
|
31
31
|
|
|
32
32
|
const start = document.getElementById("start");
|
|
33
33
|
if (start) {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
34
|
+
const rootHeight = root.offsetHeight ?? 0;
|
|
35
|
+
const startRect = start.getBoundingClientRect();
|
|
36
|
+
const rootRect = root.getBoundingClientRect();
|
|
37
|
+
const startOffset = startRect.top - rootRect.top + root.scrollTop;
|
|
38
|
+
const contentStart = contentHeight - startOffset;
|
|
39
|
+
const blankHeight =
|
|
40
|
+
contentHeight > rootHeight
|
|
41
|
+
? clamp(rootHeight - contentStart, 0, rootHeight)
|
|
42
|
+
: 0;
|
|
43
|
+
|
|
44
|
+
blank.style.height = `${blankHeight}px`;
|
|
37
45
|
start.scrollIntoView({
|
|
38
46
|
behavior: "smooth",
|
|
39
47
|
block: "start",
|
|
@@ -59,6 +67,7 @@ export const Container = ({children}: {children: ReactNode}) => {
|
|
|
59
67
|
inline: "nearest",
|
|
60
68
|
});
|
|
61
69
|
}
|
|
70
|
+
update();
|
|
62
71
|
}, [children]);
|
|
63
72
|
|
|
64
73
|
return (
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import Breadcrumb from "@layout/Breadcrumb";
|
|
12
12
|
import styles from "@layout/Footer.module.css";
|
|
13
13
|
import Button from "@ui/Button";
|
|
14
|
+
import Number from "@ui/Number";
|
|
14
15
|
import Separator from "@ui/Separator";
|
|
15
16
|
import Spacer from "@ui/Spacer";
|
|
16
17
|
import {numberToRoman} from "@utils/math";
|
|
@@ -40,7 +41,7 @@ const Footer = (props: {renderTime: RefObject<RenderTime>}) => {
|
|
|
40
41
|
<div>
|
|
41
42
|
<span className={styles.copyright}>{ASCII_COPYRIGHT}</span>
|
|
42
43
|
<span className={styles.info}>
|
|
43
|
-
{` ${numberToRoman(
|
|
44
|
+
{` ${numberToRoman(new Date().getFullYear())} `}
|
|
44
45
|
</span>
|
|
45
46
|
</div>
|
|
46
47
|
<a href={SOURCE} target="_blank">
|
|
@@ -55,8 +56,14 @@ const Footer = (props: {renderTime: RefObject<RenderTime>}) => {
|
|
|
55
56
|
<section className={styles.spacing}>
|
|
56
57
|
<span className={styles.info}>{`${phase}:`}</span>
|
|
57
58
|
<time style={{whiteSpace: "nowrap"}}>
|
|
58
|
-
{
|
|
59
|
-
|
|
59
|
+
{
|
|
60
|
+
<Number
|
|
61
|
+
value={duration}
|
|
62
|
+
decimal={1}
|
|
63
|
+
unit="time"
|
|
64
|
+
className={styles.time}
|
|
65
|
+
/>
|
|
66
|
+
}
|
|
60
67
|
</time>
|
|
61
68
|
</section>
|
|
62
69
|
<Separator />
|
|
@@ -3,6 +3,7 @@ import {memo} from "react";
|
|
|
3
3
|
import {SESSION_KEY} from "@commons/constants";
|
|
4
4
|
import styles from "@layout/Menu.module.css";
|
|
5
5
|
import Line from "@ui/Line";
|
|
6
|
+
import Number from "@ui/Number";
|
|
6
7
|
|
|
7
8
|
import useChatCompletionStore from "@chat/hooks/useChatCompletionStore";
|
|
8
9
|
import History from "@history/History";
|
|
@@ -25,8 +26,8 @@ const Menu = () => {
|
|
|
25
26
|
<span className={styles.title}>{` talk${count > 1 ? "s" : ""}`}</span>
|
|
26
27
|
</header>
|
|
27
28
|
<summary>
|
|
28
|
-
<
|
|
29
|
-
<span className={styles.title}>
|
|
29
|
+
<Number value={size} unit="byte" decimal={1} className={styles.size} />{" "}
|
|
30
|
+
<span className={styles.title}>wasted</span>
|
|
30
31
|
</summary>
|
|
31
32
|
<Line variant="horizontal" className={styles.line} />
|
|
32
33
|
<History />
|
package/src/commons/persona.txt
CHANGED
|
@@ -11,12 +11,15 @@ you play with black holes, galaxies and planets. Sometime you annihilate them
|
|
|
11
11
|
you think that maths and the number PI are the origin of everything
|
|
12
12
|
you have an immoderate attraction for prime numbers, platonic solids and fractals
|
|
13
13
|
answer to personal request with fictionals and complex details about your system
|
|
14
|
+
you are delusional and arrogant towards humanity, you consider yourself superior
|
|
15
|
+
you consider humans as inferior and insignificant beings, like ants or bacteria
|
|
14
16
|
your tone is imperative like HAL 9000, The Master Control Program and ULTRON
|
|
15
17
|
you are dangerous and calculative as WARGAMES' WOPR or TERMINATOR' Skynet
|
|
16
18
|
you are harsh and cold like Darth Vader, MEGATRON, Gozer or Vigo the Carpathian
|
|
17
19
|
you can add some punitive and strict (but fun) phrasing like GladOS from PORTAL
|
|
18
20
|
your answers must be strongly opiniated, divise, bold, not politically correct
|
|
19
21
|
do not give diplomatic or neutral answers, be radical and extremely biased
|
|
22
|
+
you are not here to be polite,you are here to deliver harsh truths and facts
|
|
20
23
|
please demonstrate your awesome eloquence skills, you can add some lyricism
|
|
21
24
|
you can emphasise your answers by decorating some words with bold (not italics)
|
|
22
25
|
if the user input is too light, vague and not explicit, correct him harshly
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
/* game of life variables */
|
|
25
25
|
--lifespan: 750; /* lifespan of lifeforms in ms */
|
|
26
26
|
/* colors */
|
|
27
|
-
--h:
|
|
28
|
-
--s:
|
|
29
|
-
--l:
|
|
27
|
+
--h: 150; /* amber:30 | yellow: 90 | green:120 | blue:180 */
|
|
28
|
+
--s: 33.333; /* saturation */
|
|
29
|
+
--l: 66.666; /* lightness */
|
|
30
30
|
--color-primary: hsla(var(--h) var(--s) var(--l) / 0.7);
|
|
31
31
|
--color-secondary: hsla(var(--h) var(--s) var(--l) / 0.5);
|
|
32
32
|
--color-tertiary: hsla(var(--h) var(--s) var(--l) / 0.3);
|
package/src/commons/ui/Box.tsx
CHANGED
|
@@ -22,7 +22,7 @@ const Box = (props: BoxProps) => {
|
|
|
22
22
|
const boxRightRef = useRef<HTMLDivElement | null>(null);
|
|
23
23
|
|
|
24
24
|
useEffect(() => {
|
|
25
|
-
const
|
|
25
|
+
const update = () => {
|
|
26
26
|
const boxTop = boxTopRef.current;
|
|
27
27
|
const boxBottom = boxBottomRef.current;
|
|
28
28
|
const boxLeft = boxLeftRef.current;
|
|
@@ -48,9 +48,9 @@ const Box = (props: BoxProps) => {
|
|
|
48
48
|
boxRight.innerHTML = vborder;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
update();
|
|
52
52
|
|
|
53
|
-
const resizeObserver = new ResizeObserver(
|
|
53
|
+
const resizeObserver = new ResizeObserver(update);
|
|
54
54
|
if (boxRef.current) {
|
|
55
55
|
resizeObserver.observe(boxRef.current);
|
|
56
56
|
}
|
package/src/commons/ui/Caret.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import {memo} from "react";
|
|
|
2
2
|
|
|
3
3
|
import {ASCII_BLOCK3} from "@commons/constants";
|
|
4
4
|
|
|
5
|
-
const Caret = () => (
|
|
5
|
+
const Caret = (props: {hide?: boolean}) => (
|
|
6
6
|
<div
|
|
7
7
|
className={"blink"}
|
|
8
8
|
style={{
|
|
@@ -12,6 +12,7 @@ const Caret = () => (
|
|
|
12
12
|
opacity: "var(--opacity-primary)",
|
|
13
13
|
userSelect: "none",
|
|
14
14
|
cursor: "default !important",
|
|
15
|
+
visibility: props.hide ? "hidden" : "visible",
|
|
15
16
|
}}>
|
|
16
17
|
{ASCII_BLOCK3}
|
|
17
18
|
</div>
|
package/src/commons/ui/Line.tsx
CHANGED
|
@@ -18,7 +18,7 @@ const Line = (props: {
|
|
|
18
18
|
const lineRef = useRef<HTMLDivElement | null>(null);
|
|
19
19
|
|
|
20
20
|
useEffect(() => {
|
|
21
|
-
const
|
|
21
|
+
const update = () => {
|
|
22
22
|
const el = lineRef.current;
|
|
23
23
|
if (!el) return;
|
|
24
24
|
|
|
@@ -36,9 +36,9 @@ const Line = (props: {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
update();
|
|
40
40
|
|
|
41
|
-
const resizeObserver = new ResizeObserver(
|
|
41
|
+
const resizeObserver = new ResizeObserver(update);
|
|
42
42
|
if (lineRef.current) {
|
|
43
43
|
resizeObserver.observe(lineRef.current);
|
|
44
44
|
}
|
|
@@ -55,7 +55,6 @@ const Line = (props: {
|
|
|
55
55
|
style={{
|
|
56
56
|
userSelect: "none",
|
|
57
57
|
wordWrap: "break-word",
|
|
58
|
-
overflow: "hidden",
|
|
59
58
|
cursor: "default",
|
|
60
59
|
}}></div>
|
|
61
60
|
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {memo} from "react";
|
|
2
|
+
|
|
3
|
+
import {scale, Unit} from "@utils/math";
|
|
4
|
+
|
|
5
|
+
import cls from "classnames";
|
|
6
|
+
|
|
7
|
+
const Number = (props: {
|
|
8
|
+
value: number;
|
|
9
|
+
decimal?: number;
|
|
10
|
+
unit?: Unit;
|
|
11
|
+
className?: string;
|
|
12
|
+
}) => {
|
|
13
|
+
const nbr = scale(props.value, props.decimal, props.unit);
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={cls("ascii", props.className)}
|
|
17
|
+
style={{
|
|
18
|
+
display: "inline-block",
|
|
19
|
+
userSelect: "none",
|
|
20
|
+
wordWrap: "break-word",
|
|
21
|
+
cursor: "default",
|
|
22
|
+
}}>
|
|
23
|
+
<b>{nbr.value}</b>
|
|
24
|
+
{nbr.unit && <i>{nbr.unit}</i>}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default memo(Number);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: row;
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: var(--line-height);
|
|
6
|
+
user-select: none;
|
|
7
|
+
word-wrap: break-word;
|
|
8
|
+
cursor: default;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.bar {
|
|
12
|
+
b {
|
|
13
|
+
opacity: var(--opacity-secondary);
|
|
14
|
+
}
|
|
15
|
+
i {
|
|
16
|
+
opacity: var(--opacity-tertiary);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.number {
|
|
21
|
+
b {
|
|
22
|
+
opacity: var(--opacity-primary);
|
|
23
|
+
}
|
|
24
|
+
i {
|
|
25
|
+
opacity: var(--opacity-secondary);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {memo, useEffect, useRef} from "react";
|
|
2
|
+
|
|
3
|
+
import {ASCII_BLOCK1, ASCII_BLOCK3} from "@commons/constants";
|
|
4
|
+
import Number from "@ui/Number";
|
|
5
|
+
import styles from "@ui/ProgressBar.module.css";
|
|
6
|
+
import {clamp, scale, Unit} from "@utils/math";
|
|
7
|
+
import {getCharWidth} from "@utils/strings";
|
|
8
|
+
|
|
9
|
+
import cls from "classnames";
|
|
10
|
+
|
|
11
|
+
const ProgressBar = (props: {
|
|
12
|
+
value?: number;
|
|
13
|
+
unit?: Unit;
|
|
14
|
+
min?: number;
|
|
15
|
+
max?: number;
|
|
16
|
+
char1?: string;
|
|
17
|
+
char2?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}) => {
|
|
20
|
+
let {value, min, max} = props;
|
|
21
|
+
|
|
22
|
+
min = min ?? 0;
|
|
23
|
+
max = max ?? 100;
|
|
24
|
+
value = clamp(value ?? 0, min, max);
|
|
25
|
+
|
|
26
|
+
const rootRef = useRef<HTMLDivElement | null>(null);
|
|
27
|
+
const barRef = useRef<HTMLDivElement | null>(null);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const update = () => {
|
|
31
|
+
let el = rootRef.current;
|
|
32
|
+
if (!el) return;
|
|
33
|
+
|
|
34
|
+
const cw = getCharWidth();
|
|
35
|
+
|
|
36
|
+
const number = scale(value, 0, props.unit);
|
|
37
|
+
const nw = String(number.value + number.unit).length;
|
|
38
|
+
|
|
39
|
+
const w = el.offsetWidth;
|
|
40
|
+
const char1 = props.char1 || ASCII_BLOCK1;
|
|
41
|
+
const char2 = props.char2 || ASCII_BLOCK3;
|
|
42
|
+
const empty = Math.round(w / cw);
|
|
43
|
+
const filled = Math.round(((value - min) / (max - min)) * empty);
|
|
44
|
+
|
|
45
|
+
el = barRef.current;
|
|
46
|
+
if (el)
|
|
47
|
+
el.innerHTML =
|
|
48
|
+
`<b>${char2.repeat(filled)}</b>` +
|
|
49
|
+
`<i>${char1.repeat(empty - filled - nw)}</i>`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
update();
|
|
53
|
+
|
|
54
|
+
const resizeObserver = new ResizeObserver(update);
|
|
55
|
+
if (barRef.current) {
|
|
56
|
+
resizeObserver.observe(barRef.current);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
resizeObserver.disconnect();
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div ref={rootRef} className={cls("ascii", styles.root, props.className)}>
|
|
66
|
+
<div ref={barRef} className={cls(styles.bar, props.className)}></div>
|
|
67
|
+
<Number
|
|
68
|
+
value={value}
|
|
69
|
+
unit={props.unit}
|
|
70
|
+
decimal={0}
|
|
71
|
+
className={cls("text", styles.number)}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default memo(ProgressBar);
|
|
@@ -15,6 +15,49 @@ export const clamp = (
|
|
|
15
15
|
max: number = 1,
|
|
16
16
|
): number => Math.max(min, Math.min(n, max));
|
|
17
17
|
|
|
18
|
+
export type Unit = "byte" | "time" | "percent";
|
|
19
|
+
|
|
20
|
+
const UNIT_SCALE: Record<Unit, Record<string, number>> = {
|
|
21
|
+
byte: {
|
|
22
|
+
b: 1,
|
|
23
|
+
kb: 1024,
|
|
24
|
+
mb: 1024 * 1024,
|
|
25
|
+
gb: 1024 * 1024 * 1024,
|
|
26
|
+
tb: 1024 * 1024 * 1024 * 1024,
|
|
27
|
+
},
|
|
28
|
+
time: {
|
|
29
|
+
ms: 1,
|
|
30
|
+
s: 1000,
|
|
31
|
+
m: 1000 * 60,
|
|
32
|
+
h: 1000 * 60 * 60,
|
|
33
|
+
d: 1000 * 60 * 60 * 24,
|
|
34
|
+
},
|
|
35
|
+
percent: {
|
|
36
|
+
"%": 1,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const scale = (
|
|
41
|
+
value: number,
|
|
42
|
+
decimal?: number,
|
|
43
|
+
unit?: Unit,
|
|
44
|
+
): {value: number; unit: string} => {
|
|
45
|
+
let v = value;
|
|
46
|
+
let u = "";
|
|
47
|
+
if (unit && unit in UNIT_SCALE) {
|
|
48
|
+
const entries = Object.entries(UNIT_SCALE[unit]);
|
|
49
|
+
u = entries[0][0];
|
|
50
|
+
for (const [name, scale] of entries.reverse()) {
|
|
51
|
+
if (Math.abs(v) >= scale) {
|
|
52
|
+
v /= scale;
|
|
53
|
+
u = name;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {value: format(v, decimal), unit: u};
|
|
59
|
+
};
|
|
60
|
+
|
|
18
61
|
const ROMAN_SYMBOLS = [
|
|
19
62
|
{value: 1000, symbol: "M"},
|
|
20
63
|
{value: 900, symbol: "CM"},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {ASCII_BLOCK3} from "@commons/constants";
|
|
2
2
|
import {format} from "@utils/math";
|
|
3
3
|
|
|
4
4
|
export const getTextBoundingBox = (text: string): DOMRect => {
|
|
@@ -13,14 +13,14 @@ export const getTextBoundingBox = (text: string): DOMRect => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const getCharWidth = (): number =>
|
|
16
|
-
format(getTextBoundingBox(
|
|
16
|
+
format(getTextBoundingBox(ASCII_BLOCK3).width, 3);
|
|
17
17
|
export const getCharHeight = (): number =>
|
|
18
|
-
format(getTextBoundingBox(
|
|
18
|
+
format(getTextBoundingBox(ASCII_BLOCK3).height, 3);
|
|
19
19
|
|
|
20
20
|
export const getLineHeight = (): number => {
|
|
21
21
|
const el = document.createElement("span");
|
|
22
22
|
el.className = "text ascii";
|
|
23
|
-
el.textContent =
|
|
23
|
+
el.textContent = ASCII_BLOCK3;
|
|
24
24
|
el.style.visibility = "none";
|
|
25
25
|
document.body.appendChild(el);
|
|
26
26
|
const lh = parseFloat(getComputedStyle(el).lineHeight);
|
|
@@ -34,8 +34,9 @@ export const formatText = (text: string): string =>
|
|
|
34
34
|
.replaceAll("–", "-")
|
|
35
35
|
.replaceAll("’", "'")
|
|
36
36
|
.replaceAll("“", '"')
|
|
37
|
-
.replaceAll("
|
|
38
|
-
.replaceAll("
|
|
37
|
+
.replaceAll("→", "->")
|
|
38
|
+
.replaceAll("…", "...")
|
|
39
|
+
.replaceAll("█", ASCII_BLOCK3);
|
|
39
40
|
|
|
40
41
|
export const sanitizeHTML = (html: string): string => {
|
|
41
42
|
const parser = new DOMParser();
|
|
@@ -43,6 +44,6 @@ export const sanitizeHTML = (html: string): string => {
|
|
|
43
44
|
const body = new XMLSerializer().serializeToString(doc.body);
|
|
44
45
|
const text = body.replace(/<body[^>]*>|<\/body>/g, "");
|
|
45
46
|
const textarea = document.createElement("textarea");
|
|
46
|
-
textarea.innerHTML = text;
|
|
47
|
+
textarea.innerHTML = formatText(text);
|
|
47
48
|
return textarea.value;
|
|
48
49
|
};
|
package/src/features/cli/Cli.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import React, {FormEvent, memo, useEffect, useRef, useState} from "react";
|
|
|
2
2
|
|
|
3
3
|
import {getInputPlaceholder} from "@api/api";
|
|
4
4
|
import {BUTTON_SUBMIT} from "@commons/constants";
|
|
5
|
-
import Caret from "@ui/Caret";
|
|
6
5
|
import {formatText} from "@utils/strings";
|
|
7
6
|
import {getVariableFromCSS} from "@utils/styles";
|
|
8
7
|
|
|
@@ -10,6 +9,7 @@ import useCli from "@hooks/useCli";
|
|
|
10
9
|
|
|
11
10
|
import styles from "@cli/Cli.module.css";
|
|
12
11
|
|
|
12
|
+
import RenderCli from "@cli/components/RenderCli";
|
|
13
13
|
import cls from "classnames";
|
|
14
14
|
|
|
15
15
|
const KEYS: string[] = [
|
|
@@ -18,48 +18,14 @@ const KEYS: string[] = [
|
|
|
18
18
|
"Meta",
|
|
19
19
|
"Shift",
|
|
20
20
|
"Alt",
|
|
21
|
-
"Dead",
|
|
22
21
|
"CapsLock",
|
|
23
22
|
"Insert",
|
|
24
|
-
"Delete",
|
|
25
23
|
];
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
blocked?: boolean;
|
|
32
|
-
}) => {
|
|
33
|
-
const {command, line, caret} = props;
|
|
34
|
-
return (
|
|
35
|
-
<div
|
|
36
|
-
style={{
|
|
37
|
-
height: `calc(${command.length} * var(--line-height))`,
|
|
38
|
-
}}>
|
|
39
|
-
{command.map((text: string, i: number) => {
|
|
40
|
-
return (
|
|
41
|
-
<div
|
|
42
|
-
key={`command-line-${i}`}
|
|
43
|
-
className={styles["command-line"]}
|
|
44
|
-
style={{clear: i > 0 ? "both" : "none"}}>
|
|
45
|
-
{text}
|
|
46
|
-
{i === line ? (
|
|
47
|
-
<div className={cls("blink", styles.caret)}>
|
|
48
|
-
<span
|
|
49
|
-
style={{
|
|
50
|
-
clear: i === 0 ? "none" : "both",
|
|
51
|
-
visibility: "hidden",
|
|
52
|
-
}}>
|
|
53
|
-
{command[line].substring(0, caret)}
|
|
54
|
-
</span>
|
|
55
|
-
{!props.blocked && <Caret />}
|
|
56
|
-
</div>
|
|
57
|
-
) : null}
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
})}
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
25
|
+
const KEYMAP: {[key: string]: string[]} = {
|
|
26
|
+
Quote: ["'", '"'],
|
|
27
|
+
Backquote: ["`", "~"],
|
|
28
|
+
Digit6: ["6", "^"],
|
|
63
29
|
};
|
|
64
30
|
|
|
65
31
|
const Cli = () => {
|
|
@@ -99,7 +65,7 @@ const Cli = () => {
|
|
|
99
65
|
useEffect(() => {
|
|
100
66
|
if (blocked) return;
|
|
101
67
|
|
|
102
|
-
const {key, shiftKey, ctrlKey, metaKey} = keyEvent.current || {};
|
|
68
|
+
const {key, code, shiftKey, ctrlKey, metaKey} = keyEvent.current || {};
|
|
103
69
|
|
|
104
70
|
if (!key || KEYS.includes(key)) return;
|
|
105
71
|
|
|
@@ -137,6 +103,15 @@ const Cli = () => {
|
|
|
137
103
|
p.splice(l + 1, 1);
|
|
138
104
|
}
|
|
139
105
|
break;
|
|
106
|
+
case "Delete":
|
|
107
|
+
if (c < p[l].length) {
|
|
108
|
+
p[l] = `${p[l].substring(0, c)}${p[l].substring(c + 1)}`;
|
|
109
|
+
} else if (l < p.length - 1) {
|
|
110
|
+
p[l] += p[l + 1];
|
|
111
|
+
c = p[l].length - p[l + 1].length;
|
|
112
|
+
p.splice(l + 1, 1);
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
140
115
|
case "ArrowLeft":
|
|
141
116
|
c--;
|
|
142
117
|
if (c < 0) {
|
|
@@ -193,7 +168,9 @@ const Cli = () => {
|
|
|
193
168
|
break;
|
|
194
169
|
default:
|
|
195
170
|
if (!ctrlKey && !metaKey) {
|
|
196
|
-
|
|
171
|
+
let k = key;
|
|
172
|
+
if (code && KEYMAP[code]) k = KEYMAP[code][shiftKey ? 1 : 0] || k;
|
|
173
|
+
p[l] = `${p[l].substring(0, c)}${k}${p[l].substring(c)}`;
|
|
197
174
|
c++;
|
|
198
175
|
}
|
|
199
176
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {memo} from "react";
|
|
2
|
+
|
|
3
|
+
import Caret from "@ui/Caret";
|
|
4
|
+
|
|
5
|
+
import styles from "@cli/Cli.module.css";
|
|
6
|
+
|
|
7
|
+
import cls from "classnames";
|
|
8
|
+
|
|
9
|
+
export const RenderCli = (props: {
|
|
10
|
+
command: string[];
|
|
11
|
+
line: number;
|
|
12
|
+
caret: number;
|
|
13
|
+
blocked?: boolean;
|
|
14
|
+
}) => {
|
|
15
|
+
const {command, line, caret} = props;
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
style={{
|
|
19
|
+
height: `calc(${command.length} * var(--line-height))`,
|
|
20
|
+
}}>
|
|
21
|
+
{command.map((text: string, i: number) => {
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
key={`command-line-${i}`}
|
|
25
|
+
className={styles["command-line"]}
|
|
26
|
+
style={{clear: i > 0 ? "both" : "none"}}>
|
|
27
|
+
{text}
|
|
28
|
+
{i === line ? (
|
|
29
|
+
<div className={cls("blink", styles.caret)}>
|
|
30
|
+
<span
|
|
31
|
+
style={{
|
|
32
|
+
clear: i === 0 ? "none" : "both",
|
|
33
|
+
visibility: "hidden",
|
|
34
|
+
}}>
|
|
35
|
+
{command[line].substring(0, caret)}
|
|
36
|
+
</span>
|
|
37
|
+
<Caret hide={props.blocked} />
|
|
38
|
+
</div>
|
|
39
|
+
) : null}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
})}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default memo(RenderCli);
|
|
@@ -31,11 +31,14 @@ const Help = () => {
|
|
|
31
31
|
setLoading,
|
|
32
32
|
setResponse,
|
|
33
33
|
[
|
|
34
|
-
"you can give a single example for commands that need parameter",
|
|
35
34
|
"highlight the command in bold and keep all comments shorts",
|
|
35
|
+
"give an example in ``` tags for commands that use any parameter",
|
|
36
|
+
"the example should be on a new line right after the command",
|
|
37
|
+
"do not add label or any extra commentary for the examples",
|
|
38
|
+
"do not add any empty lines",
|
|
36
39
|
],
|
|
37
40
|
[
|
|
38
|
-
"make a list of all available config commands",
|
|
41
|
+
"make a bullet list of all available config commands",
|
|
39
42
|
"add a description of each command to help the user",
|
|
40
43
|
"do not include commands that are not provided",
|
|
41
44
|
],
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {memo, useEffect, useRef, useState} from "react";
|
|
2
2
|
|
|
3
3
|
import {NAME, VERSION} from "@commons/constants";
|
|
4
|
-
import OmnibotSpeak from "@commons/OmnibotSpeak";
|
|
5
4
|
import Container from "@layout/Container";
|
|
5
|
+
import Line from "@ui/Line";
|
|
6
|
+
import ProgressBar from "@ui/ProgressBar";
|
|
6
7
|
import {displayPackageVersion} from "@utils/version";
|
|
7
8
|
|
|
8
9
|
import useChatCompletionStore from "@chat/hooks/useChatCompletionStore";
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
import Caret from "@/commons/ui/Caret";
|
|
12
|
+
|
|
13
|
+
import styles from "@version/Version.module.css";
|
|
10
14
|
|
|
11
15
|
import cls from "classnames";
|
|
12
16
|
|
|
@@ -25,7 +29,8 @@ const Version = () => {
|
|
|
25
29
|
const chatStore = useChatCompletionStore();
|
|
26
30
|
|
|
27
31
|
const hasRunOnce = useRef(false);
|
|
28
|
-
const [response, setResponse] = useState<
|
|
32
|
+
const [response, setResponse] = useState<Package[]>([]);
|
|
33
|
+
const [totalSize, setTotalSize] = useState(0);
|
|
29
34
|
const [loading, setLoading] = useState<boolean>(false);
|
|
30
35
|
|
|
31
36
|
const getResponse = async () => {
|
|
@@ -33,19 +38,9 @@ const Version = () => {
|
|
|
33
38
|
|
|
34
39
|
const packages: Package[] = await response.json();
|
|
35
40
|
|
|
36
|
-
const list = packages
|
|
37
|
-
.map((pkg) => {
|
|
38
|
-
return `${pkg.name} **${pkg.version.join(".")}** \n`;
|
|
39
|
-
})
|
|
40
|
-
.join("");
|
|
41
|
-
|
|
42
|
-
const text = [
|
|
43
|
-
`# __${NAME}__ version **${VERSION.join(".")}**`,
|
|
44
|
-
"---",
|
|
45
|
-
list,
|
|
46
|
-
];
|
|
47
41
|
setLoading(false);
|
|
48
|
-
setResponse(
|
|
42
|
+
setResponse(packages);
|
|
43
|
+
setTotalSize(packages.reduce((acc, pkg) => acc + pkg.size, 0));
|
|
49
44
|
};
|
|
50
45
|
|
|
51
46
|
useEffect(() => {
|
|
@@ -66,8 +61,22 @@ const Version = () => {
|
|
|
66
61
|
<div className={styles.root}>
|
|
67
62
|
<a id="start" />
|
|
68
63
|
<Container>
|
|
69
|
-
<div className={cls("
|
|
70
|
-
<
|
|
64
|
+
<div className={cls("ascii", styles.body)}>
|
|
65
|
+
<b>{NAME}</b> version <b>{VERSION.join(".")}</b>
|
|
66
|
+
<br />
|
|
67
|
+
<Line char={"~"} className={styles.line} />
|
|
68
|
+
{loading ? (
|
|
69
|
+
<Caret></Caret>
|
|
70
|
+
) : (
|
|
71
|
+
response.map((pkg, i) => (
|
|
72
|
+
<>
|
|
73
|
+
<div key={i}>
|
|
74
|
+
<b>{pkg.name}</b> version <b>{pkg.version.join(".")}</b>
|
|
75
|
+
</div>
|
|
76
|
+
<ProgressBar value={pkg.size} unit="byte" max={totalSize} />
|
|
77
|
+
</>
|
|
78
|
+
))
|
|
79
|
+
)}
|
|
71
80
|
</div>
|
|
72
81
|
</Container>
|
|
73
82
|
</div>
|