diff --git a/frontend/app/element/quicktips.less b/frontend/app/element/quicktips.less new file mode 100644 index 000000000..9a88ee51d --- /dev/null +++ b/frontend/app/element/quicktips.less @@ -0,0 +1,83 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +.tips-wrapper { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + + .tips-section { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 5px; + + .tip-section-header { + font-weight: bold; + margin-bottom: 5px; + margin-top: 10px; + font-size: 16px; + + &:first-child { + margin-top: 0; + } + } + + .tip { + display: flex; + flex-direction: row; + align-items: center; + + code { + padding: 0.1em 0.4em; + background-color: var(--highlight-bg-color); + } + + .keybinding-group { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 5px; + margin-right: 5px; + + &:first-child { + margin-left: 0; + } + } + + .keybinding { + display: inline-block; + padding: 0.1em 0.4em; + margin: 0 0.1em; + font: var(--fixed-font); + font-size: 0.85em; + color: var(--keybinding-color); + background-color: var(--keybinding-bg-color); + border-radius: 4px; + border: 1px solid var(--keybinding-border-color); + box-shadow: none; + } + + .icon-wrap { + background-color: var(--highlight-bg-color); + padding: 2px; + color: var(--secondary-text-color); + font-size: 12px; + border-radius: 2px; + margin-right: 5px; + + svg { + position: relative; + top: 3px; + left: 1px; + height: 13px; + #arrow1, + #arrow2 { + fill: var(--main-text-color); + } + } + } + } + } +} diff --git a/frontend/app/element/quicktips.tsx b/frontend/app/element/quicktips.tsx new file mode 100644 index 000000000..6036d8ff9 --- /dev/null +++ b/frontend/app/element/quicktips.tsx @@ -0,0 +1,152 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { MagnifyIcon } from "@/app/element/magnify"; +import { PLATFORM } from "@/app/store/global"; +import "./quicktips.less"; + +const KeyBinding = ({ keyDecl }: { keyDecl: string }) => { + const parts = keyDecl.split(":"); + const elems: React.ReactNode[] = []; + for (let part of parts) { + if (part === "Cmd") { + if (PLATFORM === "darwin") { + elems.push( +
+ ⌘ Cmd +
+ ); + } else { + elems.push( +
+ Alt +
+ ); + } + continue; + } + if (part == "Ctrl") { + elems.push( +
+ ^ Ctrl +
+ ); + continue; + } + if (part == "Shift") { + elems.push( +
+ ⇧ Shift +
+ ); + continue; + } + if (part == "Arrows") { + elems.push( +
+ ← +
+ ); + elems.push( +
+ → +
+ ); + elems.push( +
+ ↑ +
+ ); + elems.push( +
+ ↓ +
+ ); + continue; + } + if (part == "Digit") { + elems.push( +
+ Number (1-9) +
+ ); + continue; + } + elems.push( +
+ {part.toUpperCase()} +
+ ); + } + return
{elems}
; +}; + +const QuickTips = () => { + return ( +
+
+
Header Icons
+
+
+ +
+ Connect to a remote server + +
+
+
+ +
+ Magnify a Block +
+
+
+ +
+ Block Settings +
+
+
+ +
+ Close Block +
+ +
Important Keybindings
+ +
+ + New Tab +
+
+ + New Terminal Block +
+
+ + Navigate Between Blocks +
+
+ + Focus Nth Block +
+
+ + Switch To Nth Tab +
+ +
wsh commands
+
+
+ wsh view [filename|url] +
+ Run this command in the terminal to preview a file, directory, or web URL. +
+
+
+
+
+ ); +}; + +export { KeyBinding, QuickTips }; diff --git a/frontend/app/modals/modalsrenderer.tsx b/frontend/app/modals/modalsrenderer.tsx index 20a6ee1ec..0cc3cbf83 100644 --- a/frontend/app/modals/modalsrenderer.tsx +++ b/frontend/app/modals/modalsrenderer.tsx @@ -10,6 +10,7 @@ import { TosModal } from "./tos"; const ModalsRenderer = () => { const clientData = jotai.useAtomValue(atoms.client); + const [tosOpen, setTosOpen] = jotai.useAtom(modalsModel.tosOpen); const [modals] = jotai.useAtom(modalsModel.modalsAtom); const rtn: JSX.Element[] = []; for (const modal of modals) { @@ -18,12 +19,18 @@ const ModalsRenderer = () => { rtn.push(); } } - if (!clientData.tosagreed) { + if (tosOpen) { rtn.push(); } + useEffect(() => { + if (!clientData.tosagreed) { + setTosOpen(true); + } + }, [clientData]); useEffect(() => { globalStore.set(atoms.modalOpen, rtn.length > 0); - }); + }, [rtn]); + return <>{rtn}; }; diff --git a/frontend/app/modals/tos.less b/frontend/app/modals/tos.less index f7a85ed00..1540a139c 100644 --- a/frontend/app/modals/tos.less +++ b/frontend/app/modals/tos.less @@ -11,6 +11,7 @@ flex-direction: column; overflow-y: auto; padding: 30px; + width: 100%; header.tos-header { flex-direction: column; @@ -18,6 +19,7 @@ border-bottom: none; padding: 0; margin-bottom: 36px; + width: 100%; .logo { margin-bottom: 10px; @@ -55,6 +57,106 @@ color: var(--secondary-text-color); } + .tips-wrapper { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + + .tips-section { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 5px; + + .tip-section-header { + font-weight: bold; + margin-bottom: 5px; + margin-top: 10px; + + &:first-child { + margin-top: 0; + } + } + + .tip { + display: flex; + flex-direction: row; + align-items: center; + + .keybinding2 { + font: var(--fixed-font); + background-color: var(--highlight-bg-color); + color: var(--main-text-color); + padding: 2px 8px; + border-radius: 4px; + } + + .keybinding-group { + display: flex; + flex-direction: row; + align-items: center; + margin-left: 5px; + margin-right: 5px; + + &:first-child { + margin-left: 0; + } + } + + .keybinding { + display: inline-block; + padding: 0.1em 0.4em; + margin: 0 0.1em; + font-family: "SF Pro Text", "Segoe UI", sans-serif; + font-size: 0.85em; + color: #e0e0e0; + background-color: #333; + border-radius: 4px; + border: 1px solid #444; + box-shadow: none; + } + + .keybinding3 { + color: black; + display: inline-block; + padding: 0.2em 0.4em; + min-width: 24px; + height: 22px; + margin: 0 0.1em; + font-family: "SF Pro Text", "Segoe UI", sans-serif; + font-size: 0.9em; + border: 1px solid #aaa; + border-radius: 4px; + background-color: #ddd; + color: var(--invert-text-color); + box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.1); + text-align: center; + } + + .icon-wrap { + background-color: var(--highlight-bg-color); + padding: 2px; + color: var(--secondary-text-color); + font-size: 12px; + border-radius: 2px; + margin-right: 5px; + + svg { + position: relative; + top: 3px; + left: 1px; + height: 13px; + #arrow1, + #arrow2 { + fill: var(--main-text-color); + } + } + } + } + } + } + .content-section { display: flex; width: 100%; diff --git a/frontend/app/modals/tos.tsx b/frontend/app/modals/tos.tsx index 4a860560f..2f7d6b915 100644 --- a/frontend/app/modals/tos.tsx +++ b/frontend/app/modals/tos.tsx @@ -9,13 +9,156 @@ import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; import { useEffect, useRef, useState } from "react"; import { FlexiModal } from "./modal"; +import { QuickTips } from "@/app/element/quicktips"; +import { atoms } from "@/app/store/global"; +import { modalsModel } from "@/app/store/modalmodel"; import { RpcApi } from "@/app/store/wshclientapi"; import { WindowRpcClient } from "@/app/store/wshrpcutil"; +import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai"; import "./tos.less"; +const pageNumAtom: PrimitiveAtom = atom(1); + +const ModalPage1 = () => { + const settings = useAtomValue(atoms.settingsAtom); + const clientData = useAtomValue(atoms.client); + const [tosOpen, setTosOpen] = useAtom(modalsModel.tosOpen); + const [telemetryEnabled, setTelemetryEnabled] = useState(!!settings["telemetry:enabled"]); + const setPageNum = useSetAtom(pageNumAtom); + + const acceptTos = () => { + if (!clientData.tosagreed) { + services.ClientService.AgreeTos(); + } + setPageNum(2); + }; + + const setTelemetry = (value: boolean) => { + RpcApi.SetConfigCommand(WindowRpcClient, { "telemetry:enabled": value }) + .then(() => { + setTelemetryEnabled(value); + }) + .catch((error) => { + console.error("failed to set telemetry:", error); + }); + }; + + const label = telemetryEnabled ? "Telemetry Enabled" : "Telemetry Disabled"; + + return ( + <> +
+
+ +
+
Welcome to Wave Terminal
+
+
+
+
+ + + +
+
+
Support us on GitHub
+
+ We're open source and committed to providing a free terminal for individual users. + Please show your support by giving us a star on{" "} + + Github (wavetermdev/waveterm) + +
+
+
+
+
+ + + +
+
+
Join our Community
+
+ Get help, submit feature requests, report bugs, or just chat with fellow terminal + enthusiasts. +
+ + Join the Wave Discord Channel + +
+
+
+
+
+ +
+
+
Telemetry
+
+ We collect minimal anonymous{" "} + + telemetry data + {" "} + to help us understand how people are using Wave ( + + Privacy Policy + + ). +
+ +
+
+
+
+
+ +
+
+ + ); +}; + +const ModalPage2 = () => { + const [tosOpen, setTosOpen] = useAtom(modalsModel.tosOpen); + + const handleGetStarted = () => { + setTosOpen(false); + }; + + return ( + <> +
+
+ +
+
Icons and Keybindings
+
+
+ +
+
+
+ +
+
+ + ); +}; + const TosModal = () => { - const [telemetryEnabled, setTelemetryEnabled] = useState(true); const modalRef = useRef(null); + const [pageNum, setPageNum] = useAtom(pageNumAtom); + const clientData = useAtomValue(atoms.client); const updateModalHeight = () => { const windowHeight = window.innerHeight; @@ -30,6 +173,16 @@ const TosModal = () => { } }; + useEffect(() => { + // on unmount, always reset pagenum + if (clientData.tosagreed) { + setPageNum(2); + } + return () => { + setPageNum(1); + }; + }, []); + useEffect(() => { updateModalHeight(); // Run on initial render @@ -39,118 +192,10 @@ const TosModal = () => { }; }, []); - const acceptTos = () => { - services.ClientService.AgreeTos(); - }; - - const setTelemetry = (value: boolean) => { - RpcApi.SetConfigCommand(WindowRpcClient, { "telemetry:enabled": value }) - .then(() => { - setTelemetryEnabled(value); - }) - .catch((error) => { - console.error("failed to set telemetry:", error); - }); - }; - - useEffect(() => { - services.FileService.GetFullConfig() - .then((data) => { - if ("telemetry:enabled" in data.settings) { - setTelemetryEnabled(true); - } else { - setTelemetryEnabled(false); - } - }) - .catch((error) => { - console.error("failed to get config:", error); - }); - }, []); - - const label = telemetryEnabled ? "Telemetry Enabled" : "Telemetry Disabled"; - return ( -
-
- -
-
Welcome to Wave Terminal
-
-
-
-
- - - -
-
-
Support us on GitHub
-
- We're open source and committed to providing a free terminal for individual - users. Please show your support by giving us a star on{" "} - - Github (wavetermdev/waveterm) - -
-
-
-
-
- - - -
-
-
Join our Community
-
- Get help, submit feature requests, report bugs, or just chat with fellow terminal - enthusiasts. -
- - Join the Wave Discord Channel - -
-
-
-
-
- -
-
-
Telemetry
-
- We collect minimal anonymous{" "} - - telemetry data - {" "} - to help us understand how people are using Wave ( - - Privacy Policy - - ). -
- -
-
-
-
-
- -
-
+ {pageNum === 1 ? : }
); diff --git a/frontend/app/store/modalmodel.ts b/frontend/app/store/modalmodel.ts index 68b944c45..8fae9cd5c 100644 --- a/frontend/app/store/modalmodel.ts +++ b/frontend/app/store/modalmodel.ts @@ -6,8 +6,10 @@ import { globalStore } from "./global"; class ModalsModel { modalsAtom: jotai.PrimitiveAtom>; + tosOpen: jotai.PrimitiveAtom; constructor() { + this.tosOpen = jotai.atom(false); this.modalsAtom = jotai.atom([]); } diff --git a/frontend/app/theme.less b/frontend/app/theme.less index 80208749c..87b61c6a8 100644 --- a/frontend/app/theme.less +++ b/frontend/app/theme.less @@ -26,6 +26,10 @@ --block-bg-solid-color: rgb(0, 0, 0); --block-border-radius: 8px; + --keybinding-color: #e0e0e0; + --keybinding-bg-color: #333; + --keybinding-border-color: #444; + /* scrollbar colors */ --scrollbar-background-color: transparent; --scrollbar-thumb-color: rgba(255, 255, 255, 0.15); diff --git a/frontend/app/view/helpview/helpview.tsx b/frontend/app/view/helpview/helpview.tsx index ec7b41f10..48c804f3a 100644 --- a/frontend/app/view/helpview/helpview.tsx +++ b/frontend/app/view/helpview/helpview.tsx @@ -148,7 +148,7 @@ a codeedit block which you can use to quickly edit the file using Wave's embedde ### edit \`\`\` -wsh edit [path] +wsh editor [path] \`\`\` This will open up codeedit for the specified file. This is useful for quickly editing files on a local or remote machine in our graphical editor. This command will wait until the file is closed before exiting (unlike \`view\`) so you can set your \`$EDITOR\` to \`wsh edit\` for a seamless experience. You can combine this with a \`-m\` flag to open the editor in magnified mode. diff --git a/frontend/wave.ts b/frontend/wave.ts index c314196f7..1d2db23af 100644 --- a/frontend/wave.ts +++ b/frontend/wave.ts @@ -7,6 +7,7 @@ import { registerElectronReinjectKeyHandler, registerGlobalKeys, } from "@/app/store/keymodel"; +import { modalsModel } from "@/app/store/modalmodel"; import { FileService, ObjectService } from "@/app/store/services"; import { RpcApi } from "@/app/store/wshclientapi"; import { initWshrpc, WindowRpcClient } from "@/app/store/wshrpcutil"; @@ -52,6 +53,7 @@ loadFonts(); (window as any).countersClear = countersClear; (window as any).getLayoutModelForActiveTab = getLayoutModelForActiveTab; (window as any).pushFlashError = pushFlashError; +(window as any).modalsModel = modalsModel; document.title = `The Next Wave (${windowId.substring(0, 8)})`;