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
+
+
+
+
+
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)
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+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)
-
-
-
-
-
-
-
-
+ {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)})`;