diff --git a/src/app/app.tsx b/src/app/app.tsx index 42dba420..3acaa712 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -16,6 +16,7 @@ import { PluginsView } from "./pluginsview/pluginsview"; import { BookmarksView } from "./bookmarks/bookmarks"; import { HistoryView } from "./history/history"; import { ConnectionsView } from "./connections/connections"; +import { ClientSettingsView } from "./clientsettings/clientsettings"; import { MainSideBar } from "./sidebar/sidebar"; import { DisconnectedModal, ClientStopModal } from "./common/modals"; import { ModalsProvider } from "./common/modals/provider"; @@ -109,6 +110,7 @@ class App extends React.Component<{}, {}> { + diff --git a/src/app/clientsettings/clientsettings.less b/src/app/clientsettings/clientsettings.less new file mode 100644 index 00000000..034953ec --- /dev/null +++ b/src/app/clientsettings/clientsettings.less @@ -0,0 +1,200 @@ +@import "../../app/common/themes/themes.less"; + +.clientsettings-view { + background-color: @background-session; + flex-grow: 1; + display: flex; + flex-direction: column; + position: relative; + overflow: auto; + margin-bottom: 10px; + margin-right: 10px; + border-radius: 8px; + border: 1px solid rgba(241, 246, 243, 0.08); + background: rgba(13, 13, 13, 0.85); + + .header { + margin: 24px 18px; + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 20px; + border-bottom: 1px solid white; + } + + .content { + padding: 0 18px 0 30px; + } + + // just marked these as important since we're keeping the + // settings-field styles below this intact until we figure out what do with them + .settings-field { + &:not(:first-child) { + margin-top: 15px !important; + } + + .settings-label { + width: 250px !important; + } + } + + // @TODO: These styles are duplicated in the settings modals + .settings-field { + display: flex; + flex-direction: row; + align-items: center; + + &.settings-field.sub-field { + .settings-label { + font-weight: normal; + + text-align: right; + padding-right: 20px; + } + } + + &.settings-error { + color: @term-red; + margin-top: 20px; + padding: 10px; + border-radius: 5px; + background-color: #200707; + border: 1px solid @term-red; + font-weight: bold; + + .error-dismiss { + padding: 2px; + cursor: pointer; + } + } + + .settings-label { + font-weight: bold; + width: 12em; + display: flex; + flex-direction: row; + align-items: center; + margin-right: 10px; + + .info-message { + margin-left: 5px; + } + } + + .settings-input { + display: flex; + flex-direction: row; + align-items: center; + + &.settings-clickable { + cursor: pointer; + } + + &.inline-edit.edit-active { + input.input { + padding: 0; + height: 20px; + } + + .button { + height: 20px; + } + } + + input { + padding: 4px; + border-radius: 3px; + } + + .control { + .icon { + width: 1.5em; + height: 1.5em; + margin: 0; + } + } + + .tab-color-icon.color-default path { + fill: @tab-green; + } + .tab-color-icon.color-green path { + fill: @tab-green; + } + .tab-color-icon.color-orange path { + fill: @tab-orange; + } + .tab-color-icon.color-red path { + fill: @tab-red; + } + .tab-color-icon.color-yellow path { + fill: @tab-yellow; + } + .tab-color-icon.color-blue path { + fill: @tab-blue; + } + .tab-color-icon.color-mint path { + fill: @tab-mint; + } + .tab-color-icon.color-cyan path { + fill: @tab-cyan; + } + .tab-color-icon.color-white path { + fill: @tab-white; + } + .tab-color-icon.color-violet path { + fill: @tab-violet; + } + .tab-color-icon.color-pink path { + fill: @tab-pink; + } + + .tab-colors, + .tab-icons { + display: flex; + flex-direction: row; + align-items: center; + + .tab-color-sep, + .tab-icon-sep { + padding-left: 10px; + padding-right: 10px; + font-weight: bold; + } + + .tab-color-icon, + .tab-icon-icon { + width: 1.1em; + vertical-align: middle; + } + + .tab-color-name, + .tab-icon-name { + margin-left: 1em; + } + + .tab-color-select, + .tab-icon-select { + cursor: pointer; + margin: 5px; + &:hover { + outline: 2px solid white; + } + } + } + + .action-text { + margin-left: 20px; + + color: @term-red; + } + + .settings-share-link { + width: 160px; + } + } + + &:not(:first-child) { + margin-top: 10px; + } + } +} diff --git a/src/app/clientsettings/clientsettings.tsx b/src/app/clientsettings/clientsettings.tsx new file mode 100644 index 00000000..8d3c4cde --- /dev/null +++ b/src/app/clientsettings/clientsettings.tsx @@ -0,0 +1,219 @@ +// Copyright 2023, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import * as React from "react"; +import * as mobxReact from "mobx-react"; +import * as mobx from "mobx"; +import { boundMethod } from "autobind-decorator"; +import cn from "classnames"; +import { GlobalModel, GlobalCommandRunner, MinFontSize, MaxFontSize, RemotesModel } from "../../model/model"; +import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "../common/common"; +import { CommandRtnType, ClientDataType } from "../../types/types"; +import { commandRtnHandler, isBlank } from "../../util/util"; + +import "./clientsettings.less"; + +type OV = mobx.IObservableValue; + +// @ts-ignore +const VERSION = __WAVETERM_VERSION__; +// @ts-ignore +const BUILD = __WAVETERM_BUILD__; + +@mobxReact.observer +class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> { + fontSizeDropdownActive: OV = mobx.observable.box(false, { name: "clientSettings-fontSizeDropdownActive" }); + errorMessage: OV = mobx.observable.box(null, { name: "ClientSettings-errorMessage" }); + + @boundMethod + closeModal(): void { + GlobalModel.modalsModel.popModal(); + } + + @boundMethod + dismissError(): void { + mobx.action(() => { + this.errorMessage.set(null); + })(); + } + + @boundMethod + handleChangeFontSize(fontSize: string): void { + let newFontSize = Number(fontSize); + this.fontSizeDropdownActive.set(false); + if (GlobalModel.termFontSize.get() == newFontSize) { + return; + } + let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + togglefontSizeDropdown(): void { + mobx.action(() => { + this.fontSizeDropdownActive.set(!this.fontSizeDropdownActive.get()); + })(); + } + + @boundMethod + handleChangeTelemetry(val: boolean): void { + let prtn: Promise = null; + if (val) { + prtn = GlobalCommandRunner.telemetryOn(false); + } else { + prtn = GlobalCommandRunner.telemetryOff(false); + } + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + handleChangeReleaseCheck(val: boolean): void { + let prtn: Promise = null; + if (val) { + prtn = GlobalCommandRunner.releaseCheckAutoOn(false); + } else { + prtn = GlobalCommandRunner.releaseCheckAutoOff(false); + } + commandRtnHandler(prtn, this.errorMessage); + } + + getFontSizes(): any { + let availableFontSizes: { label: string; value: number }[] = []; + for (let s = MinFontSize; s <= MaxFontSize; s++) { + availableFontSizes.push({ label: s + "px", value: s }); + } + return availableFontSizes; + } + + @boundMethod + inlineUpdateOpenAIModel(newModel: string): void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel }); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + inlineUpdateOpenAIToken(newToken: string): void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken }); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr }); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + setErrorMessage(msg: string): void { + mobx.action(() => { + this.errorMessage.set(msg); + })(); + } + + render() { + let isHidden = GlobalModel.activeMainView.get() != "clientsettings"; + if (isHidden) { + return null; + } + + let cdata: ClientDataType = GlobalModel.clientData.get(); + let openAIOpts = cdata.openaiopts ?? {}; + let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********"; + let maxTokensStr = String( + openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens + ); + let curFontSize = GlobalModel.termFontSize.get(); + + return ( +
+
+
Client Settings
+
+
+
+
Term Font Size
+
+ +
+
+
+
Client ID
+
{cdata.clientid}
+
+
+
Client Version
+
+ {VERSION} {BUILD} +
+
+
+
DB Version
+
{cdata.dbversion}
+
+
+
Basic Telemetry
+
+ +
+
+
+
Check for Updates
+
+ +
+
+
+
OpenAI Token
+
+ +
+
+
+
OpenAI Model
+
+ +
+
+
+
OpenAI MaxTokens
+
+ +
+
+ +
+
+ ); + } +} + +export { ClientSettingsView }; diff --git a/src/app/common/modals/registry.tsx b/src/app/common/modals/registry.tsx index ebdc8382..7b01e740 100644 --- a/src/app/common/modals/registry.tsx +++ b/src/app/common/modals/registry.tsx @@ -10,7 +10,7 @@ import { EditRemoteConnModal, TabSwitcherModal, } from "../modals"; -import { ScreenSettingsModal, SessionSettingsModal, LineSettingsModal, ClientSettingsModal } from "./settings"; +import { ScreenSettingsModal, SessionSettingsModal, LineSettingsModal } from "./settings"; import * as constants from "../../appconst"; const modalsRegistry: { [key: string]: () => React.ReactElement } = { @@ -22,7 +22,6 @@ const modalsRegistry: { [key: string]: () => React.ReactElement } = { [constants.SCREEN_SETTINGS]: () => , [constants.SESSION_SETTINGS]: () => , [constants.LINE_SETTINGS]: () => , - [constants.CLIENT_SETTINGS]: () => , [constants.TAB_SWITCHER]: () => , }; diff --git a/src/app/common/modals/settings.tsx b/src/app/common/modals/settings.tsx index 3718a838..1ba089a2 100644 --- a/src/app/common/modals/settings.tsx +++ b/src/app/common/modals/settings.tsx @@ -17,22 +17,12 @@ import { Screen, Session, } from "../../../model/model"; -import { - Toggle, - InlineSettingsTextEdit, - SettingsError, - InfoMessage, - Modal, - Dropdown, - Tooltip, - Button, -} from "../common"; +import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal, Dropdown, Tooltip } from "../common"; import { LineType, RendererPluginType, ClientDataType, CommandRtnType, RemoteType } from "../../../types/types"; import { PluginModel } from "../../../plugins/plugins"; import * as util from "../../../util/util"; import { commandRtnHandler } from "../../../util/util"; import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; -import { ReactComponent as AngleDownIcon } from "../../assets/icons/history/angle-down.svg"; import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg"; import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg"; diff --git a/src/app/sidebar/sidebar.tsx b/src/app/sidebar/sidebar.tsx index 57dcd9bd..a0d1b64b 100644 --- a/src/app/sidebar/sidebar.tsx +++ b/src/app/sidebar/sidebar.tsx @@ -131,7 +131,11 @@ class MainSideBar extends React.Component<{}, {}> { @boundMethod handleSettingsClick(): void { - GlobalModel.modalsModel.pushModal(constants.CLIENT_SETTINGS); + if (GlobalModel.activeMainView.get() == "clientsettings") { + GlobalModel.showSessionView(); + return; + } + GlobalCommandRunner.clientSettingsView(); } @boundMethod diff --git a/src/model/model.ts b/src/model/model.ts index 29b6487e..787a7dcc 100644 --- a/src/model/model.ts +++ b/src/model/model.ts @@ -2576,6 +2576,14 @@ class ConnectionsViewModel { } } +class ClientSettingsViewModel { + showClientSettingsView(): void { + mobx.action(() => { + GlobalModel.activeMainView.set("clientsettings"); + })(); + } +} + class BookmarksModel { bookmarks: OArr = mobx.observable.array([], { name: "Bookmarks", @@ -3305,10 +3313,11 @@ class Model { authKey: string; isDev: boolean; platform: string; - activeMainView: OV<"plugins" | "session" | "history" | "bookmarks" | "webshare" | "connections"> = - mobx.observable.box("session", { - name: "activeMainView", - }); + activeMainView: OV< + "plugins" | "session" | "history" | "bookmarks" | "webshare" | "connections" | "clientsettings" + > = mobx.observable.box("session", { + name: "activeMainView", + }); termFontSize: CV; alertMessage: OV = mobx.observable.box(null, { name: "alertMessage", @@ -3337,6 +3346,7 @@ class Model { bookmarksModel: BookmarksModel; historyViewModel: HistoryViewModel; connectionViewModel: ConnectionsViewModel; + clientSettingsViewModel: ClientSettingsViewModel; modalsModel: ModalsModel; clientData: OV = mobx.observable.box(null, { name: "clientData", @@ -3360,6 +3370,7 @@ class Model { this.bookmarksModel = new BookmarksModel(); this.historyViewModel = new HistoryViewModel(); this.connectionViewModel = new ConnectionsViewModel(); + this.clientSettingsViewModel = new ClientSettingsViewModel(); this.remotesModalModel = new RemotesModalModel(); this.remotesModel = new RemotesModel(); this.modalsModel = new ModalsModel(); @@ -4794,6 +4805,10 @@ class CommandRunner { GlobalModel.connectionViewModel.showConnectionsView(); } + clientSettingsView() { + GlobalModel.clientSettingsViewModel.showClientSettingsView(); + } + historyView(params: HistorySearchParams) { let kwargs = { nohist: "1" }; kwargs["offset"] = String(params.offset);