Compare commits

..

14 Commits

Author SHA1 Message Date
sawka 9a9ee68604 Merge branch 'main' into sawka/layout 2024-02-20 23:44:25 -08:00
sawka 51c0c00416 fix another special case ai-chat close + cmd focus issue 2024-02-20 23:43:56 -08:00
sawka 02d605a624 fix bug where we sometimes lose cmd focus on tab switching 2024-02-20 23:37:26 -08:00
sawka 6613f480e3 merge main 2024-02-20 23:28:09 -08:00
Evan Simkowitz 96cabeff98 Fix sidebar middle div max-height calculation (#299)
* Fix sidebar middle div max-height calculation

* remove logs

* update bottom div padding
2024-02-20 12:23:39 -08:00
Red J Adaya 8cb449d2e7 remove old model (#307) 2024-02-20 12:22:09 -08:00
Mike Sawka 7b6025fd4b forward port v0.6.3 changes to main (#308) 2024-02-20 12:20:17 -08:00
Mike Sawka 07ad5f063e Fix #302 and clean up extraneous terminal margins (#303)
* font loading fix #302

* fix inconsistent paddings.  issue was that the first '[' feels indented (because of the fixed with font).  applying a negative 2px margin fixes it and allows us to remove the paddings on the other elements.
2024-02-18 17:58:22 -08:00
sawka e0427e5ed4 layout tests, checkpoint 2024-02-18 11:23:23 -08:00
Evan Simkowitz fe3ffd1545 Use IsEmpty rather than nullcheck for scbus types
* Use IsEmpty rather than nullcheck for scbus types
2024-02-16 12:14:05 -08:00
Mike Sawka e2e71898c1 new wlog package to do distributed logging from waveshell back to wavesrv (#295) 2024-02-15 17:42:43 -08:00
Evan Simkowitz 07fa5bf9cb Remove verbose log from scbus (#296) 2024-02-15 16:50:03 -08:00
Evan Simkowitz 8acda3525b Break model update code out of sstore (#290)
* Break update code out of sstore

* add license disclaimers

* missed one

* add another

* fix regression in openai updates, remove unnecessary functions

* another copyright

* update casts

* fix issue with variadic updates

* remove logs

* remove log

* remove unnecessary log

* save work

* moved a bunch of stuff to scbus

* make modelupdate an object

* fix new screen not updating active screen

* add comment

* make updates into packet types

* different cast

* update comments, remove unused methods

* add one more comment

* add an IsEmpty() on model updates to prevent sending empty updates to client
2024-02-15 16:45:47 -08:00
Sylvie Crowe 158378a7ad Ssh Fixes and Improvements (#293)
* feat: parse multiple identity files in ssh

While this does not make it possible to discover multiple identity files
in every case, it does make it possible to parse them individually and
check for user input if it's required for each one.

* chore: remove unnecessary print in updatebus.go

* chore: remove unnecessary print in sshclient.go

* chore: remove old publicKey auth check

With the new callback in place, we no longer need this, so it has been
removed.

* refactor: move logic for wave and config options

The logic for making decisions between details made available from wave
and details made available from ssh_config was spread out. This change
condenses it into one function for gathering those details and one for
picking between them.

It also adds a few new keywords but the logic for those hasn't been
implemented yet.

* feat: allow attempting auth methods in any order

While waveterm does not provide the control over which order to attempt
yet, it is possible to provide that information in the ssh_config. This
change allows that order to take precedence in a case where it is set.

* feat: add batch mode support

BatchMode turns off user input to enter passwords for ssh. Because we
save passwords, we can still attempt these methods but we disable the
user interactive prompts in this case.

* fix: fix auth ordering and identity files

The last few commits introduced a few bugs that are fixed here. The
first is that the auth ordering is parsed as a single string and not a
list. This is fixed by manually splitting the string into a list. The
second is that the copy of identity files was not long enough to copy
the contents of the original. This is now updated to use the length of
the original in its construction.

* deactivate timer while connecting to new ssh

The new ssh setup handles timers differently from the old one due to the
possibility of asking for user input multiple times. This limited the
user input to entirely be done within 15 seconds. This removes that
restriction which will allow those timers to increase. It does not
impact the legacy ssh systems or the local connections on the new
system.

* merge branch 'main' into 'ssh--auth-control'

This was mostly straightforward, but it appears that a previous commit
to main broke the user input modals by deleting a function. This adds
that back in addition to the merge.

* fix: allow 60 second timeouts for ssh inputs

With the previous change, it is now possible to extend the timeout for
manual inputs. 60 seconds should be a reasonable starting point.

* fix: change size of dummy key to 2048

This fixes the CodeQL scan issue for using a weak key.
2024-02-15 15:58:50 -08:00
48 changed files with 1512 additions and 6220 deletions
+1
View File
@@ -1,3 +1,4 @@
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+4
View File
@@ -8,6 +8,10 @@
}
:root {
/* updated by GlobalModel to user's preferred fontsize */
--termfontsize: 12px;
--termlineheight: 15px;
--fa-style-family: "Font Awesome 6 Sharp";
}
+2 -1
View File
@@ -18,7 +18,8 @@ export const ConfirmKey_HideShellPrompt = "hideshellprompt";
export const NoStrPos = -1;
export const RemotePtyRows = 8; // also in main.tsx
export const RemotePtyRows = 8;
export const RemotePtyTotalRows = 25;
export const RemotePtyCols = 80;
export const ProdServerEndpoint = "http://127.0.0.1:1619";
export const ProdServerWsEndpoint = "ws://127.0.0.1:1623";
@@ -370,7 +370,11 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
ref={this.termRef}
data-remoteid={remote.remoteid}
style={{
height: textmeasure.termHeightFromRows(appconst.RemotePtyRows, termFontSize),
height: textmeasure.termHeightFromRows(
appconst.RemotePtyRows,
termFontSize,
appconst.RemotePtyTotalRows
),
width: termWidth,
}}
></div>
+1 -27
View File
@@ -388,7 +388,7 @@ class LineCmd extends React.Component<
let height = 45 + 24; // height of zero height terminal
const usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width);
if (usedRows > 0) {
height = 48 + 24 + termHeightFromRows(usedRows, GlobalModel.termFontSize.get());
height = 48 + 24 + termHeightFromRows(usedRows, GlobalModel.termFontSize.get(), cmd.getTermMaxRows());
}
return height;
}
@@ -753,32 +753,6 @@ class LineCmd extends React.Component<
/>
</If>
</ErrorBoundary>
<If condition={cmd.getRtnState()}>
<div
key="rtnstate"
className="cmd-rtnstate"
style={{
visibility: cmd.getStatus() == "done" ? "visible" : "hidden",
}}
>
<If condition={rsdiff == null || rsdiff == ""}>
<div className="cmd-rtnstate-label">state unchanged</div>
<div className="cmd-rtnstate-sep"></div>
</If>
<If condition={rsdiff != null && rsdiff != ""}>
<div className="cmd-rtnstate-label">new state</div>
<div className="cmd-rtnstate-sep"></div>
<div className="cmd-rtnstate-diff" style={{ fontSize: rtnStateDiffSize }}>
<div className="cmd-rtnstate-diff-inner">{this.rtnStateDiff.get()}</div>
</div>
</If>
</div>
</If>
<If condition={isSelected && !isFocused && rendererType == "terminal"}>
<div className="cmd-hints">
<div className="hint-item color-nohover-white">focus line ({renderCmdText("L")})</div>
</div>
</If>
</If>
</div>
);
+14 -37
View File
@@ -23,18 +23,9 @@
.line.line-cmd {
flex-direction: column;
scroll-margin-bottom: 20px;
scroll-margin-bottom: var(--termlineheight);
position: relative;
.plus {
opacity: 0.5;
}
&:hover {
.plus {
opacity: 1;
}
}
.sidebar-message {
color: @term-yellow;
}
@@ -45,13 +36,14 @@
padding-bottom: 0.7rem;
width: 100%;
line-height: 1.2;
height: calc(var(--termlineheight) * 2);
&.is-expanded {
height: auto;
}
&.hide-prompt {
height: 32px;
height: calc(var(--termlineheight) * 2);
}
.line-icon {
@@ -81,12 +73,15 @@
display: block;
opacity: 1;
}
}
.meta.meta-line1 {
margin-left: 2px;
color: rgba(@base-color, 0.6) !important;
font-size: 11px;
.meta.meta-line1 {
color: rgba(@base-color, 0.6) !important;
font-size: var(--termfontsize);
}
.meta.meta-line2 {
margin-left: -2px;
}
}
&.has-rtnstate .terminal-wrapper {
@@ -113,11 +108,6 @@
overflow-x: hidden;
}
.terminal {
margin-right: 8px;
padding: 0.25rem;
}
&.cmd-done .terminal .xterm-cursor {
display: none;
}
@@ -208,7 +198,6 @@
.simple-line-header {
display: flex;
margin-top: 5px;
flex-direction: row;
}
@@ -350,14 +339,9 @@
display: flex;
flex-direction: row;
.simple-line-status {
margin-top: 5px;
}
.user {
color: lighten(@soft-blue, 10%);
font-weight: bold;
margin-top: 1px;
margin-right: 10px;
}
@@ -365,12 +349,10 @@
.termopts,
.renderer {
display: flex;
margin-top: 5px;
}
.renderer {
margin-left: 3px;
margin-top: 5px;
svg {
width: 1em;
margin-right: 0.5em;
@@ -381,7 +363,6 @@
.settings {
display: none;
margin-left: 0.5em;
margin-top: 0.65em;
cursor: pointer;
width: 1em;
height: 1em;
@@ -396,7 +377,6 @@
}
.termopts {
margin-top: 5px;
display: none;
.resize-button {
cursor: pointer;
@@ -407,7 +387,6 @@
.metapart-mono {
margin-left: 8px;
margin-top: 4px;
white-space: nowrap;
}
@@ -466,6 +445,7 @@
padding: 0 0 10px 0;
flex-grow: 1;
position: relative;
overflow-x: hidden;
&::-webkit-scrollbar-thumb {
background-color: transparent !important;
@@ -482,10 +462,6 @@
}
}
.line:nth-child(2) {
padding-top: 1px;
}
.lines-spacer {
flex-grow: 1;
}
@@ -493,7 +469,8 @@
.line-sep {
display: flex;
align-items: center;
margin-top: 1em;
height: var(--termlineheight);
font-size: var(--termfontsize);
color: rgba(@base-color, 0.5);
}
-2
View File
@@ -18,8 +18,6 @@ let MagicLayout = {
ScreenMinContentSize: 100,
ScreenMaxContentSize: 5000,
// the 3 is for descenders, which get cut off in the terminal without this
TermDescendersHeight: 3,
TermWidthBuffer: 15,
TabWidth: 154,
+2 -2
View File
@@ -119,7 +119,6 @@
}
.middle {
max-height: calc(100vh - 32em);
padding: 4px 6px 8px 6px;
border-bottom: 1px solid @base-border;
.item {
@@ -142,9 +141,10 @@
.bottom {
position: absolute;
bottom: 2em;
bottom: 2rem;
left: 0;
width: 100%;
padding-top: 0.8rem;
}
.item {
+37 -6
View File
@@ -64,7 +64,7 @@ interface MainSideBarProps {
@mobxReact.observer
class MainSideBar extends React.Component<MainSideBarProps, {}> {
sidebarRef = React.createRef<HTMLDivElement>();
middleHeightSubtractor = mobx.observable.box(404);
handleSessionClick(sessionId: string) {
GlobalCommandRunner.switchSession(sessionId);
@@ -203,14 +203,37 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
});
}
/**
* Calculate the subtractor portion for the middle div's height calculation, which should be `100vh - subtractor`.
*/
setMiddleHeightSubtractor() {
const windowHeight = window.innerHeight;
const bottomHeight = windowHeight - window.document.getElementById("sidebar-bottom")?.offsetTop;
const middleTop = document.getElementById("sidebar-middle")?.offsetTop;
const newMiddleHeightSubtractor = bottomHeight + middleTop;
if (!Number.isNaN(newMiddleHeightSubtractor)) {
mobx.action(() => {
this.middleHeightSubtractor.set(newMiddleHeightSubtractor);
})();
}
}
componentDidMount() {
this.setMiddleHeightSubtractor();
}
componentDidUpdate() {
this.setMiddleHeightSubtractor();
}
render() {
let clientData = this.props.clientData;
const clientData = this.props.clientData;
let needsUpdate = false;
if (!clientData?.clientopts.noreleasecheck && !isBlank(clientData?.releaseinfo?.latestversion)) {
needsUpdate = compareLoose(appconst.VERSION, clientData.releaseinfo.latestversion) < 0;
}
let mainSidebar = GlobalModel.mainSidebarModel;
let isCollapsed = mainSidebar.getCollapsed();
const mainSidebar = GlobalModel.mainSidebarModel;
const isCollapsed = mainSidebar.getCollapsed();
return (
<ResizableSidebar
className="main-sidebar"
@@ -271,8 +294,16 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
</CenteredIcon>,
]}
/>
<div className="middle hideScrollbarUntillHover">{this.getSessions()}</div>
<div className="bottom">
<div
className="middle hideScrollbarUntillHover"
id="sidebar-middle"
style={{
maxHeight: `calc(100vh - ${this.middleHeightSubtractor.get()}px)`,
}}
>
{this.getSessions()}
</div>
<div className="bottom" id="sidebar-bottom">
<If condition={needsUpdate}>
<SideBarItem
key="update-available"
+2 -2
View File
@@ -5,7 +5,7 @@ import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { GlobalModel } from "@/models";
import { isBlank } from "@/util";
import { isBlank } from "@/util/util";
import { boundMethod } from "autobind-decorator";
import cn from "classnames";
import { For } from "tsx-control-statements/components";
@@ -92,7 +92,7 @@ class AIChat extends React.Component<{}, {}> {
if (checkKeyPressed(waveEvent, "Escape")) {
e.preventDefault();
e.stopPropagation();
inputModel.closeAIAssistantChat();
inputModel.closeAIAssistantChat(true);
}
if (checkKeyPressed(waveEvent, "Ctrl:l")) {
+1 -1
View File
@@ -232,7 +232,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
if (inputModel.inputMode.get() != null) {
inputModel.resetInputMode();
}
inputModel.closeAIAssistantChat();
inputModel.closeAIAssistantChat(true);
return;
}
if (checkKeyPressed(waveEvent, "Cmd:e")) {
+2 -1
View File
@@ -109,6 +109,7 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, {
return <div className="screen-view" ref={this.screenViewRef}></div>;
}
let fontSize = GlobalModel.termFontSize.get();
let dprStr = sprintf("%0.3f", GlobalModel.devicePixelRatio.get());
let viewOpts = screen.viewOpts.get();
let hasSidebar = viewOpts?.sidebar?.open;
let winWidth = "100%";
@@ -145,7 +146,7 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, {
return (
<div className="screen-view" data-screenid={screen.screenId} ref={this.screenViewRef}>
<ScreenWindowView
key={screen.screenId + ":" + fontSize}
key={screen.screenId + ":" + fontSize + ":" + dprStr}
session={session}
screen={screen}
width={winWidth}
+39 -3
View File
@@ -177,6 +177,7 @@ function readAuthKey() {
return authKeyStr.trim();
}
const reloadAcceleratorKey = unamePlatform == "darwin" ? "Option+R" : "Super+R";
let cmdOrAlt = process.platform === "darwin" ? "Cmd" : "Alt";
let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{
role: "appMenu",
@@ -205,9 +206,41 @@ let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{ role: "reload", accelerator: reloadAcceleratorKey },
{ role: "toggleDevTools" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
{ role: "zoomOut" },
{
label: "Actual Size",
accelerator: cmdOrAlt + "+0",
click: () => {
if (MainWindow == null) {
return;
}
MainWindow.webContents.setZoomFactor(1);
MainWindow.webContents.send("zoom-changed");
},
},
{
label: "Zoom In",
accelerator: cmdOrAlt + "+Plus",
click: () => {
if (MainWindow == null) {
return;
}
const zoomFactor = MainWindow.webContents.getZoomFactor();
MainWindow.webContents.setZoomFactor(zoomFactor * 1.1);
MainWindow.webContents.send("zoom-changed");
},
},
{
label: "Zoom Out",
accelerator: cmdOrAlt + "+-",
click: () => {
if (MainWindow == null) {
return;
}
const zoomFactor = MainWindow.webContents.getZoomFactor();
MainWindow.webContents.setZoomFactor(zoomFactor / 1.1);
MainWindow.webContents.send("zoom-changed");
},
},
{ type: "separator" },
{ role: "togglefullscreen" },
],
@@ -375,6 +408,9 @@ function createMainWindow(clientData: ClientDataType | null) {
win.on("close", () => {
MainWindow = null;
});
win.webContents.on("zoom-changed", (e) => {
win.webContents.send("zoom-changed");
});
win.webContents.setWindowOpenHandler(({ url, frameName }) => {
if (url.startsWith("https://docs.waveterm.dev/")) {
console.log("openExternal docs", url);
+1
View File
@@ -21,6 +21,7 @@ contextBridge.exposeInMainWorld("api", {
onWCmd: (callback) => ipcRenderer.on("w-cmd", callback),
onPCmd: (callback) => ipcRenderer.on("p-cmd", callback),
onRCmd: (callback) => ipcRenderer.on("r-cmd", callback),
onZoomChanged: (callback) => ipcRenderer.on("zoom-changed", callback),
onMetaArrowUp: (callback) => ipcRenderer.on("meta-arrowup", callback),
onMetaArrowDown: (callback) => ipcRenderer.on("meta-arrowdown", callback),
onMetaPageUp: (callback) => ipcRenderer.on("meta-pageup", callback),
+4 -7
View File
@@ -8,6 +8,7 @@ import { sprintf } from "sprintf-js";
import { App } from "@/app/app";
import * as DOMPurify from "dompurify";
import { loadFonts } from "./util/util";
import * as textmeasure from "./util/textmeasure";
// @ts-ignore
let VERSION = __WAVETERM_VERSION__;
@@ -20,18 +21,14 @@ document.addEventListener("DOMContentLoaded", () => {
let reactElem = React.createElement(App, null, null);
let elem = document.getElementById("app");
let root = createRoot(elem);
let isFontLoaded = document.fonts.check("12px 'JetBrains Mono'");
if (isFontLoaded) {
document.fonts.ready.then(() => {
root.render(reactElem);
} else {
document.fonts.ready.then(() => {
root.render(reactElem);
});
}
});
});
(window as any).mobx = mobx;
(window as any).sprintf = sprintf;
(window as any).DOMPurify = DOMPurify;
(window as any).textmeasure = textmeasure;
console.log("WaveTerm", VERSION, BUILD);
+5
View File
@@ -88,6 +88,11 @@ class Cmd {
return this.data.get().termopts;
}
getTermMaxRows(): number {
let termOpts = this.getTermOpts();
return termOpts?.rows;
}
getCmdStr(): string {
return this.data.get().cmdstr;
}
+2 -2
View File
@@ -115,7 +115,7 @@ class HistoryViewModel {
} else {
this.activeItem.set(hitem.historyid);
let width = termWidthFromCols(80, this.globalModel.termFontSize.get());
let height = termHeightFromRows(25, this.globalModel.termFontSize.get());
let height = termHeightFromRows(25, this.globalModel.termFontSize.get(), 25);
this.specialLineContainer = new SpecialLineContainer(
this,
{ width, height },
@@ -149,7 +149,7 @@ class HistoryViewModel {
}
_deleteSelected(): void {
let lineIds = Array.from(this.selectedItems.keys());
let lineIds: string[] = Array.from(this.selectedItems.keys());
let prtn = GlobalCommandRunner.historyPurgeLines(lineIds);
prtn.then((result: CommandRtnType) => {
if (!result.success) {
+10 -3
View File
@@ -618,10 +618,17 @@ class InputModel {
})();
}
closeAIAssistantChat(): void {
// pass true to give focus to the input (e.g. if this is an 'active' close of the chat)
// when resetting the input (when switching screens, don't give focus)
closeAIAssistantChat(giveFocus: boolean): void {
if (!this.aIChatShow.get()) {
return;
}
mobx.action(() => {
this.aIChatShow.set(false);
this.giveFocus();
if (giveFocus) {
this.giveFocus();
}
})();
}
@@ -723,7 +730,7 @@ class InputModel {
resetInput(): void {
mobx.action(() => {
this.setHistoryShow(false);
this.closeAIAssistantChat();
this.closeAIAssistantChat(false);
this.infoShow.set(false);
this.inputMode.set(null);
this.resetHistory();
+176 -177
View File
@@ -32,13 +32,8 @@ import { MainSidebarModel } from "./mainsidebar";
import { Screen } from "./screen";
import { Cmd } from "./cmd";
import { GlobalCommandRunner } from "./global";
type KeyModsType = {
meta?: boolean;
ctrl?: boolean;
alt?: boolean;
shift?: boolean;
};
import { clearMonoFontCache } from "@/util/textmeasure";
import type { TermWrap } from "@/plugins/terminal/term";
type SWLinePtr = {
line: LineType;
@@ -46,36 +41,6 @@ type SWLinePtr = {
screen: Screen;
};
type ElectronApi = {
getId: () => string;
getIsDev: () => boolean;
getPlatform: () => string;
getAuthKey: () => string;
getWaveSrvStatus: () => boolean;
restartWaveSrv: () => boolean;
reloadWindow: () => void;
openExternalLink: (url: string) => void;
reregisterGlobalShortcut: (shortcut: string) => void;
onTCmd: (callback: (mods: KeyModsType) => void) => void;
onICmd: (callback: (mods: KeyModsType) => void) => void;
onLCmd: (callback: (mods: KeyModsType) => void) => void;
onHCmd: (callback: (mods: KeyModsType) => void) => void;
onPCmd: (callback: (mods: KeyModsType) => void) => void;
onRCmd: (callback: (mods: KeyModsType) => void) => void;
onWCmd: (callback: (mods: KeyModsType) => void) => void;
onMenuItemAbout: (callback: () => void) => void;
onMetaArrowUp: (callback: () => void) => void;
onMetaArrowDown: (callback: () => void) => void;
onMetaPageUp: (callback: () => void) => void;
onMetaPageDown: (callback: () => void) => void;
onBracketCmd: (callback: (event: any, arg: { relative: number }, mods: KeyModsType) => void) => void;
onDigitCmd: (callback: (event: any, arg: { digit: number }, mods: KeyModsType) => void) => void;
contextScreen: (screenOpts: { screenId: string }, position: { x: number; y: number }) => void;
contextEditMenu: (position: { x: number; y: number }, opts: ContextMenuOpts) => void;
onWaveSrvStatusChange: (callback: (status: boolean, pid: number) => void) => void;
getLastLogs: (numOfLines: number, callback: (logs: any) => void) => void;
};
function getApi(): ElectronApi {
return (window as any).api;
}
@@ -133,7 +98,10 @@ class Model {
});
lineSettingsModal: OV<number> = mobx.observable.box(null, {
name: "lineSettingsModal",
}); // linenum
});
devicePixelRatio: OV<number> = mobx.observable.box(window.devicePixelRatio, {
name: "devicePixelRatio",
});
remotesModel: RemotesModel;
inputModel: InputModel;
@@ -189,6 +157,10 @@ class Model {
}
return fontSize;
});
mobx.autorun(() => {
const fontSize = this.termFontSize.get();
this._setCssTermFontSize(fontSize);
});
getApi().onTCmd(this.onTCmd.bind(this));
getApi().onICmd(this.onICmd.bind(this));
getApi().onLCmd(this.onLCmd.bind(this));
@@ -196,6 +168,7 @@ class Model {
getApi().onPCmd(this.onPCmd.bind(this));
getApi().onWCmd(this.onWCmd.bind(this));
getApi().onRCmd(this.onRCmd.bind(this));
getApi().onZoomChanged(this.onZoomChanged.bind(this));
getApi().onMenuItemAbout(this.onMenuItemAbout.bind(this));
getApi().onMetaArrowUp(this.onMetaArrowUp.bind(this));
getApi().onMetaArrowDown(this.onMetaArrowDown.bind(this));
@@ -345,16 +318,10 @@ class Model {
return appconst.ProdServerEndpoint;
}
setTermFontSize(fontSize: number) {
if (fontSize < appconst.MinFontSize) {
fontSize = appconst.MinFontSize;
}
if (fontSize > appconst.MaxFontSize) {
fontSize = appconst.MaxFontSize;
}
mobx.action(() => {
this.termFontSize.set(fontSize);
})();
_setCssTermFontSize(fontSize: number) {
const root = document.documentElement;
root.style.setProperty("--termfontsize", fontSize + "px");
root.style.setProperty("--termlinehight", Math.ceil(fontSize * 1.2) + "px");
}
getBaseWsHostPort(): string {
@@ -514,6 +481,30 @@ class Model {
}
}
onZoomChanged(): void {
mobx.action(() => {
this.devicePixelRatio.set(window.devicePixelRatio);
clearMonoFontCache();
})();
}
// for debuggin
getSelectedTermWrap(): TermWrap {
let screen = this.getActiveScreen();
if (screen == null) {
return null;
}
let lineNum = screen.selectedLine.get();
if (lineNum == null) {
return null;
}
let line = screen.getLineByNum(lineNum);
if (line == null) {
return null;
}
return screen.getTermWrap(line.lineid);
}
clearModals(): boolean {
let didSomething = false;
mobx.action(() => {
@@ -715,7 +706,7 @@ class Model {
return this.ws.open.get();
}
runUpdate(genUpdate: UpdateMessage, interactive: boolean) {
runUpdate(genUpdate: UpdatePacket, interactive: boolean) {
mobx.action(() => {
const oldContext = this.getUIContext();
try {
@@ -727,8 +718,9 @@ class Model {
const newContext = this.getUIContext();
if (oldContext.sessionid != newContext.sessionid || oldContext.screenid != newContext.screenid) {
this.inputModel.resetInput();
if (!("ptydata64" in genUpdate)) {
const reversedGenUpdate = genUpdate.slice().reverse();
if (genUpdate.type == "model") {
const modelUpdate = genUpdate as ModelUpdatePacket;
const reversedGenUpdate = modelUpdate.data.slice().reverse();
const lastCmdLine = reversedGenUpdate.find((update) => "cmdline" in update);
if (lastCmdLine) {
// TODO a bit of a hack since this update gets applied in runUpdate_internal.
@@ -768,20 +760,12 @@ class Model {
}
updateActiveSession(sessionId: string): void {
const [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
if (sessionId != null) {
const newSessionId = sessionId;
if (this.activeSessionId.get() != newSessionId) {
this.activeSessionId.set(newSessionId);
}
}
const [newActiveSessionId, newActiveScreenId] = this.getActiveIds();
if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) {
this.activeMainView.set("session");
this.deactivateScreenLines();
this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
}
}
updateScreenNumRunningCommands(numRunningCommandUpdates: ScreenNumRunningCommandsUpdateType[]) {
@@ -796,9 +780,9 @@ class Model {
}
}
runUpdate_internal(genUpdate: UpdateMessage, uiContext: UIContextType, interactive: boolean) {
if ("ptydata64" in genUpdate) {
const ptyMsg: PtyDataUpdateType = genUpdate;
runUpdate_internal(genUpdate: UpdatePacket, uiContext: UIContextType, interactive: boolean) {
if (genUpdate.type == "pty") {
const ptyMsg = genUpdate.data as PtyDataUpdateType;
if (isBlank(ptyMsg.remoteid)) {
// regular update
this.updatePtyData(ptyMsg);
@@ -807,125 +791,138 @@ class Model {
const ptyData = base64ToArray(ptyMsg.ptydata64);
this.remotesModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
}
return;
}
let showedRemotesModal = false;
genUpdate.forEach((update) => {
if (update.connect != null) {
if (update.connect.screens != null) {
this.screenMap.clear();
this.updateScreens(update.connect.screens);
}
if (update.connect.sessions != null) {
this.sessionList.clear();
this.updateSessions(update.connect.sessions);
}
if (update.connect.remotes != null) {
this.remotes.clear();
this.updateRemotes(update.connect.remotes);
}
if (update.connect.activesessionid != null) {
this.updateActiveSession(update.connect.activesessionid);
}
if (update.connect.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands(update.connect.screennumrunningcommands);
}
if (update.connect.screenstatusindicators != null) {
this.updateScreenStatusIndicators(update.connect.screenstatusindicators);
}
} else if (genUpdate.type == "model") {
const modelUpdateItems = genUpdate.data as ModelUpdateItemType[];
this.sessionListLoaded.set(true);
this.remotesLoaded.set(true);
} else if (update.screen != null) {
this.updateScreens([update.screen]);
} else if (update.session != null) {
this.updateSessions([update.session]);
} else if (update.activesessionid != null) {
this.updateActiveSession(update.activesessionid);
} else if (update.line != null) {
this.addLineCmd(update.line.line, update.line.cmd, interactive);
} else if (update.cmd != null) {
this.updateCmd(update.cmd);
} else if (update.screenlines != null) {
this.updateScreenLines(update.screenlines, false);
} else if (update.remote != null) {
this.updateRemotes([update.remote]);
// This code's purpose is to show view remote connection modal when a new connection is added
if (!showedRemotesModal && this.remotesModel.recentConnAddedState.get()) {
showedRemotesModal = true;
this.remotesModel.openReadModal(update.remote.remoteid);
}
} else if (update.mainview != null) {
switch (update.mainview.mainview) {
case "session":
this.activeMainView.set("session");
break;
case "history":
if (update.mainview.historyview != null) {
this.historyViewModel.showHistoryView(update.mainview.historyview);
} else {
console.warn("invalid historyview in update:", update.mainview);
}
break;
case "bookmarks":
if (update.mainview.bookmarksview != null) {
this.bookmarksModel.showBookmarksView(
update.mainview.bookmarksview?.bookmarks ?? [],
update.mainview.bookmarksview?.selectedbookmark
);
} else {
console.warn("invalid bookmarksview in update:", update.mainview);
}
break;
case "plugins":
this.pluginsModel.showPluginsView();
break;
default:
console.warn("invalid mainview in update:", update.mainview);
}
} else if (update.bookmarks != null) {
if (update.bookmarks.bookmarks != null) {
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
}
} else if (update.clientdata != null) {
this.setClientData(update.clientdata);
} else if (update.cmdline != null) {
this.inputModel.updateCmdLine(update.cmdline);
} else if (update.openaicmdinfochat != null) {
this.inputModel.setOpenAICmdInfoChat(update.openaicmdinfochat);
} else if (update.screenstatusindicator != null) {
this.updateScreenStatusIndicators([update.screenstatusindicator]);
} else if (update.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands([update.screennumrunningcommands]);
} else if (update.userinputrequest != null) {
let userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (interactive) {
if (update.info != null) {
const info: InfoType = update.info;
this.inputModel.flashInfoMsg(info, info.timeoutms);
} else if (update.remoteview != null) {
const rview: RemoteViewType = update.remoteview;
if (rview.remoteedit != null) {
this.remotesModel.openEditModal({ ...rview.remoteedit });
let showedRemotesModal = false;
const [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
modelUpdateItems.forEach((update) => {
if (update.connect != null) {
if (update.connect.screens != null) {
this.screenMap.clear();
this.updateScreens(update.connect.screens);
}
} else if (update.alertmessage != null) {
const alertMessage: AlertMessageType = update.alertmessage;
this.showAlert(alertMessage);
} else if (update.history != null) {
if (
uiContext.sessionid == update.history.sessionid &&
uiContext.screenid == update.history.screenid
) {
this.inputModel.setHistoryInfo(update.history);
if (update.connect.sessions != null) {
this.sessionList.clear();
this.updateSessions(update.connect.sessions);
}
if (update.connect.remotes != null) {
this.remotes.clear();
this.updateRemotes(update.connect.remotes);
}
if (update.connect.activesessionid != null) {
this.updateActiveSession(update.connect.activesessionid);
}
if (update.connect.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands(update.connect.screennumrunningcommands);
}
if (update.connect.screenstatusindicators != null) {
this.updateScreenStatusIndicators(update.connect.screenstatusindicators);
}
this.sessionListLoaded.set(true);
this.remotesLoaded.set(true);
} else if (update.screen != null) {
this.updateScreens([update.screen]);
} else if (update.session != null) {
this.updateSessions([update.session]);
} else if (update.activesessionid != null) {
this.updateActiveSession(update.activesessionid);
} else if (update.line != null) {
this.addLineCmd(update.line.line, update.line.cmd, interactive);
} else if (update.cmd != null) {
this.updateCmd(update.cmd);
} else if (update.screenlines != null) {
this.updateScreenLines(update.screenlines, false);
} else if (update.remote != null) {
this.updateRemotes([update.remote]);
// This code's purpose is to show view remote connection modal when a new connection is added
if (!showedRemotesModal && this.remotesModel.recentConnAddedState.get()) {
showedRemotesModal = true;
this.remotesModel.openReadModal(update.remote.remoteid);
}
} else if (update.mainview != null) {
switch (update.mainview.mainview) {
case "session":
this.activeMainView.set("session");
break;
case "history":
if (update.mainview.historyview != null) {
this.historyViewModel.showHistoryView(update.mainview.historyview);
} else {
console.warn("invalid historyview in update:", update.mainview);
}
break;
case "bookmarks":
if (update.mainview.bookmarksview != null) {
this.bookmarksModel.showBookmarksView(
update.mainview.bookmarksview?.bookmarks ?? [],
update.mainview.bookmarksview?.selectedbookmark
);
} else {
console.warn("invalid bookmarksview in update:", update.mainview);
}
break;
case "plugins":
this.pluginsModel.showPluginsView();
break;
default:
console.warn("invalid mainview in update:", update.mainview);
}
} else if (update.bookmarks != null) {
if (update.bookmarks.bookmarks != null) {
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
}
} else if (update.clientdata != null) {
this.setClientData(update.clientdata);
} else if (update.cmdline != null) {
this.inputModel.updateCmdLine(update.cmdline);
} else if (update.openaicmdinfochat != null) {
this.inputModel.setOpenAICmdInfoChat(update.openaicmdinfochat);
} else if (update.screenstatusindicator != null) {
this.updateScreenStatusIndicators([update.screenstatusindicator]);
} else if (update.screennumrunningcommands != null) {
this.updateScreenNumRunningCommands([update.screennumrunningcommands]);
} else if (update.userinputrequest != null) {
const userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (interactive) {
if (update.info != null) {
const info: InfoType = update.info;
this.inputModel.flashInfoMsg(info, info.timeoutms);
} else if (update.remoteview != null) {
const rview: RemoteViewType = update.remoteview;
if (rview.remoteedit != null) {
this.remotesModel.openEditModal({ ...rview.remoteedit });
}
} else if (update.alertmessage != null) {
const alertMessage: AlertMessageType = update.alertmessage;
this.showAlert(alertMessage);
} else if (update.history != null) {
if (
uiContext.sessionid == update.history.sessionid &&
uiContext.screenid == update.history.screenid
) {
this.inputModel.setHistoryInfo(update.history);
}
} else if (this.isDev) {
console.log("did not match update", update);
}
} else if (this.isDev) {
console.log("did not match update", update);
}
} else if (this.isDev) {
console.log("did not match update", update);
});
// Check if the active session or screen has changed, and if so, watch the new screen
const [newActiveSessionId, newActiveScreenId] = this.getActiveIds();
if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) {
this.activeMainView.set("session");
this.deactivateScreenLines();
this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
}
});
} else {
console.warn("unknown update", genUpdate);
}
}
updateRemotes(remotes: RemoteType[]): void {
@@ -1064,11 +1061,13 @@ class Model {
this.handleCmdRestart(cmd);
}
isInfoUpdate(update: UpdateMessage): boolean {
if (update == null || "ptydata64" in update) {
isInfoUpdate(update: UpdatePacket): boolean {
if (update.type == "model") {
const modelUpdate = update as ModelUpdatePacket;
return modelUpdate.data.some((u) => u.info != null || u.history != null);
} else {
return false;
}
return update.some((u) => u.info != null || u.history != null);
}
getClientDataLoop(loopNum: number): void {
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More