From 9eb19bf85b66f10f9e1baaa747eefa077c45fa05 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Thu, 10 Aug 2023 23:09:24 -0500 Subject: [PATCH] make it look nicer --- .tokeignore | 1 + README.md | 6 + client/App.tsx | 10 +- client/Chat.tsx | 70 ++++++ client/Grub.tsx | 86 +++++++ client/Room.tsx | 126 +++------- client/Upload.tsx | 183 ++++++++++---- client/global.css | 4 + client/lib/roomContext.ts | 8 + package-lock.json | 496 +++++++++++++++++++++++++++++++++++++- package.json | 6 + src/main.rs | 2 +- src/message.rs | 2 +- tsconfig.json | 1 - vite.config.ts | 2 +- 15 files changed, 857 insertions(+), 146 deletions(-) create mode 100644 .tokeignore create mode 100644 client/Chat.tsx create mode 100644 client/Grub.tsx create mode 100644 client/global.css create mode 100644 client/lib/roomContext.ts diff --git a/.tokeignore b/.tokeignore new file mode 100644 index 0000000..483a9c4 --- /dev/null +++ b/.tokeignore @@ -0,0 +1 @@ +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 039856f..2f6a57c 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,9 @@ npm ci npm run build cargo build --release ``` + +To run: + +``` +grub [--port 6106] +``` diff --git a/client/App.tsx b/client/App.tsx index b80c1b0..92ffeef 100644 --- a/client/App.tsx +++ b/client/App.tsx @@ -1,5 +1,9 @@ +import "bootstrap/dist/css/bootstrap.min.css"; +import "./global.css"; + import { v4 } from "uuid"; import Room from "./Room"; +import { Container } from "react-bootstrap"; export default function App() { const x = new URL(window.location.href); @@ -9,5 +13,9 @@ export default function App() { location.href = `/${v4()}`; } - return ; + return ( + + + + ); } diff --git a/client/Chat.tsx b/client/Chat.tsx new file mode 100644 index 0000000..595e246 --- /dev/null +++ b/client/Chat.tsx @@ -0,0 +1,70 @@ +import { useContext, useState } from "react"; +import useWebSocket from "react-use-websocket"; +import { wsUrl } from "./constants"; +import CBOR from "cbor"; +import * as uuid from "uuid"; +import { RoomContext } from "./lib/roomContext"; + +export default function Chat() { + const { roomId, clientId } = useContext(RoomContext); + const [chats, setChats] = useState([]); + const [message, setMessage] = useState(""); + + const { + sendMessage, + lastJsonMessage, + readyState: newReadyState, + } = useWebSocket(wsUrl, { + share: true, + onOpen: ({}) => { + console.log("Shiet, connected."); + }, + onMessage: async (event) => { + const data = CBOR.decode(await event.data.arrayBuffer()); + console.log("received", data); + + if (data.type === "ChatMessage") { + setChats([...chats, data]); + } + }, + }); + + function sendCborMessage(data) { + let cbor = CBOR.encode(data); + sendMessage(cbor); + } + + function onSubmit(e) { + e.preventDefault(); + sendCborMessage({ + type: "ChatMessage", + timestamp: new Date().toISOString(), + message_id: uuid.v4(), + room_id: roomId, + author: clientId, + content: message, + }); + setMessage(""); + } + + return ( + <> + Messages: + +
+ setMessage(e.target.value)} + placeholder="Type a message..." + /> +
+ + ); +} diff --git a/client/Grub.tsx b/client/Grub.tsx new file mode 100644 index 0000000..5c8db79 --- /dev/null +++ b/client/Grub.tsx @@ -0,0 +1,86 @@ +import * as Automerge from "@automerge/automerge"; +import { useContext, useState } from "react"; +import useWebSocket from "react-use-websocket"; +import { wsUrl } from "./constants"; +import CBOR from "cbor"; +import { RoomContext } from "./lib/roomContext"; + +export default function Grub() { + const { roomId, clientId } = useContext(RoomContext); + const [doc, setDoc] = useState(Automerge.init()); + const [syncState, setSyncState] = useState(Automerge.initSyncState()); + const [addItemName, setAddItemName] = useState(""); + + function updateDoc(newDoc) { + setDoc(newDoc); + } + + const { sendMessage } = useWebSocket(wsUrl, { + share: true, + onOpen: ({}) => { + console.log("Shiet, connected."); + }, + onMessage: async (event) => { + const data = CBOR.decode(await event.data.arrayBuffer()); + + if (data.type === "Automerge") { + const [nextDoc, nextSyncState, patch] = Automerge.receiveSyncMessage( + doc, + syncState, + data.message[1] + ); + setDoc(nextDoc); + setSyncState(nextSyncState); + console.log("patch", patch); + } + }, + }); + + function sendCborMessage(data) { + let cbor = CBOR.encode(data); + sendMessage(cbor); + } + + function addItem(e) { + e.preventDefault(); + const newDoc = Automerge.change(doc, (doc) => { + if (!doc.items) doc.items = []; + doc.items.push({ + id: uuid.v4(), + content: addItemName, + }); + }); + updateDoc(newDoc); + const [syncMessage, binary] = Automerge.generateSyncMessage(doc, syncState); + if (syncMessage) + sendCborMessage({ + type: "Automerge", + client_id: clientId, + room_id: roomId, + message: binary, + }); + setAddItemName(""); + } + + const items = doc.items || []; + + return ( + <> + Grubs: + +
+ setAddItemName(e.target.value)} + placeholder="Type a message..." + /> + +
+ + ); +} diff --git a/client/Room.tsx b/client/Room.tsx index 8a8bbce..a08609c 100644 --- a/client/Room.tsx +++ b/client/Room.tsx @@ -3,9 +3,13 @@ import { useEffect, useState } from "react"; import useWebSocket, { ReadyState } from "react-use-websocket"; import * as Automerge from "@automerge/automerge"; import * as uuid from "uuid"; -import {} from "libsodium"; -import Upload from "./Upload"; import { wsUrl } from "./constants"; +import { Tab, Tabs } from "react-bootstrap"; +import Upload from "./Upload"; +import Chat from "./Chat"; +import Grub from "./Grub"; +import { RoomContext } from "./lib/roomContext"; +import { Wifi } from "react-bootstrap-icons"; const connectionStatusMap = { [ReadyState.CONNECTING]: "Connecting", @@ -19,17 +23,6 @@ export default function Room({ roomId }) { const [readyState, setReadyState] = useState(ReadyState.CLOSED); const [connectedClients, setConnectedClients] = useState([]); const [clientId, setClientId] = useState(null); - const [chats, setChats] = useState([]); - - const [doc, setDoc] = useState(Automerge.init()); - const [syncState, setSyncState] = useState(Automerge.initSyncState()); - - const [message, setMessage] = useState(""); - const [addItemName, setAddItemName] = useState(""); - - function updateDoc(newDoc) { - setDoc(newDoc); - } const { sendMessage, @@ -52,10 +45,6 @@ export default function Room({ roomId }) { setConnectedClients(data.clients.map((x) => uuid.stringify(x))); } - if (data.type === "ChatMessage") { - setChats([...chats, data]); - } - if (data.type === "Automerge") { const [nextDoc, nextSyncState, patch] = Automerge.receiveSyncMessage( doc, @@ -81,7 +70,6 @@ export default function Room({ roomId }) { } useEffect(() => { - console.log("hellosu", readyState, newReadyState); if ( readyState === ReadyState.CONNECTING && newReadyState === ReadyState.OPEN @@ -93,88 +81,48 @@ export default function Room({ roomId }) { setReadyState(newReadyState); }, [newReadyState]); - function onSubmit(e) { - e.preventDefault(); - sendWtfMessage({ - type: "ChatMessage", - timestamp: new Date().toISOString(), - message_id: uuid.v4(), - room_id: roomId, - author: clientId, - content: message, - }); - setMessage(""); - } - - function addItem(e) { - e.preventDefault(); - const newDoc = Automerge.change(doc, (doc) => { - if (!doc.items) doc.items = []; - doc.items.push({ - id: uuid.v4(), - content: addItemName, - }); - }); - updateDoc(newDoc); - const [syncMessage, binary] = Automerge.generateSyncMessage(doc, syncState); - if (syncMessage) - sendWtfMessage({ - type: "Automerge", - client_id: clientId, - room_id: roomId, - message: binary, - }); - setAddItemName(""); - } - - const items = doc.items || []; const connectionStatus = connectionStatusMap[readyState]; if (newReadyState !== ReadyState.OPEN) return <>Connecting...; return ( - <> - -
+

Room Id: {roomId}

-

Connection status: {connectionStatus}

+

+ Connection status: +

Connected:
    {connectedClients.map((x) => (
  • {x}
  • ))}
- Messages: -
    - {chats.map((x) => ( -
  • - [{x.timestamp}] {uuid.stringify(x.author)}: {x.content} -
  • - ))} -
-
- setMessage(e.target.value)} - placeholder="Type a message..." - /> -
- Grubs: -
    - {items.map((x) => ( -
  • {x.content}
  • - ))} -
-
- setAddItemName(e.target.value)} - placeholder="Type a message..." - /> - -
- + + + + + + + + + + + +
); } + +function ConnectionStatus({ readyState }) { + switch (readyState) { + case ReadyState.CONNECTING: + return ; + case ReadyState.OPEN: + return ; + default: + return null; + } +} diff --git a/client/Upload.tsx b/client/Upload.tsx index 134bebf..be52e21 100644 --- a/client/Upload.tsx +++ b/client/Upload.tsx @@ -1,14 +1,48 @@ -import { useCallback, useEffect, useState } from "react"; -import * as sodium from "libsodium-wrappers"; +import { useCallback, useContext, useEffect, useState } from "react"; +import * as sodium from "libsodium-wrappers-sumo"; import useWebSocket from "react-use-websocket"; import CBOR from "cbor"; import { wsUrl } from "./constants"; +import { RoomContext } from "./lib/roomContext"; +import { useDebouncedCallback } from "use-debounce"; +import { + Row, + Col, + Button, + Form, + Badge, + OverlayTrigger, + Tooltip, +} from "react-bootstrap"; -export default function Upload({ roomId }) { +export default function Upload() { + const { roomId } = useContext(RoomContext); const [selectedFile, setSelectedFile] = useState(null); const [encryptionKey, setEncryptionKey] = useState(null); + const [passphrase, setPassphrase] = useState(""); const [uploadProgress, setUploadProgress] = useState(null); + const setHashedPassphrase = useDebouncedCallback( + (passphrase) => { + if (passphrase) { + const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES); + console.log("salt", salt); + setEncryptionKey( + sodium.crypto_pwhash( + sodium.crypto_box_SEEDBYTES, + passphrase, + salt, + sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, + sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE, + sodium.crypto_pwhash_ALG_ARGON2ID13 + ) + ); + } + }, + // delay in ms + 1000 + ); + const [decryptionKey, setDecryptionKey] = useState(""); const [selectedSaveFile, setSelectedSaveFile] = useState(null); const [decryptState, setDecryptState] = useState(null); @@ -22,11 +56,7 @@ export default function Upload({ roomId }) { const readyToDownload = !!decryptionKeyInternal && !!selectedSaveFile && !!writeStream; - const { - sendMessage, - lastJsonMessage, - readyState: newReadyState, - } = useWebSocket(wsUrl, { + const { sendMessage } = useWebSocket(wsUrl, { share: true, onMessage: async (event) => { const message = CBOR.decode(await event.data.arrayBuffer()); @@ -69,10 +99,14 @@ export default function Upload({ roomId }) { useEffect(() => { (async () => { await sodium.ready; - const key = sodium.crypto_secretstream_xchacha20poly1305_keygen(); - setEncryptionKey(key); + if (passphrase) { + setHashedPassphrase(passphrase); + } else { + const key = sodium.crypto_secretstream_xchacha20poly1305_keygen(); + setEncryptionKey(key); + } })(); - }, []); + }, [passphrase]); const uploadFile = useCallback(async () => { if (!encryptionKey) return; @@ -128,43 +162,102 @@ export default function Upload({ roomId }) { } return ( -
-

Send file

+ <> +

How does this work?

+
    +
  1. + Uploader: Copy the key and send it to whoever you're sending + files to +
  2. +
  3. + Receiver: Paste the key into the "Paste key here" box +
  4. +
  5. + Receiver: Press the "select save file" button to choose where + to save the file to +
  6. +
  7. + Receiver: Tell the uploader to press "upload" +
  8. +
  9. + Uploader: Press "upload" +
  10. +
+

Only one person can upload at a time.

-

Upload Key: {encryptionKey && bytes2Hex(encryptionKey)}

+ + +

Send file

- setSelectedFile(e.target.files[0])} /> - - -
- -

Receive file

- -

-

    -
  1. - setDecryptionKey(e.target.value)} - placeholder="Download Key..." + {/*

    + Upload passphrase: + { + setEncryptionKey(null); + setPassphrase(e.target.value); + }} /> -

  2. -
  3. - -
  4. -
- ( - {readyToDownload - ? "you are ready to download" - : "you are not ready to download, make sure you enter the key and select a place to save"} - ) -

-
+

*/} + +

+ Upload Key:{" "} + +

+ + setSelectedFile(e.target.files[0])} + /> + + + + +

+ Receive file + {readyToDownload ? ( + ready + ) : ( + + you are not ready to download, make sure you enter the key + and select a place to save + + } + > + not ready + + )} +

+ +
    +
  1. + setDecryptionKey(e.target.value)} + placeholder="Paste key here..." + /> +
  2. +
  3. + +
  4. +
+ + + ); } diff --git a/client/global.css b/client/global.css new file mode 100644 index 0000000..38498f7 --- /dev/null +++ b/client/global.css @@ -0,0 +1,4 @@ +.container { + max-width: 980px; + margin: auto; +} diff --git a/client/lib/roomContext.ts b/client/lib/roomContext.ts new file mode 100644 index 0000000..bc80673 --- /dev/null +++ b/client/lib/roomContext.ts @@ -0,0 +1,8 @@ +import { createContext } from "react"; + +interface RoomContextProps { + roomId: string; + clientId: string; +} + +export const RoomContext = createContext(undefined); diff --git a/package-lock.json b/package-lock.json index 06a831b..3dd1cfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,29 @@ { "name": "grub", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "grub", + "version": "0.1.0", "dependencies": { "@automerge/automerge": "^2.0.3", + "bootstrap": "^5.3.1", "cbor": "npm:@jprochazk/cbor@^0.5.0", "libsodium": "^0.7.11", "libsodium-wrappers": "^0.7.11", + "libsodium-wrappers-sumo": "^0.7.11", "localforage": "^1.10.0", "match-sorter": "^6.3.1", "react": "^18.2.0", + "react-bootstrap": "^2.8.0", + "react-bootstrap-icons": "^1.10.3", "react-dom": "^18.2.0", "react-router-dom": "^6.15.0", "react-use-websocket": "^4.3.1", "sort-by": "^1.2.0", + "use-debounce": "^9.0.4", "uuid": "^9.0.0" }, "devDependencies": { @@ -24,6 +32,7 @@ "@types/react-dom": "^18.2.7", "@types/uuid": "^9.0.2", "@vitejs/plugin-react": "^4.0.4", + "sass": "^1.65.1", "typescript": "^5.1.6", "vite": "^4.4.9", "vite-plugin-top-level-await": "^1.3.1", @@ -795,6 +804,29 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.1.tgz", + "integrity": "sha512-ovVPSD1WlRpZHt7GI9DqJrWG3OIYS+NXQ9y5HIewMJpSe+jPQmMQfyRmgX4EnvmxSlp0u04Wg/7oItcoSIb/RA==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@remix-run/router": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", @@ -803,6 +835,45 @@ "node": ">=14.0.0" } }, + "node_modules/@restart/hooks": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz", + "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/plugin-virtual": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.1.tgz", @@ -1014,6 +1085,14 @@ "node": ">=10" } }, + "node_modules/@swc/helpers": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/libsodium-wrappers": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", @@ -1023,14 +1102,12 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "18.2.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.20.tgz", "integrity": "sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1046,11 +1123,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/uuid": { "version": "9.0.2", @@ -1058,6 +1142,11 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, + "node_modules/@types/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", + "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" + }, "node_modules/@vitejs/plugin-react": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", @@ -1088,6 +1177,58 @@ "node": ">=4" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bootstrap": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.1.tgz", + "integrity": "sha512-jzwza3Yagduci2x0rr9MeFSORjcHpt0lRZukZPZQJT1Dth5qzV7XcgGqYzi39KGAVYR8QEDVoO0ubFKOxzMG+g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.21.10", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", @@ -1160,6 +1301,38 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1184,8 +1357,7 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -1204,6 +1376,23 @@ } } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.490", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz", @@ -1265,6 +1454,18 @@ "node": ">=0.8.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -1288,6 +1489,18 @@ "node": ">=6.9.0" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -1311,6 +1524,62 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, + "node_modules/immutable": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", + "integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", + "dev": true + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1345,6 +1614,11 @@ "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==" }, + "node_modules/libsodium-sumo": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.11.tgz", + "integrity": "sha512-bY+7ph7xpk51Ez2GbE10lXAQ5sJma6NghcIDaSPbM/G9elfrjLa0COHl/7P6Wb/JizQzl5UQontOOP1z0VwbLA==" + }, "node_modules/libsodium-wrappers": { "version": "0.7.11", "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", @@ -1353,6 +1627,14 @@ "libsodium": "^0.7.11" } }, + "node_modules/libsodium-wrappers-sumo": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.11.tgz", + "integrity": "sha512-DGypHOmJbB1nZn89KIfGOAkDgfv5N6SBGC3Qvmy/On0P0WD1JQvNRS/e3UL3aFF+xC0m+MYz5M+MnRnK2HMrKQ==", + "dependencies": { + "libsodium-sumo": "^0.7.11" + } + }, "node_modules/lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", @@ -1428,6 +1710,23 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-path": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.6.0.tgz", @@ -1442,6 +1741,18 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.27", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", @@ -1470,6 +1781,38 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -1481,6 +1824,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz", + "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.3", + "@types/react-transition-group": "^4.4.5", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-bootstrap-icons": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.10.3.tgz", + "integrity": "sha512-j4hSby6gT9/enhl3ybB1tfr1slZNAYXDVntcRrmVjxB3//2WwqrzpESVqKhyayYVaWpEtnwf9wgUQ03cuziwrw==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.8.6" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -1493,6 +1876,11 @@ "react": "^18.2.0" } }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -1532,6 +1920,21 @@ "react-dom": ">=16.8" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/react-use-websocket": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.3.1.tgz", @@ -1541,6 +1944,18 @@ "react-dom": ">= 18.0.0" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", @@ -1567,6 +1982,23 @@ "fsevents": "~2.3.2" } }, + "node_modules/sass": { + "version": "1.65.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.65.1.tgz", + "integrity": "sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -1622,6 +2054,23 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -1635,6 +2084,20 @@ "node": ">=14.17" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -1665,6 +2128,17 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-debounce": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.4.tgz", + "integrity": "sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==", + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", @@ -1751,6 +2225,14 @@ "vite": "^2 || ^3 || ^4" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 361332f..071609c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@types/react-dom": "^18.2.7", "@types/uuid": "^9.0.2", "@vitejs/plugin-react": "^4.0.4", + "sass": "^1.65.1", "typescript": "^5.1.6", "vite": "^4.4.9", "vite-plugin-top-level-await": "^1.3.1", @@ -18,16 +19,21 @@ }, "dependencies": { "@automerge/automerge": "^2.0.3", + "bootstrap": "^5.3.1", "cbor": "npm:@jprochazk/cbor@^0.5.0", "libsodium": "^0.7.11", "libsodium-wrappers": "^0.7.11", + "libsodium-wrappers-sumo": "^0.7.11", "localforage": "^1.10.0", "match-sorter": "^6.3.1", "react": "^18.2.0", + "react-bootstrap": "^2.8.0", + "react-bootstrap-icons": "^1.10.3", "react-dom": "^18.2.0", "react-router-dom": "^6.15.0", "react-use-websocket": "^4.3.1", "sort-by": "^1.2.0", + "use-debounce": "^9.0.4", "uuid": "^9.0.0" } } diff --git a/src/main.rs b/src/main.rs index 93435b6..3e68062 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,7 +36,7 @@ use uuid::Uuid; #[derive(Debug, Parser)] struct Opt { - #[clap(long, default_value = "3000")] + #[clap(long, default_value = "6106")] port: u16, } diff --git a/src/message.rs b/src/message.rs index de33cf0..ee2d134 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,5 +1,5 @@ use std::collections::HashSet; -use std::fmt::{Formatter}; +use std::fmt::Formatter; use std::io::Cursor; use axum::extract::ws; diff --git a/tsconfig.json b/tsconfig.json index a48cfc1..5645cda 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { "lib": ["ESNext", "DOM"], - "target": "ESNext", "jsx": "react-jsx" } } diff --git a/vite.config.ts b/vite.config.ts index 4d7ba15..2ab38ee 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ server: { proxy: { "/ws": { - target: "ws://localhost:3000/ws", + target: "ws://localhost:6106/ws", changeOrigin: true, rewrite: (path) => path.replace(/^\/ws/, ""), ws: true,