From 5098c7b90556d326556314e90c691c7dafc954ab Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Thu, 3 Nov 2022 14:58:34 -0700 Subject: [PATCH 1/2] rename all js to ts --- api/package.json | 1 - api/src/index.js | 4 - api/src/test_kernel.js | 7 -- ui/src/App.tsx | 2 +- .../components/{repo/graph.js => Canvas.tsx} | 10 +-- .../{CodeSlate.js => CodeSlate.tsx} | 0 ui/src/components/{Header.js => Header.tsx} | 0 .../components/{MyMonaco.js => MyMonaco.tsx} | 0 ui/src/components/{MySlate.js => MySlate.tsx} | 0 ui/src/components/{MyXTerm.js => MyXTerm.tsx} | 0 .../{repo/sidebar.js => Sidebar.tsx} | 6 +- ui/src/components/repo/edges.js | 10 --- ui/src/components/repo/nodes.js | 87 ------------------- .../{slate_helper.js => slate_helper.tsx} | 0 ui/src/lib/{auth.js => auth.tsx} | 14 ++- ui/src/lib/{fetch.js => fetch.tsx} | 0 ui/src/lib/{me.js => me.tsx} | 0 .../repo/prompt.js => lib/prompt.tsx} | 0 ui/src/lib/{runtime.js => runtime.tsx} | 0 ui/src/lib/{store.js => store.tsx} | 28 +++++- ui/src/lib/{utils.js => utils.tsx} | 0 ui/src/pages/{about.js => about.tsx} | 0 ui/src/pages/{docs.js => docs.tsx} | 0 ui/src/pages/{index.js => index.tsx} | 0 ui/src/pages/{login.js => login.tsx} | 0 ui/src/pages/{profile.js => profile.tsx} | 0 ui/src/pages/{repo.js => repo.tsx} | 4 +- ui/src/pages/{repos.js => repos.tsx} | 0 ui/src/pages/{signup.js => signup.tsx} | 0 ui/src/pages/{test.js => test.tsx} | 0 ui/tsconfig.json | 3 +- 31 files changed, 45 insertions(+), 131 deletions(-) delete mode 100644 api/src/index.js delete mode 100644 api/src/test_kernel.js rename ui/src/components/{repo/graph.js => Canvas.tsx} (99%) rename ui/src/components/{CodeSlate.js => CodeSlate.tsx} (100%) rename ui/src/components/{Header.js => Header.tsx} (100%) rename ui/src/components/{MyMonaco.js => MyMonaco.tsx} (100%) rename ui/src/components/{MySlate.js => MySlate.tsx} (100%) rename ui/src/components/{MyXTerm.js => MyXTerm.tsx} (100%) rename ui/src/components/{repo/sidebar.js => Sidebar.tsx} (98%) delete mode 100644 ui/src/components/repo/edges.js delete mode 100644 ui/src/components/repo/nodes.js rename ui/src/components/{slate_helper.js => slate_helper.tsx} (100%) rename ui/src/lib/{auth.js => auth.tsx} (91%) rename ui/src/lib/{fetch.js => fetch.tsx} (100%) rename ui/src/lib/{me.js => me.tsx} (100%) rename ui/src/{components/repo/prompt.js => lib/prompt.tsx} (100%) rename ui/src/lib/{runtime.js => runtime.tsx} (100%) rename ui/src/lib/{store.js => store.tsx} (95%) rename ui/src/lib/{utils.js => utils.tsx} (100%) rename ui/src/pages/{about.js => about.tsx} (100%) rename ui/src/pages/{docs.js => docs.tsx} (100%) rename ui/src/pages/{index.js => index.tsx} (100%) rename ui/src/pages/{login.js => login.tsx} (100%) rename ui/src/pages/{profile.js => profile.tsx} (100%) rename ui/src/pages/{repo.js => repo.tsx} (97%) rename ui/src/pages/{repos.js => repos.tsx} (100%) rename ui/src/pages/{signup.js => signup.tsx} (100%) rename ui/src/pages/{test.js => test.tsx} (100%) diff --git a/api/package.json b/api/package.json index cae9c752..791277e5 100644 --- a/api/package.json +++ b/api/package.json @@ -1,7 +1,6 @@ { "name": "api", "version": "1.0.0", - "main": "index.js", "license": "MIT", "scripts": { "build": "tsc", diff --git a/api/src/index.js b/api/src/index.js deleted file mode 100644 index 49b5451c..00000000 --- a/api/src/index.js +++ /dev/null @@ -1,4 +0,0 @@ -// export * from "./socket.js"; -// export * from "./typedefs.js"; -// export * from "./resolver.js"; -export * from "./server.js"; diff --git a/api/src/test_kernel.js b/api/src/test_kernel.js deleted file mode 100644 index 50402bf8..00000000 --- a/api/src/test_kernel.js +++ /dev/null @@ -1,7 +0,0 @@ -import { startNativeKernel } from "./kernel.js"; - -function test() { - startNativeKernel("/Users/hebi/Library/Jupyter/kernels/racket/kernel.json"); -} - -test(); diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 4fba8441..12f46a18 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -11,7 +11,7 @@ import Login from "./pages/login"; import Signup from "./pages/signup"; import Profile from "./pages/profile"; -import { AuthProvider } from "./lib/auth.js"; +import { AuthProvider } from "./lib/auth"; import { Header, Footer } from "./components/Header"; import Box from "@mui/material/Box"; diff --git a/ui/src/components/repo/graph.js b/ui/src/components/Canvas.tsx similarity index 99% rename from ui/src/components/repo/graph.js rename to ui/src/components/Canvas.tsx index 04a66049..ea75e3d4 100644 --- a/ui/src/components/repo/graph.js +++ b/ui/src/components/Canvas.tsx @@ -15,7 +15,7 @@ import ReactFlow, { MiniMap, Controls, Handle, - useReactFlow + useReactFlow, } from "react-flow-renderer"; import Box from "@mui/material/Box"; import CircularProgress from "@mui/material/CircularProgress"; @@ -33,9 +33,9 @@ import { nolookalikes } from "nanoid-dictionary"; import { useStore } from "zustand"; -import { RepoContext } from "../../lib/store"; +import { RepoContext } from "../lib/store"; -import { MyMonaco } from "../MyMonaco"; +import { MyMonaco } from "./MyMonaco"; const nanoid = customAlphabet(nolookalikes, 10); @@ -52,7 +52,7 @@ const ScopeNode = memo(({ data, id, isConnectable, selected }) => { const { setNodes } = useReactFlow(); const onResize = useCallback( - ({width, height, offx, offy }) => { + ({ width, height, offx, offy }) => { setNodes((nds) => { return nds.map((node) => { if (node.id === id) { @@ -142,7 +142,7 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { const { setNodes } = useReactFlow(); const onResize = useCallback( - ({width, height, offx, offy }) => { + ({ width, height, offx, offy }) => { setNodes((nds) => { return nds.map((node) => { if (node.id === id) { diff --git a/ui/src/components/CodeSlate.js b/ui/src/components/CodeSlate.tsx similarity index 100% rename from ui/src/components/CodeSlate.js rename to ui/src/components/CodeSlate.tsx diff --git a/ui/src/components/Header.js b/ui/src/components/Header.tsx similarity index 100% rename from ui/src/components/Header.js rename to ui/src/components/Header.tsx diff --git a/ui/src/components/MyMonaco.js b/ui/src/components/MyMonaco.tsx similarity index 100% rename from ui/src/components/MyMonaco.js rename to ui/src/components/MyMonaco.tsx diff --git a/ui/src/components/MySlate.js b/ui/src/components/MySlate.tsx similarity index 100% rename from ui/src/components/MySlate.js rename to ui/src/components/MySlate.tsx diff --git a/ui/src/components/MyXTerm.js b/ui/src/components/MyXTerm.tsx similarity index 100% rename from ui/src/components/MyXTerm.js rename to ui/src/components/MyXTerm.tsx diff --git a/ui/src/components/repo/sidebar.js b/ui/src/components/Sidebar.tsx similarity index 98% rename from ui/src/components/repo/sidebar.js rename to ui/src/components/Sidebar.tsx index 854a1e4d..f4a32fb9 100644 --- a/ui/src/components/repo/sidebar.js +++ b/ui/src/components/Sidebar.tsx @@ -19,11 +19,11 @@ import CloudUploadIcon from "@mui/icons-material/CloudUpload"; import { useStore } from "zustand"; -import { usePrompt } from "./prompt"; +import { usePrompt } from "../lib/prompt"; -import { RepoContext, selectNumDirty } from "../../lib/store"; +import { RepoContext, selectNumDirty } from "../lib/store"; -import useMe from "../../lib/me"; +import useMe from "../lib/me"; function Flex(props) { return ( diff --git a/ui/src/components/repo/edges.js b/ui/src/components/repo/edges.js deleted file mode 100644 index 12d4676a..00000000 --- a/ui/src/components/repo/edges.js +++ /dev/null @@ -1,10 +0,0 @@ -export default [ - { id: "e1-2", source: "1", target: "2", animated: true }, - { id: "e1-3", source: "1", target: "3" }, - { id: "e2a-4a", source: "2a", target: "4a" }, - { id: "e3-4", source: "3", target: "4" }, - { id: "e3-4b", source: "3", target: "4b" }, - { id: "e4a-4b1", source: "4a", target: "4b1" }, - { id: "e4a-4b2", source: "4a", target: "4b2" }, - { id: "e4b1-4b2", source: "4b1", target: "4b2" }, -]; diff --git a/ui/src/components/repo/nodes.js b/ui/src/components/repo/nodes.js deleted file mode 100644 index abc309ef..00000000 --- a/ui/src/components/repo/nodes.js +++ /dev/null @@ -1,87 +0,0 @@ -const nodes = [ - { - id: "1", - type: "input", - data: { label: "Node 0" }, - position: { x: 250, y: 5 }, - className: "light", - extent: "parent", - }, - { - id: "2", - type: "scope", - data: { label: "Group A" }, - position: { x: 100, y: 100 }, - className: "light", - style: { - backgroundColor: "rgba(240,240,240,0.25)", - width: 200, - height: 200, - }, - extent: "parent", - }, - { - id: "2a", - data: { label: "Node A.1" }, - position: { x: 10, y: 50 }, - parentNode: "2", - extent: "parent", - }, - { - id: "3", - data: { label: "Node 1" }, - position: { x: 320, y: 100 }, - className: "light", - extent: "parent", - }, - { - id: "4", - type: "scope", - data: { label: "Group B" }, - position: { x: 320, y: 200 }, - className: "light", - style: { backgroundColor: "rgba(255, 0, 0, 0.2)", width: 300, height: 300 }, - extent: "parent", - }, - - { - id: "4b", - type: "scope", - data: { label: "Group B.A" }, - position: { x: 15, y: 120 }, - className: "light", - style: { - backgroundColor: "rgba(255, 0, 255, 0.2)", - height: 150, - width: 270, - }, - parentNode: "4", - extent: "parent", - }, - { - id: "4a", - data: { label: "Node B.1" }, - position: { x: 15, y: 65 }, - className: "light", - parentNode: "4", - extent: "parent", - }, - { - id: "4b1", - data: { label: "Node B.A.1" }, - position: { x: 20, y: 40 }, - className: "light", - parentNode: "4b", - extent: "parent", - }, - { - id: "4b2", - data: { label: "Node B.A.2" }, - position: { x: 100, y: 100 }, - className: "light", - parentNode: "4b", - extent: "parent", - }, -]; - -export default nodes; diff --git a/ui/src/components/slate_helper.js b/ui/src/components/slate_helper.tsx similarity index 100% rename from ui/src/components/slate_helper.js rename to ui/src/components/slate_helper.tsx diff --git a/ui/src/lib/auth.js b/ui/src/lib/auth.tsx similarity index 91% rename from ui/src/lib/auth.js rename to ui/src/lib/auth.tsx index 2f2075c8..7ff35380 100644 --- a/ui/src/lib/auth.js +++ b/ui/src/lib/auth.tsx @@ -12,7 +12,9 @@ import { nolookalikes } from "nanoid-dictionary"; const nanoid = customAlphabet(nolookalikes, 10); -const authContext = createContext(); +type AuthContextType = ReturnType; + +const authContext = createContext(null); export function AuthProvider({ children }) { const auth = useProvideAuth(); @@ -27,11 +29,11 @@ export function AuthProvider({ children }) { } export const useAuth = () => { - return useContext(authContext); + return useContext(authContext)!; }; function useProvideAuth() { - const [authToken, setAuthToken] = useState(null); + const [authToken, setAuthToken] = useState(null); useEffect(() => { // load initial state from local storage @@ -122,11 +124,7 @@ function useProvideAuth() { }); if (result.errors) { - throw Error( - result.errors[0].message + - "\n" + - result.errors[0].extensions.exception.stacktrace.join("\n") - ); + throw Error(result.errors[0].message); } if (result?.data?.signup?.token) { diff --git a/ui/src/lib/fetch.js b/ui/src/lib/fetch.tsx similarity index 100% rename from ui/src/lib/fetch.js rename to ui/src/lib/fetch.tsx diff --git a/ui/src/lib/me.js b/ui/src/lib/me.tsx similarity index 100% rename from ui/src/lib/me.js rename to ui/src/lib/me.tsx diff --git a/ui/src/components/repo/prompt.js b/ui/src/lib/prompt.tsx similarity index 100% rename from ui/src/components/repo/prompt.js rename to ui/src/lib/prompt.tsx diff --git a/ui/src/lib/runtime.js b/ui/src/lib/runtime.tsx similarity index 100% rename from ui/src/lib/runtime.js rename to ui/src/lib/runtime.tsx diff --git a/ui/src/lib/store.js b/ui/src/lib/store.tsx similarity index 95% rename from ui/src/lib/store.js rename to ui/src/lib/store.tsx index e7577293..9c759a31 100644 --- a/ui/src/lib/store.js +++ b/ui/src/lib/store.tsx @@ -67,6 +67,30 @@ const initialState = { socketIntervalId: null, }; +type Pod = { + id: string; + name: string; + type: string; + content: string; + remoteHash: string; + dirty: boolean; + children: string[]; + parent: string; + result: string; + status: string; + stdout: string; + stderr: string; + error: string | null; + lang: string; + runtime: string; + runtimeStatus: string; +}; + +interface BearState { + pods: Record; + id2parent: Record; +} + const createRepoSlice = (set, get) => ({ ...initialState, // FIXME should reset to inital state, not completely empty. @@ -99,7 +123,7 @@ const createRepoSlice = (set, get) => ({ } // update all other siblings' index // FIXME this might cause other pods to be re-rendered - const pod = { + const pod: Pod = { content: "", column: 1, result: "", @@ -403,7 +427,7 @@ const createRepoSlice = (set, get) => ({ }); export const createRepoStore = () => - createStore((...a) => ({ + createStore((...a) => ({ ...createRepoSlice(...a), ...createRuntimeSlice(...a), })); diff --git a/ui/src/lib/utils.js b/ui/src/lib/utils.tsx similarity index 100% rename from ui/src/lib/utils.js rename to ui/src/lib/utils.tsx diff --git a/ui/src/pages/about.js b/ui/src/pages/about.tsx similarity index 100% rename from ui/src/pages/about.js rename to ui/src/pages/about.tsx diff --git a/ui/src/pages/docs.js b/ui/src/pages/docs.tsx similarity index 100% rename from ui/src/pages/docs.js rename to ui/src/pages/docs.tsx diff --git a/ui/src/pages/index.js b/ui/src/pages/index.tsx similarity index 100% rename from ui/src/pages/index.js rename to ui/src/pages/index.tsx diff --git a/ui/src/pages/login.js b/ui/src/pages/login.tsx similarity index 100% rename from ui/src/pages/login.js rename to ui/src/pages/login.tsx diff --git a/ui/src/pages/profile.js b/ui/src/pages/profile.tsx similarity index 100% rename from ui/src/pages/profile.js rename to ui/src/pages/profile.tsx diff --git a/ui/src/pages/repo.js b/ui/src/pages/repo.tsx similarity index 97% rename from ui/src/pages/repo.js rename to ui/src/pages/repo.tsx index 37684c71..db9fcd76 100644 --- a/ui/src/pages/repo.js +++ b/ui/src/pages/repo.tsx @@ -11,8 +11,8 @@ import { createRepoStore, RepoContext } from "../lib/store"; import useMe from "../lib/me"; // import { Deck } from "../components/repo/pod"; -import { Deck } from "../components/repo/graph"; -import { Sidebar } from "../components/repo/sidebar"; +import { Deck } from "../components/Canvas"; +import { Sidebar } from "../components/Sidebar"; function RepoWrapper({ children }) { // this component is used to provide foldable sidebar diff --git a/ui/src/pages/repos.js b/ui/src/pages/repos.tsx similarity index 100% rename from ui/src/pages/repos.js rename to ui/src/pages/repos.tsx diff --git a/ui/src/pages/signup.js b/ui/src/pages/signup.tsx similarity index 100% rename from ui/src/pages/signup.js rename to ui/src/pages/signup.tsx diff --git a/ui/src/pages/test.js b/ui/src/pages/test.tsx similarity index 100% rename from ui/src/pages/test.js rename to ui/src/pages/test.tsx diff --git a/ui/tsconfig.json b/ui/tsconfig.json index a273b0cf..374b19dc 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -18,7 +18,8 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "noImplicitAny": false, }, "include": [ "src" From 8ed2022f090139645d40f2b150985055dfca7bef Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Fri, 4 Nov 2022 03:05:02 -0700 Subject: [PATCH 2/2] fix TS compile errors; fix login/signout popover; fix and enable auto-save; use new MUI login page --- api/src/spawner-docker.ts | 2 +- ui/package.json | 2 + ui/src/components/Canvas.tsx | 78 +++-- ui/src/components/CodeSlate.tsx | 493 ----------------------------- ui/src/components/Header.tsx | 62 ++-- ui/src/components/MyMonaco.tsx | 107 +------ ui/src/components/MySlate.tsx | 209 ------------ ui/src/components/Sidebar.tsx | 67 ++-- ui/src/components/slate_helper.tsx | 144 --------- ui/src/lib/fetch.tsx | 24 +- ui/src/lib/runtime.tsx | 294 +++++------------ ui/src/lib/store.tsx | 266 ++++++++++++++-- ui/src/lib/utils.tsx | 2 +- ui/src/pages/login.tsx | 386 ++++++++-------------- ui/src/pages/repo.tsx | 13 +- ui/src/pages/repos.tsx | 16 +- ui/src/pages/test.tsx | 1 - ui/yarn.lock | 10 + 18 files changed, 587 insertions(+), 1589 deletions(-) delete mode 100644 ui/src/components/CodeSlate.tsx delete mode 100644 ui/src/components/MySlate.tsx delete mode 100644 ui/src/components/slate_helper.tsx diff --git a/api/src/spawner-docker.ts b/api/src/spawner-docker.ts index 161b2d07..27b1728d 100644 --- a/api/src/spawner-docker.ts +++ b/api/src/spawner-docker.ts @@ -69,7 +69,7 @@ export async function loadOrCreateContainer( image, name, network, - Env: String[] = [] + Env: string[] = [] ) { console.log("loading container", name); let ip = await loadContainer(name, network); diff --git a/ui/package.json b/ui/package.json index f4f2a8e6..cf8e1d89 100644 --- a/ui/package.json +++ b/ui/package.json @@ -41,6 +41,8 @@ "stompjs": "^2.3.3", "typescript": "^4.4.2", "web-vitals": "^2.1.0", + "xterm": "^5.0.0", + "xterm-addon-fit": "^0.6.0", "zustand": "^4.1.3" }, "scripts": { diff --git a/ui/src/components/Canvas.tsx b/ui/src/components/Canvas.tsx index ea75e3d4..8bd07e17 100644 --- a/ui/src/components/Canvas.tsx +++ b/ui/src/components/Canvas.tsx @@ -16,6 +16,7 @@ import ReactFlow, { Controls, Handle, useReactFlow, + Position, } from "react-flow-renderer"; import Box from "@mui/material/Box"; import CircularProgress from "@mui/material/CircularProgress"; @@ -39,13 +40,20 @@ import { MyMonaco } from "./MyMonaco"; const nanoid = customAlphabet(nolookalikes, 10); -const ScopeNode = memo(({ data, id, isConnectable, selected }) => { +interface Props { + data: any; + id: string; + isConnectable: boolean; + selected: boolean; +} + +const ScopeNode = memo(({ data, id, isConnectable, selected }) => { // add resize to the node const ref = useRef(null); const store = useContext(RepoContext); if (!store) throw new Error("Missing BearContext.Provider in the tree"); const updatePod = useStore(store, (state) => state.updatePod); - const [target, setTarget] = React.useState(); + const [target, setTarget] = React.useState(); const [frame] = React.useState({ translate: [0, 0], }); @@ -81,9 +89,17 @@ const ScopeNode = memo(({ data, id, isConnectable, selected }) => { }} className="custom-drag-handle" > - + Scope: {data?.label} - + {selected && ( { e.target.style.height = `${e.height}px`; e.target.style.transform = `translate(${beforeTranslate[0]}px, ${beforeTranslate[1]}px)`; onResize({ - id, width: e.width, height: e.height, offx: beforeTranslate[0], @@ -126,7 +141,7 @@ const ScopeNode = memo(({ data, id, isConnectable, selected }) => { ); }); -const CodeNode = memo(({ data, id, isConnectable, selected }) => { +const CodeNode = memo(({ data, id, isConnectable, selected }) => { const store = useContext(RepoContext); if (!store) throw new Error("Missing BearContext.Provider in the tree"); const pod = useStore(store, (state) => state.pods[id]); @@ -135,7 +150,7 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { const clearResults = useStore(store, (s) => s.clearResults); const wsRun = useStore(store, (state) => state.wsRun); const ref = useRef(null); - const [target, setTarget] = React.useState(); + const [target, setTarget] = React.useState(null); const [frame] = React.useState({ translate: [0, 0], }); @@ -171,7 +186,11 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { }} ref={ref} > - + Code: {data?.label} { > { setPodContent({ id: pod.id, content: value }); @@ -244,8 +262,7 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { {pod.running && } {pod.result && ( @@ -276,7 +293,7 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { )} {pod.error && ( - + Error: {pod.error.evalue} {pod.error.stacktrace && ( @@ -291,7 +308,11 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => { - + {selected && ( ([]); + const [edges, setEdges] = useState([]); const store = useContext(RepoContext); if (!store) throw new Error("Missing BearContext.Provider in the tree"); @@ -357,7 +378,7 @@ export function Deck({ props }) { const getRealNodes = useCallback( (id, level) => { - let res = []; + let res: any[] = []; let children = id2children[id]; if (id !== "ROOT") { res.push({ @@ -411,13 +432,13 @@ export function Deck({ props }) { [setEdges] ); - const [reactFlowInstance, setReactFlowInstance] = useState(null); - const reactFlowWrapper = useRef(null); + const [reactFlowInstance, setReactFlowInstance] = useState(null); + const reactFlowWrapper = useRef(null); const addPod = useStore(store, (state) => state.addPod); const setPodPosition = useStore(store, (state) => state.setPodPosition); const setPodParent = useStore(store, (state) => state.setPodParent); - const remoteDelete = useStore(store, (state) => state.remoteDelete); + const deletePod = useStore(store, (state) => state.deletePod); const addNode = useCallback( (x, y, type) => { @@ -478,11 +499,15 @@ export function Deck({ props }) { (node) => { let x = node.position.x; let y = node.position.y; - let parent = pods[node.parentNode]; + let parent = pods[node.parent]; while (parent) { x += parent.x; y += parent.y; - parent = pods[parent.parentNode]; + if (parent.parent) { + parent = pods[parent.parent]; + } else { + break; + } } return [x, y]; }, @@ -491,7 +516,8 @@ export function Deck({ props }) { const getScopeAt = useCallback( (x, y, id) => { - const scope = nodes.findLast((node) => { + // FIXME should be fineLast, but findLast cannot pass TS compiler. + const scope = nodes.find((node) => { let [x1, y1] = getAbsPos(node); return ( node.type === "scope" && @@ -627,10 +653,10 @@ export function Deck({ props }) { (nodes) => { // remove from pods for (const node of nodes) { - remoteDelete({ id: node.id }); + deletePod({ id: node.id, toDelete: [] }); } }, - [remoteDelete] + [deletePod] ); const [showContextMenu, setShowContextMenu] = useState(false); @@ -679,7 +705,7 @@ export function Deck({ props }) { { - if (n.style?.background) return n.style.background; + if (n.style?.background) return n.style.background as string; if (n.type === "code") return "#0041d0"; if (n.type === "scope") return "#ff0072"; diff --git a/ui/src/components/CodeSlate.tsx b/ui/src/components/CodeSlate.tsx deleted file mode 100644 index 2abeb770..00000000 --- a/ui/src/components/CodeSlate.tsx +++ /dev/null @@ -1,493 +0,0 @@ -import Prism from "prismjs"; -import "prismjs/components/prism-python"; -import "prismjs/components/prism-php"; -import "prismjs/components/prism-sql"; -import "prismjs/components/prism-java"; -import "prismjs/components/prism-julia"; -import "prismjs/components/prism-scheme"; -import "prismjs/components/prism-racket"; -import "prismjs/components/prism-typescript"; - -import React, { - useState, - useCallback, - useMemo, - useEffect, - useRef, -} from "react"; - -import { Slate, Editable, ReactEditor, withReact, useSlate } from "slate-react"; -import { Text, createEditor, Editor, Transforms, Range } from "slate"; -import { withHistory } from "slate-history"; -import { css } from "@emotion/css"; -import { - FaBold, - FaItalic, - FaUnderline, - FaStrikethrough, - FaExternalLinkSquareAlt, - FaExternalLinkAlt, - FaArrowsAltV, -} from "react-icons/fa"; -import { MdImportExport, MdSwapVert, MdCallMissed } from "react-icons/md"; - -import Box from "@mui/material/Box"; - -import { Button, Menu, Portal } from "./slate_helper"; - -const toggleFormat = (editor, format) => { - const isActive = isFormatActive(editor, format); - Transforms.setNodes( - editor, - { [format]: isActive ? null : true }, - { match: Text.isText, split: true } - ); -}; - -const isFormatActive = (editor, format) => { - const [match] = Editor.nodes(editor, { - match: (n) => n[format] === true, - mode: "all", - }); - return !!match; -}; - -const RichLeaf = ({ attributes, children, leaf }) => { - if (leaf.bold) { - children = {children}; - } - - if (leaf.italic) { - children = {children}; - } - - if (leaf.underlined) { - children = {children}; - } - - if (leaf.strikethrough) { - children = {children}; - } - - if (leaf.export) { - // yellow background - children = ( - - {children} - - ); - } - - if (leaf.midport) { - children = ( - - {children} - - ); - } - - return { attributes, children, leaf }; -}; - -const HoveringToolbar = ({ onExport = () => {}, onMidport = () => {} }) => { - const ref = useRef(); - const editor = useSlate(); - - useEffect(() => { - const el = ref.current; - const { selection } = editor; - - if (!el) { - return; - } - - if ( - !selection || - !ReactEditor.isFocused(editor) || - Range.isCollapsed(selection) || - Editor.string(editor, selection) === "" - ) { - el.removeAttribute("style"); - return; - } - - const domSelection = window.getSelection(); - const domRange = domSelection.getRangeAt(0); - const rect = domRange.getBoundingClientRect(); - el.style.opacity = "1"; - el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`; - el.style.left = `${ - rect.left + window.pageXOffset - el.offsetWidth / 2 + rect.width / 2 - }px`; - }); - - return ( - - - } /> - } /> - } - onMouseDown={() => { - const isActive = isFormatActive(editor, "export"); - // what if partially active? - let name = Editor.string(editor, editor.selection); - onExport(name, isActive); - }} - /> - } - onMouseDown={() => { - const isActive = isFormatActive(editor, "midport"); - // what if partially active? - let name = Editor.string(editor, editor.selection); - onMidport(name, isActive); - }} - /> - } /> - } /> - - - ); -}; - -const FormatButton = ({ format, icon, onMouseDown = () => {} }) => { - const editor = useSlate(); - return ( - - ); -}; - -export function RichCodeSlate({ - value, - onChange, - language = "javascript", - onExport = () => {}, - onMidport = () => {}, - onRun = () => {}, -}) { - const renderLeaf = useCallback((props) => , []); - const editor = useMemo(() => withHistory(withReact(createEditor())), []); - // decorate function depends on the language selected - const decorate = useCallback( - ([node, path]) => { - const ranges = []; - // FIXME the highlighted text is not Text anymore, thus lose syntax highlighting - if (!Text.isText(node)) { - return ranges; - } - const tokens = Prism.tokenize(node.text, Prism.languages[language]); - let start = 0; - - for (const token of tokens) { - const length = getLength(token); - const end = start + length; - - if (typeof token !== "string") { - ranges.push({ - [token.type]: true, - anchor: { path, offset: start }, - focus: { path, offset: end }, - }); - } - - start = end; - } - - return ranges; - }, - [language] - ); - - return ( - - - { - if (event.shiftKey && event.key === "Enter") { - // console.log("Shift-enter!"); - event.preventDefault(); - // run code - onRun(); - } - }} - /> - - ); -} - -export function CodeSlate({ value, onChange, language = "javascript" }) { - const renderLeaf = useCallback((props) => , []); - const editor = useMemo(() => withHistory(withReact(createEditor())), []); - // decorate function depends on the language selected - const decorate = useCallback( - ([node, path]) => { - const ranges = []; - if (!Text.isText(node)) { - return ranges; - } - const tokens = Prism.tokenize(node.text, Prism.languages[language]); - let start = 0; - - for (const token of tokens) { - const length = getLength(token); - const end = start + length; - - if (typeof token !== "string") { - ranges.push({ - [token.type]: true, - anchor: { path, offset: start }, - focus: { path, offset: end }, - }); - } - - start = end; - } - - return ranges; - }, - [language] - ); - - return ( - - - - ); -} - -export const CodeHighlightingExample = () => { - const [value, setValue] = useState(initialValue); - const [language, setLanguage] = useState("html"); - - return ( -
-
-

- Select a language - -

-
- setValue(value)} - language={language} - /> -
- ); -}; - -const getLength = (token) => { - if (typeof token === "string") { - return token.length; - } else if (typeof token.content === "string") { - return token.content.length; - } else { - return token.content.reduce((l, t) => l + getLength(t), 0); - } -}; - -// different token types, styles found on Prismjs website -const Leaf = ({ attributes, children, leaf }) => { - return ( - - {children} - - ); -}; - -const initialValue = [ - { - type: "paragraph", - children: [ - { - text: "

Hi!

", - }, - ], - }, -]; - -// modifications and additions to prism library - -Prism.languages.python = Prism.languages.extend("python", {}); -Prism.languages.insertBefore("python", "prolog", { - comment: { pattern: /##[^\n]*/, alias: "comment" }, -}); -Prism.languages.javascript = Prism.languages.extend("javascript", {}); -Prism.languages.insertBefore("javascript", "prolog", { - comment: { pattern: /\/\/[^\n]*/, alias: "comment" }, -}); -Prism.languages.html = Prism.languages.extend("html", {}); -Prism.languages.insertBefore("html", "prolog", { - comment: { pattern: //, alias: "comment" }, -}); -Prism.languages.markdown = Prism.languages.extend("markup", {}); -Prism.languages.insertBefore("markdown", "prolog", { - blockquote: { pattern: /^>(?:[\t ]*>)*/m, alias: "punctuation" }, - code: [ - { pattern: /^(?: {4}|\t).+/m, alias: "keyword" }, - { pattern: /``.+?``|`[^`\n]+`/, alias: "keyword" }, - ], - title: [ - { - pattern: /\w+.*(?:\r?\n|\r)(?:==+|--+)/, - alias: "important", - inside: { punctuation: /==+$|--+$/ }, - }, - { - pattern: /(^\s*)#+.+/m, - lookbehind: !0, - alias: "important", - inside: { punctuation: /^#+|#+$/ }, - }, - ], - hr: { - pattern: /(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m, - lookbehind: !0, - alias: "punctuation", - }, - list: { - pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m, - lookbehind: !0, - alias: "punctuation", - }, - "url-reference": { - pattern: - /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/, - inside: { - variable: { pattern: /^(!?\[)[^\]]+/, lookbehind: !0 }, - string: /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/, - punctuation: /^[\[\]!:]|[<>]/, - }, - alias: "url", - }, - bold: { - pattern: /(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/, - lookbehind: !0, - inside: { punctuation: /^\*\*|^__|\*\*$|__$/ }, - }, - italic: { - pattern: /(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/, - lookbehind: !0, - inside: { punctuation: /^[*_]|[*_]$/ }, - }, - url: { - pattern: - /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/, - inside: { - variable: { pattern: /(!?\[)[^\]]+(?=\]$)/, lookbehind: !0 }, - string: { pattern: /"(?:\\.|[^"\\])*"(?=\)$)/ }, - }, - }, -}); -Prism.languages.markdown.bold.inside.url = Prism.util.clone( - Prism.languages.markdown.url -); -Prism.languages.markdown.italic.inside.url = Prism.util.clone( - Prism.languages.markdown.url -); -Prism.languages.markdown.bold.inside.italic = Prism.util.clone( - Prism.languages.markdown.italic -); -Prism.languages.markdown.italic.inside.bold = Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore - -export default CodeHighlightingExample; diff --git a/ui/src/components/Header.tsx b/ui/src/components/Header.tsx index 50aa3f30..ae0ca83a 100644 --- a/ui/src/components/Header.tsx +++ b/ui/src/components/Header.tsx @@ -10,6 +10,7 @@ import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import Avatar from "@mui/material/Avatar"; import Link from "@mui/material/Link"; +import Button from "@mui/material/Button"; import Toolbar from "@mui/material/Toolbar"; import IconButton from "@mui/material/IconButton"; @@ -21,6 +22,8 @@ import AppBar from "@mui/material/AppBar"; import { useAuth } from "../lib/auth"; +import useMe from "../lib/me"; + export function Header() { const [anchorElNav, setAnchorElNav] = useState(null); const [anchorElUser, setAnchorElUser] = useState(null); @@ -42,6 +45,7 @@ export function Header() { const { isSignedIn, signOut } = useAuth(); let navigate = useNavigate(); + const { me } = useMe(); return ( @@ -165,44 +169,22 @@ export function Header() {
{isSignedIn() ? ( - - - - - - - + {me?.firstname} + + Logout + ) : ( Login @@ -213,13 +195,9 @@ export function Header() { ); } -const MyMenuItem = ({ children, isLast, to = "/" }) => { +const MyMenuItem = ({ children, to = "/" }) => { return ( - + {children} diff --git a/ui/src/components/MyMonaco.tsx b/ui/src/components/MyMonaco.tsx index 61b9d18b..0ec54f1d 100644 --- a/ui/src/components/MyMonaco.tsx +++ b/ui/src/components/MyMonaco.tsx @@ -24,92 +24,6 @@ function construct_indent(pos, indent) { }, ]; } - -monaco.languages.registerOnTypeFormattingEditProvider("scheme", { - provideOnTypeFormattingEdits: function (model, position, ch, options, token) { - // get the first non-empty line - let line = ""; - let linum = position.lineNumber - 1; - while (line.trim().length === 0 && linum > 0) { - line = model.getLineContent(linum); - linum -= 1; - } - if (line.trim().length === 0) return []; - /* - - (aaa (bbb (ccc - xxx - yyy) - (xxx) - yyy (zzz xxx - iii)) - - - xxx) - xxx) - (define) - */ - let n_open = (line.match(/\(/g) || []).length; - let n_close = (line.match(/\)/g) || []).length; - if (n_open === n_close) { - // 1. If previous line has equal number of open and close, use that indentation - return construct_indent(position, line.length - line.trimLeft().length); - } else if (n_open > n_close) { - // 2. If previous line has more ), find the last )'s matching (, and - // - if there's "(aaa bbb", use bbb's indentation - // - else, use ('s indentation + 2 - let ct = 0; - for (let i = line.length - 1; i >= 0; i--) { - if (line[i] === ")") { - ct += 1; - } else if (line[i] === "(") { - ct -= 1; - } - if (ct === -1) { - // check the pattern - if (line.substring(i).match(/^\((define|lambda|let|for[^*]).*/)) { - return construct_indent(position, i + 2); - } - if (line.substring(i).match(/^\([([]/)) { - // this is (let ([xxx] - // [])) - return construct_indent(position, i + 1); - } - // trim right, and find " " - let match = line.substring(i).trimRight().match(/\s/); - if (match) { - // if it is (define (fdsf), I want to index 2 - return construct_indent(position, i + match.index + 1); - } else { - return construct_indent(position, i + 2); - } - } - } - } else { - // 3. If previous line has more ( - // - If there's "(aaa bbb", use bbb's indentation - // - else, use ('s indentation + 2 - let range = model.findPreviousMatch(")", position, false).range; - let pos = new Position(range.endLineNumber, range.endColumn); - let match = model.matchBracket(pos); - // this is actually a unmatched parenthesis - if (!match) return []; - let openPos = match[1]; - - let line2 = model.getLineContent(openPos.startLineNumber); - let match2 = line2 - .substring(0, openPos.startColumn) - .match(/\((define|lambda|let)\s*\($/); - if (match2) { - return construct_indent(position, match2.index + 2); - } else { - return construct_indent(position, openPos.startColumn - 1); - } - } - }, - autoFormatTriggerCharacters: ["\n"], -}); - function decide_indent_open(line) { // Assume line has more (. Decide the indent let ct = 0; @@ -149,7 +63,7 @@ function racket_format(model) { // - if n_open > n_close: from right, find the first unpaired open ( // - if n_open < n_close: find the last close, and find the match brackets let indent = 0; - let shifts = {}; + let shifts: { [key: number]: number } = {}; for (let linum = 1; linum <= model.getLineCount(); linum += 1) { let line = model.getLineContent(linum); if (line.trim().length === 0) { @@ -197,7 +111,7 @@ function racket_format(model) { } // console.log("shifts:", shifts); // console.log("computing edits .."); - let res = []; + let res: any[] = []; for (const [linum, shift] of Object.entries(shifts)) { let edit = { range: { @@ -263,7 +177,7 @@ export function MyMonacoDiff({ from, to }) { Math.max(one.getContentHeight(), two.getContentHeight()) ); // console.log("target height:", contentHeight); - const editorElement = editor.getDomNode(); + const editorElement = editor.getContainerDomNode(); if (!editorElement) { return; } @@ -281,7 +195,10 @@ export function MyMonacoDiff({ from, to }) { ); } -async function computeDiff(original, modified) { +async function computeDiff( + original, + modified +): Promise { return new Promise((resolve, reject) => { // 1. get a diff editor // 2. onDidUpdateDiff @@ -318,8 +235,8 @@ async function updateGitGutter(editor) { // console.log("original", gitvalue); // console.log("modified", value); // console.log("diffs:", diffs); - let decorations = []; - for (const diff of diffs) { + let decorations: any[] = []; + for (const diff of diffs || []) { // newly added lines if (diff.originalStartLineNumber > diff.originalEndLineNumber) { // newly added @@ -382,7 +299,7 @@ export function MyMonaco({ lang = "javascript", value = "", gitvalue = null, - onChange = () => {}, + onChange = (value) => {}, onRun = () => {}, }) { // console.log("rendering monaco .."); @@ -390,7 +307,7 @@ export function MyMonaco({ if (lang === "racket") { lang = "scheme"; } - let [editor, setEditor] = useState(null); + let [editor, setEditor] = useState(null); if (editor) { // console.log("mounting gitgutter updater"); editor.staged = gitvalue; @@ -447,7 +364,7 @@ export function MyMonaco({ editor.onDidContentSizeChange(updateHeight); // FIXME clean up? editor.addCommand( - [monaco.KeyMod.Shift | monaco.KeyCode.Enter], + monaco.KeyMod.Shift | monaco.KeyCode.Enter, function () { onRun(); } diff --git a/ui/src/components/MySlate.tsx b/ui/src/components/MySlate.tsx deleted file mode 100644 index 4384dbb2..00000000 --- a/ui/src/components/MySlate.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import { Editor, Transforms, Text, createEditor } from "slate"; -import { Range } from "slate"; - -import { Slate, Editable, ReactEditor, withReact, useSlate } from "slate-react"; -import { withHistory } from "slate-history"; -import React, { useState, useMemo, useEffect, useRef } from "react"; -import { Button, Menu, Portal } from "./slate_helper"; -import { css } from "@emotion/css"; -import { FaBold, FaItalic, FaUnderline, FaStrikethrough } from "react-icons/fa"; -import { Node } from "slate"; - -export function slackGetPlainText(nodes) { - if (!nodes) { - return ""; - } - return nodes.map((n) => Node.string(n)).join("\n"); -} - -const initialValue = [ - { - type: "paragraph", - children: [ - { - text: "This example shows how you can make a hovering menu appear above your content, which you can use to make text ", - }, - { text: "bold", bold: true }, - { text: ", " }, - { text: "italic", italic: true }, - { text: ", or anything else you might want to do!" }, - ], - }, - { - type: "paragraph", - children: [ - { text: "Try it out yourself! Just " }, - { text: "select any piece of text and the menu will appear", bold: true }, - { text: "." }, - ], - }, -]; - -export function MySlateExample({ content = initialValue }) { - const [value, setValue] = useState(content); - return setValue(value)} />; -} - -export function MySlate({ value, onChange }) { - const editor = useMemo(() => withHistory(withReact(createEditor())), []); - - return ( - - - } - placeholder="Enter some text..." - onDOMBeforeInput={(event) => { - // this will prevent default insertText - // event.preventDefault(); - // console.log(event.inputType); - switch (event.inputType) { - case "formatBold": - return toggleFormat(editor, "bold"); - case "formatItalic": - return toggleFormat(editor, "italic"); - case "formatUnderline": - return toggleFormat(editor, "underlined"); - default: - // insertText will throw error here - // throw new Error("Invalid inputType", event.inputType); - break; - } - }} - /> - - ); -} - -/* eslint-disable no-unused-vars */ -function MySlateSimple() { - const editor = useMemo(() => withReact(createEditor()), []); - // Add the initial value when setting up our state. - const [value, setValue] = useState([ - { - type: "paragraph", - children: [{ text: "A line of text in a paragraph." }], - }, - ]); - - return ( - setValue(newValue)} - > - - - ); -} - -const toggleFormat = (editor, format) => { - const isActive = isFormatActive(editor, format); - Transforms.setNodes( - editor, - { [format]: isActive ? null : true }, - { match: Text.isText, split: true } - ); -}; - -const isFormatActive = (editor, format) => { - const [match] = Editor.nodes(editor, { - match: (n) => n[format] === true, - mode: "all", - }); - return !!match; -}; - -const Leaf = ({ attributes, children, leaf }) => { - if (leaf.bold) { - children = {children}; - } - - if (leaf.italic) { - children = {children}; - } - - if (leaf.underlined) { - children = {children}; - } - - if (leaf.strikethrough) { - children = {children}; - } - - return {children}; -}; - -const HoveringToolbar = () => { - const ref = useRef(); - const editor = useSlate(); - - useEffect(() => { - const el = ref.current; - const { selection } = editor; - - if (!el) { - return; - } - - if ( - !selection || - !ReactEditor.isFocused(editor) || - Range.isCollapsed(selection) || - Editor.string(editor, selection) === "" - ) { - el.removeAttribute("style"); - return; - } - - const domSelection = window.getSelection(); - const domRange = domSelection.getRangeAt(0); - const rect = domRange.getBoundingClientRect(); - el.style.opacity = "1"; - el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`; - el.style.left = `${ - rect.left + window.pageXOffset - el.offsetWidth / 2 + rect.width / 2 - }px`; - }); - - return ( - - - } /> - } /> - } /> - } /> - - - ); -}; - -const FormatButton = ({ format, icon }) => { - const editor = useSlate(); - return ( - - ); -}; diff --git a/ui/src/components/Sidebar.tsx b/ui/src/components/Sidebar.tsx index f4a32fb9..1db20fef 100644 --- a/ui/src/components/Sidebar.tsx +++ b/ui/src/components/Sidebar.tsx @@ -9,7 +9,7 @@ import IconButton from "@mui/material/IconButton"; import { grey } from "@mui/material/colors"; -import { useSnackbar } from "notistack"; +import { useSnackbar, VariantType } from "notistack"; import { gql, useQuery, useMutation, useApolloClient } from "@apollo/client"; @@ -70,11 +70,11 @@ function SidebarRuntime() { Runtime connected?{" "} {runtimeConnected ? ( - + Yes ) : ( - + No )} @@ -115,15 +115,15 @@ function SidebarKernel() { {Object.entries(kernels).map(([lang, kernel]) => ( - + {lang} {" "} {runtimeConnected ? ( - + {kernel.status ? kernel.status : "Unknown"} ) : ( - + NA )} @@ -158,7 +158,6 @@ function ApplyAll() { const store = useContext(RepoContext); if (!store) throw new Error("Missing BearContext.Provider in the tree"); const numDirty = useStore(store, selectNumDirty()); - const wsRunAll = useStore(store, (s) => s.wsRunAll); const clearAllResults = useStore(store, (s) => s.clearAllResults); const remoteUpdateAllPods = useStore(store, (s) => s.remoteUpdateAllPods); usePrompt( @@ -166,19 +165,20 @@ function ApplyAll() { numDirty > 0 ); - let [intervalId, setIntervalId] = useState(null); + let [intervalId, setIntervalId] = + useState | null>(null); useEffect(() => { - if (intervalId) { - clearInterval(intervalId); - } - setIntervalId( - setInterval(() => { - // websocket resets after 60s of idle by most firewalls - // console.log("periodically saving .."); - // dispatch(remoteUpdateAllPods()); - }, 1000) - ); + console.log("Setting interval"); + let id = setInterval(() => { + // websocket resets after 60s of idle by most firewalls + console.log("periodically saving .."); + remoteUpdateAllPods(); + }, 1000); + return () => { + console.log("removing interval"); + clearInterval(id); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -193,32 +193,17 @@ function ApplyAll() { > {numDirty > 0 ? ( - + save {numDirty} to cloud ) : ( - + Saved to cloud. )} - - - {/* 0} - message="You have unsaved changes. Are you sure you want to leave?" - /> */} - - {error && {error}} - - - - )} - - ); -} - -///////// Password - -export const PasswordField = React.forwardRef((props, ref) => { - const [isOpen, setOpen] = useState(false); - const inputRef = React.useRef(null); - // FIXME alternatives? - // const mergeRef = useMergeRefs(inputRef, ref); - const mergeRef = inputRef; - - const onClickReveal = () => { - setOpen(!isOpen); - const input = inputRef.current; - - if (input) { - input.focus({ - preventScroll: true, - }); - const length = input.value.length * 2; - requestAnimationFrame(() => { - input.setSelectionRange(length, length); - }); - } - }; - - return ( - - - Password - - Forgot Password? - - - {/* - : } - onClick={onClickReveal} - /> - */} - - - ); -}); -PasswordField.displayName = "PasswordField"; -export default function Login() { - const { isSignedIn } = useAuth(); - const navigate = useNavigate(); + let navigate = useNavigate(); useEffect(() => { if (isSignedIn()) { navigate("/"); } }, [isSignedIn, navigate]); + + const formik = useFormik({ + initialValues: { + email: "", + password: "", + }, + // validationSchema: validationSchema, + onSubmit: (values) => { + setError(null); + return signIn({ + email: values.email, + password: values.password, + }).catch((err) => { + // TODO use more user friendly error message + setError(err.message); + }); + }, + }); + return ( - - - - Sign in to your account - - + + + - Don't have an account? - - Sign up for free - - - - - or continue with - + + + + Sign in + + - - - - - + + + } + label="Remember me" + /> + {error && {error}} + + + + + Forgot password? + + + + + {"Don't have an account? Sign Up"} + + {/* + {"Don't have an account? Sign Up"} + */} + - - - - - - - + + + + + ); } diff --git a/ui/src/pages/repo.tsx b/ui/src/pages/repo.tsx index db9fcd76..6de743af 100644 --- a/ui/src/pages/repo.tsx +++ b/ui/src/pages/repo.tsx @@ -10,8 +10,7 @@ import { useStore } from "zustand"; import { createRepoStore, RepoContext } from "../lib/store"; import useMe from "../lib/me"; -// import { Deck } from "../components/repo/pod"; -import { Deck } from "../components/Canvas"; +import { Canvas } from "../components/Canvas"; import { Sidebar } from "../components/Sidebar"; function RepoWrapper({ children }) { @@ -34,7 +33,7 @@ function RepoWrapper({ children }) { onClick={() => { setShow(!show); }} - size="xs" + size="small" // variant="ghost" > {show ? "Hide" : "Show"} @@ -66,7 +65,7 @@ function RepoWrapper({ children }) { onClick={() => { setShow(!show); }} - size="xs" + size="small" // variant="ghost" > {show ? "Hide" : "Show"} @@ -96,9 +95,9 @@ function RepoImpl() { }, [me, id, setSessionId]); useEffect(() => { resetState(); - setRepo(id); + setRepo(id!); // load the repo. It is actually not a queue, just an async thunk - loadRepo(id); + loadRepo(id!); }, [id, loadRepo, resetState, setRepo]); // FIXME Removing queueL. This will cause Repo to be re-rendered a lot of @@ -118,7 +117,7 @@ function RepoImpl() { // m={2} overflow="auto" > - + )} diff --git a/ui/src/pages/repos.tsx b/ui/src/pages/repos.tsx index e13e03a2..af0bcde2 100644 --- a/ui/src/pages/repos.tsx +++ b/ui/src/pages/repos.tsx @@ -82,7 +82,7 @@ function RepoLine({ repo }) { to={`/repo/${repo.id}`} >{`${repo.name}`} {error && {error}} diff --git a/ui/src/pages/test.tsx b/ui/src/pages/test.tsx index 8fdd253f..7a9441e2 100644 --- a/ui/src/pages/test.tsx +++ b/ui/src/pages/test.tsx @@ -1,5 +1,4 @@ // import React, { useState } from "react"; -// import { MySlateExample } from "../components/MySlate"; import * as React from "react"; import Button from "@mui/material/Button"; diff --git a/ui/yarn.lock b/ui/yarn.lock index 6d41871a..c62833d9 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -15672,6 +15672,16 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +xterm-addon-fit@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.6.0.tgz#142e1ce181da48763668332593fc440349c88c34" + integrity sha512-9/7A+1KEjkFam0yxTaHfuk9LEvvTSBi0PZmEkzJqgafXPEXL9pCMAVV7rB09sX6ATRDXAdBpQhZkhKj7CGvYeg== + +xterm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.0.0.tgz#0af50509b33d0dc62fde7a4ec17750b8e453cc5c" + integrity sha512-tmVsKzZovAYNDIaUinfz+VDclraQpPUnAME+JawosgWRMphInDded/PuY0xmU5dOhyeYZsI0nz5yd8dPYsdLTA== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"