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.
This commit is contained in:
Sylvia Crowe 2024-02-13 16:30:07 -08:00
parent 6bd60e8330
commit 5abff8075b
142 changed files with 2437 additions and 2502 deletions

View File

@ -7,10 +7,9 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { ContextMenuOpts } from "../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "../models"; import { GlobalModel } from "@/models";
import { isBlank } from "../util/util"; import { isBlank } from "@/util/util";
import { WorkspaceView } from "./workspace/workspaceview"; import { WorkspaceView } from "./workspace/workspaceview";
import { PluginsView } from "./pluginsview/pluginsview"; import { PluginsView } from "./pluginsview/pluginsview";
import { BookmarksView } from "./bookmarks/bookmarks"; import { BookmarksView } from "./bookmarks/bookmarks";
@ -25,8 +24,6 @@ import "./app.less";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class App extends React.Component<{}, {}> { class App extends React.Component<{}, {}> {
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" }); dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });

View File

@ -47,3 +47,13 @@ export const TabIcons = [
export const VERSION = __WAVETERM_VERSION__; export const VERSION = __WAVETERM_VERSION__;
// @ts-ignore // @ts-ignore
export const BUILD = __WAVETERM_BUILD__; export const BUILD = __WAVETERM_BUILD__;
/**
* Levels for the screen status indicator
*/
export enum StatusIndicatorLevel {
None = 0,
Output = 1,
Success = 2,
Error = 3,
}

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.bookmarks-view { .bookmarks-view {
background-color: @background-session; background-color: @background-session;

View File

@ -7,15 +7,14 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import type { BookmarkType } from "../../types/types"; import { GlobalModel } from "@/models";
import { GlobalModel } from "../../models"; import { CmdStrCode, Markdown } from "@/common/elements";
import { CmdStrCode, Markdown } from "../common/elements";
import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg"; import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg";
import { ReactComponent as CopyIcon } from "../assets/icons/favourites/copy.svg"; import { ReactComponent as CopyIcon } from "@/assets/icons/favourites/copy.svg";
import { ReactComponent as PenIcon } from "../assets/icons/favourites/pen.svg"; import { ReactComponent as PenIcon } from "@/assets/icons/favourites/pen.svg";
import { ReactComponent as TrashIcon } from "../assets/icons/favourites/trash.svg"; import { ReactComponent as TrashIcon } from "@/assets/icons/favourites/trash.svg";
import { ReactComponent as FavoritesIcon } from "../assets/icons/favourites.svg"; import { ReactComponent as FavoritesIcon } from "@/assets/icons/favourites.svg";
import "./bookmarks.less"; import "./bookmarks.less";

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.clientsettings-view { .clientsettings-view {
background-color: @background-session; background-color: @background-session;

View File

@ -6,20 +6,19 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "../../models"; import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "../common/elements"; import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/common/elements";
import * as types from "../../types/types"; import { commandRtnHandler, isBlank } from "@/util/util";
import { commandRtnHandler, isBlank } from "../../util/util"; import * as appconst from "@/app/appconst";
import * as appconst from "../appconst";
import "./clientsettings.less"; import "./clientsettings.less";
@mobxReact.observer @mobxReact.observer
class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> { class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> {
fontSizeDropdownActive: types.OV<boolean> = mobx.observable.box(false, { fontSizeDropdownActive: OV<boolean> = mobx.observable.box(false, {
name: "clientSettings-fontSizeDropdownActive", name: "clientSettings-fontSizeDropdownActive",
}); });
errorMessage: types.OV<string> = mobx.observable.box(null, { name: "ClientSettings-errorMessage" }); errorMessage: OV<string> = mobx.observable.box(null, { name: "ClientSettings-errorMessage" });
@boundMethod @boundMethod
dismissError(): void { dismissError(): void {
@ -48,7 +47,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
@boundMethod @boundMethod
handleChangeTelemetry(val: boolean): void { handleChangeTelemetry(val: boolean): void {
let prtn: Promise<types.CommandRtnType> = null; let prtn: Promise<CommandRtnType> = null;
if (val) { if (val) {
prtn = GlobalCommandRunner.telemetryOn(false); prtn = GlobalCommandRunner.telemetryOn(false);
} else { } else {
@ -59,7 +58,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
@boundMethod @boundMethod
handleChangeReleaseCheck(val: boolean): void { handleChangeReleaseCheck(val: boolean): void {
let prtn: Promise<types.CommandRtnType> = null; let prtn: Promise<CommandRtnType> = null;
if (val) { if (val) {
prtn = GlobalCommandRunner.releaseCheckAutoOn(false); prtn = GlobalCommandRunner.releaseCheckAutoOn(false);
} else { } else {
@ -112,7 +111,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
return null; return null;
} }
let cdata: types.ClientDataType = GlobalModel.clientData.get(); let cdata: ClientDataType = GlobalModel.clientData.get();
let openAIOpts = cdata.openaiopts ?? {}; let openAIOpts = cdata.openaiopts ?? {};
let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********"; let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
let maxTokensStr = String( let maxTokensStr = String(

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-button { .wave-button {
background: none; background: none;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.checkbox { .checkbox {
display: flex; display: flex;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.cmdstr-code { .cmdstr-code {
position: relative; position: relative;

View File

@ -6,8 +6,8 @@ import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { ReactComponent as CheckIcon } from "../../assets/icons/line/check.svg"; import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg";
import { ReactComponent as CopyIcon } from "../../assets/icons/history/copy.svg"; import { ReactComponent as CopyIcon } from "@/assets/icons/history/copy.svg";
import "./cmdstrcode.less"; import "./cmdstrcode.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-dropdown { .wave-dropdown {
position: relative; position: relative;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.inline-edit { .inline-edit {
.icon { .icon {

View File

@ -7,12 +7,10 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../../util/keyutil"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import "./inlinesettingstextedit.less"; import "./inlinesettingstextedit.less";
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class InlineSettingsTextEdit extends React.Component< class InlineSettingsTextEdit extends React.Component<
{ {

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-input-decoration { .wave-input-decoration {
display: flex; display: flex;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.markdown { .markdown {
color: @term-white; color: @term-white;

View File

@ -6,7 +6,7 @@ import * as mobxReact from "mobx-react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import "./markdown.less"; import "./markdown.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-modal-container { .wave-modal-container {
position: fixed; position: fixed;

View File

@ -10,8 +10,6 @@ import { IconButton } from "./iconbutton";
import "./modal.less"; import "./modal.less";
type OV<V> = mobx.IObservableValue<V>;
interface ModalHeaderProps { interface ModalHeaderProps {
onClose?: () => void; onClose?: () => void;
title: string; title: string;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-password { .wave-password {
.wave-textfield-inner-eye { .wave-textfield-inner-eye {

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.sidebar-handle { .sidebar-handle {
position: absolute; position: absolute;

View File

@ -6,13 +6,11 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner } from "../../../models"; import { GlobalModel, GlobalCommandRunner } from "@/models";
import { MagicLayout } from "../../magiclayout"; import { MagicLayout } from "@/app/magiclayout";
import "./resizablesidebar.less"; import "./resizablesidebar.less";
type OV<V> = mobx.IObservableValue<V>;
interface ResizableSidebarProps { interface ResizableSidebarProps {
parentRef: React.RefObject<HTMLElement>; parentRef: React.RefObject<HTMLElement>;
position: "left" | "right"; position: "left" | "right";

View File

@ -6,8 +6,6 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class SettingsError extends React.Component<{ errorMessage: OV<string> }, {}> { class SettingsError extends React.Component<{ errorMessage: OV<string> }, {}> {
@boundMethod @boundMethod

View File

@ -1,8 +1,8 @@
// Copyright 2023, Command Line Inc. // Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import * as appconst from "../../appconst"; import * as appconst from "@/app/appconst";
function ShowWaveShellInstallPrompt(callbackFn: () => void) { function ShowWaveShellInstallPrompt(callbackFn: () => void) {
let message: string = ` let message: string = `

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-status-container { .wave-status-container {
display: flex; display: flex;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-textfield { .wave-textfield {
display: flex; display: flex;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.checkbox-toggle { .checkbox-toggle {
position: relative; position: relative;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.wave-tooltip { .wave-tooltip {
display: flex; display: flex;

View File

@ -1,5 +1,4 @@
import React, { Component, ReactNode } from "react"; import React, { Component, ReactNode } from "react";
import { RendererContext } from "../../../types/types";
import cn from "classnames"; import cn from "classnames";
interface ErrorBoundaryState { interface ErrorBoundaryState {

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.front-icon { .front-icon {
margin-right: 5px; margin-right: 5px;

View File

@ -1,12 +1,12 @@
import React from "react"; import React from "react";
import { StatusIndicatorLevel } from "../../../types/types";
import cn from "classnames"; import cn from "classnames";
import { ReactComponent as SpinnerIndicator } from "../../assets/icons/spinner-indicator.svg"; import { ReactComponent as SpinnerIndicator } from "@/assets/icons/spinner-indicator.svg";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import * as mobx from "mobx"; import * as mobx from "mobx";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as appconst from "@/app/appconst";
import { ReactComponent as RotateIconSvg } from "../../assets/icons/line/rotate.svg"; import { ReactComponent as RotateIconSvg } from "@/assets/icons/line/rotate.svg";
interface PositionalIconProps { interface PositionalIconProps {
children?: React.ReactNode; children?: React.ReactNode;
@ -131,7 +131,7 @@ interface StatusIndicatorProps {
/** /**
* The level of the status indicator. This will determine the color of the status indicator. * The level of the status indicator. This will determine the color of the status indicator.
*/ */
level: StatusIndicatorLevel; level: appconst.StatusIndicatorLevel;
className?: string; className?: string;
/** /**
* If true, a spinner will be shown around the status indicator. * If true, a spinner will be shown around the status indicator.
@ -191,16 +191,16 @@ export class StatusIndicator extends React.Component<StatusIndicatorProps> {
const { level, className, runningCommands } = this.props; const { level, className, runningCommands } = this.props;
const spinnerVisible = this.spinnerVisible.get(); const spinnerVisible = this.spinnerVisible.get();
let statusIndicator = null; let statusIndicator = null;
if (level != StatusIndicatorLevel.None || spinnerVisible) { if (level != appconst.StatusIndicatorLevel.None || spinnerVisible) {
let indicatorLevelClass = null; let indicatorLevelClass = null;
switch (level) { switch (level) {
case StatusIndicatorLevel.Output: case appconst.StatusIndicatorLevel.Output:
indicatorLevelClass = "output"; indicatorLevelClass = "output";
break; break;
case StatusIndicatorLevel.Success: case appconst.StatusIndicatorLevel.Success:
indicatorLevelClass = "success"; indicatorLevelClass = "success";
break; break;
case StatusIndicatorLevel.Error: case appconst.StatusIndicatorLevel.Error:
indicatorLevelClass = "error"; indicatorLevelClass = "error";
break; break;
} }

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.about-modal { .about-modal {
.wave-modal-content { .wave-modal-content {

View File

@ -5,12 +5,12 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { Modal, LinkButton } from "../elements"; import { Modal, LinkButton } from "@/elements";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import * as appconst from "../../appconst"; import * as appconst from "@/app/appconst";
import logo from "../../assets/waveterm-logo-with-bg.svg"; import logo from "@/assets/waveterm-logo-with-bg.svg";
import "./about.less"; import "./about.less";
@mobxReact.observer @mobxReact.observer

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.alert-modal { .alert-modal {
width: 500px; width: 500px;

View File

@ -5,8 +5,8 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { Markdown, Modal, Button, Checkbox } from "../elements"; import { Markdown, Modal, Button, Checkbox } from "@/elements";
import { GlobalModel, GlobalCommandRunner } from "../../../models"; import { GlobalModel, GlobalCommandRunner } from "@/models";
import "./alert.less"; import "./alert.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.clientstop-modal { .clientstop-modal {
.inner-content { .inner-content {

View File

@ -5,8 +5,8 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { Modal, Button } from "../elements"; import { Modal, Button } from "@/elements";
import "./clientstop.less"; import "./clientstop.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.crconn-modal { .crconn-modal {
width: 452px; width: 452px;

View File

@ -6,8 +6,7 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "../../../models"; import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import * as T from "../../../types/types";
import { import {
Modal, Modal,
TextField, TextField,
@ -17,13 +16,11 @@ import {
PasswordField, PasswordField,
Tooltip, Tooltip,
ShowWaveShellInstallPrompt, ShowWaveShellInstallPrompt,
} from "../elements"; } from "@/elements";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import "./createremoteconn.less"; import "./createremoteconn.less";
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class CreateRemoteConnModal extends React.Component<{}, {}> { class CreateRemoteConnModal extends React.Component<{}, {}> {
tempAlias: OV<string>; tempAlias: OV<string>;
@ -35,7 +32,7 @@ class CreateRemoteConnModal extends React.Component<{}, {}> {
tempKeyFile: OV<string>; tempKeyFile: OV<string>;
tempShellPref: OV<string>; tempShellPref: OV<string>;
errorStr: OV<string>; errorStr: OV<string>;
remoteEdit: T.RemoteEditType; remoteEdit: RemoteEditType;
model: RemotesModel; model: RemotesModel;
constructor(props: { remotesModel?: RemotesModel }) { constructor(props: { remotesModel?: RemotesModel }) {

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.disconnected-modal { .disconnected-modal {
.wave-modal-content { .wave-modal-content {

View File

@ -5,8 +5,8 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { Modal, Button } from "../elements"; import { Modal, Button } from "@/elements";
import "./disconnected.less"; import "./disconnected.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.erconn-modal { .erconn-modal {
width: 502px; width: 502px;

View File

@ -6,15 +6,12 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "../../../models"; import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import * as T from "../../../types/types"; import { Modal, TextField, InputDecoration, Dropdown, PasswordField, Tooltip } from "@/elements";
import { Modal, TextField, InputDecoration, Dropdown, PasswordField, Tooltip } from "../elements"; import * as util from "@/util/util";
import * as util from "../../../util/util";
import "./editremoteconn.less"; import "./editremoteconn.less";
type OV<V> = mobx.IObservableValue<V>;
const PasswordUnchangedSentinel = "--unchanged--"; const PasswordUnchangedSentinel = "--unchanged--";
@mobxReact.observer @mobxReact.observer
@ -42,11 +39,11 @@ class EditRemoteConnModal extends React.Component<{}, {}> {
return this.model.selectedRemoteId.get(); return this.model.selectedRemoteId.get();
} }
get selectedRemote(): T.RemoteType { get selectedRemote(): RemoteType {
return GlobalModel.getRemote(this.selectedRemoteId); return GlobalModel.getRemote(this.selectedRemoteId);
} }
get remoteEdit(): T.RemoteEditType { get remoteEdit(): RemoteEditType {
return this.model.remoteEdit.get(); return this.model.remoteEdit.get();
} }

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.line-settings-modal { .line-settings-modal {
width: 640px; width: 640px;

View File

@ -5,16 +5,13 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel, GlobalCommandRunner } from "../../../models"; import { GlobalModel, GlobalCommandRunner } from "@/models";
import { SettingsError, Modal, Dropdown } from "../elements"; import { SettingsError, Modal, Dropdown } from "@/elements";
import { LineType, RendererPluginType } from "../../../types/types"; import { PluginModel } from "@/plugins/plugins";
import { PluginModel } from "../../../plugins/plugins"; import { commandRtnHandler } from "@/util/util";
import { commandRtnHandler } from "../../../util/util";
import "./linesettings.less"; import "./linesettings.less";
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class LineSettingsModal extends React.Component<{}, {}> { class LineSettingsModal extends React.Component<{}, {}> {
rendererDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "lineSettings-rendererDropdownActive" }); rendererDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "lineSettings-rendererDropdownActive" });

View File

@ -3,7 +3,7 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { TosModal } from "./tos"; import { TosModal } from "./tos";
@mobxReact.observer @mobxReact.observer

View File

@ -13,8 +13,8 @@ import {
ScreenSettingsModal, ScreenSettingsModal,
LineSettingsModal, LineSettingsModal,
UserInputModal, UserInputModal,
} from "../modals"; } from "@/modals";
import * as constants from "../../appconst"; import * as constants from "@/app/appconst";
const modalsRegistry: { [key: string]: React.ComponentType } = { const modalsRegistry: { [key: string]: React.ComponentType } = {
[constants.ABOUT]: AboutModal, [constants.ABOUT]: AboutModal,

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.screen-settings-modal { .screen-settings-modal {
width: 640px; width: 640px;

View File

@ -7,20 +7,16 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, Screen } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Dropdown, Tooltip } from "../elements"; import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Dropdown, Tooltip } from "@/elements";
import { RemoteType } from "../../../types/types"; import * as util from "@/util/util";
import * as util from "../../../util/util"; import { ReactComponent as SquareIcon } from "@/assets/icons/tab/square.svg";
import { commandRtnHandler } from "../../../util/util"; import { ReactComponent as GlobeIcon } from "@/assets/icons/globe.svg";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; import { ReactComponent as StatusCircleIcon } from "@/assets/icons/statuscircle.svg";
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg"; import * as appconst from "@/app/appconst";
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg";
import * as appconst from "../../appconst";
import "./screensettings.less"; import "./screensettings.less";
type OV<V> = mobx.IObservableValue<V>;
const ScreenDeleteMessage = ` const ScreenDeleteMessage = `
Are you sure you want to delete this tab? Are you sure you want to delete this tab?
@ -98,7 +94,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false); let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { tabcolor: color }, false);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -119,7 +115,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenArchive(this.screenId, val); let prtn = GlobalCommandRunner.screenArchive(this.screenId, val);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -137,7 +133,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val); let prtn = GlobalCommandRunner.screenWebShare(this.screen.screenId, val);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
}); });
} }
@ -170,7 +166,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false); let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { name: val }, false);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -182,7 +178,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false); let prtn = GlobalCommandRunner.screenSetSettings(this.screenId, { sharename: val }, false);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -209,7 +205,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.screenDelete(this.screenId, false); let prtn = GlobalCommandRunner.screenDelete(this.screenId, false);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
GlobalModel.modalsModel.popModal(); GlobalModel.modalsModel.popModal();
}); });
} }

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.session-settings-modal { .session-settings-modal {
width: 640px; width: 640px;

View File

@ -5,15 +5,12 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel, GlobalCommandRunner, Session } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Session } from "@/models";
import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Tooltip } from "../elements"; import { Toggle, InlineSettingsTextEdit, SettingsError, Modal, Tooltip } from "@/elements";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import { commandRtnHandler } from "../../../util/util";
import "./sessionsettings.less"; import "./sessionsettings.less";
type OV<V> = mobx.IObservableValue<V>;
const SessionDeleteMessage = ` const SessionDeleteMessage = `
Are you sure you want to delete this workspace? Are you sure you want to delete this workspace?
@ -52,7 +49,7 @@ class SessionSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.sessionSetSettings(this.sessionId, { name: newVal }, false); let prtn = GlobalCommandRunner.sessionSetSettings(this.sessionId, { name: newVal }, false);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -64,7 +61,7 @@ class SessionSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.sessionArchive(this.sessionId, val); let prtn = GlobalCommandRunner.sessionArchive(this.sessionId, val);
commandRtnHandler(prtn, this.errorMessage); util.commandRtnHandler(prtn, this.errorMessage);
} }
@boundMethod @boundMethod
@ -76,7 +73,7 @@ class SessionSettingsModal extends React.Component<{}, {}> {
return; return;
} }
let prtn = GlobalCommandRunner.sessionDelete(this.sessionId); let prtn = GlobalCommandRunner.sessionDelete(this.sessionId);
commandRtnHandler(prtn, this.errorMessage, () => GlobalModel.modalsModel.popModal()); util.commandRtnHandler(prtn, this.errorMessage, () => GlobalModel.modalsModel.popModal());
}); });
} }

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.tabswitcher-modal { .tabswitcher-modal {
width: 452px; width: 452px;

View File

@ -5,18 +5,20 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner } from "../../../models"; import { GlobalModel, GlobalCommandRunner } from "@/models";
import { Modal, TextField, InputDecoration, Tooltip } from "../elements"; import { Modal, TextField, InputDecoration, Tooltip } from "@/elements";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import { Screen } from "../../../models"; import { Screen } from "@/models";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; import { ReactComponent as SquareIcon } from "@/assets/icons/tab/square.svg";
import "./tabswitcher.less"; import "./tabswitcher.less";
type OV<V> = mobx.IObservableValue<V>; type ViewDataType = {
type OArr<V> = mobx.IObservableArray<V>; label: string;
value: string;
};
type SwitcherDataType = { type SwitcherDataType = {
sessionId: string; sessionId: string;
@ -27,9 +29,25 @@ type SwitcherDataType = {
screenName: string; screenName: string;
icon: string; icon: string;
color: string; color: string;
viewData?: ViewDataType;
}; };
const MaxOptionsToDisplay = 100; const MaxOptionsToDisplay = 100;
const additionalOptions = [
{ label: "Connections", value: "connections" },
{ label: "History", value: "history" },
{ label: "Settings", value: "clientsettings" },
].map((item, index) => ({
sessionId: `additional-${index}`,
sessionName: "",
sessionIdx: -1,
screenId: `additional-${index}`,
screenIdx: -1,
screenName: "",
icon: "",
color: "",
viewData: item,
}));
@mobxReact.observer @mobxReact.observer
class TabSwitcherModal extends React.Component<{}, {}> { class TabSwitcherModal extends React.Component<{}, {}> {
@ -47,8 +65,8 @@ class TabSwitcherModal extends React.Component<{}, {}> {
componentDidMount() { componentDidMount() {
this.activeSessionIdx = GlobalModel.getActiveSession().sessionIdx.get(); this.activeSessionIdx = GlobalModel.getActiveSession().sessionIdx.get();
let oSessions = GlobalModel.sessionList; const oSessions = GlobalModel.sessionList;
let oScreens = GlobalModel.screenMap; const oScreens = GlobalModel.screenMap;
oScreens.forEach((oScreen) => { oScreens.forEach((oScreen) => {
if (oScreen == null) { if (oScreen == null) {
return; return;
@ -57,13 +75,13 @@ class TabSwitcherModal extends React.Component<{}, {}> {
return; return;
} }
// Find the matching session in the observable array // Find the matching session in the observable array
let foundSession = oSessions.find((s) => { const foundSession = oSessions.find((s) => {
return s.sessionId == oScreen.sessionId && !s.archived.get(); return s.sessionId == oScreen.sessionId && !s.archived.get();
}); });
if (!foundSession) { if (!foundSession) {
return; return;
} }
let data: SwitcherDataType = { const data: SwitcherDataType = {
sessionName: foundSession.name.get(), sessionName: foundSession.name.get(),
sessionId: foundSession.sessionId, sessionId: foundSession.sessionId,
sessionIdx: foundSession.sessionIdx.get(), sessionIdx: foundSession.sessionIdx.get(),
@ -88,11 +106,11 @@ class TabSwitcherModal extends React.Component<{}, {}> {
} }
componentDidUpdate() { componentDidUpdate() {
let currFocusedIdx = this.focusedIdx.get(); const currFocusedIdx = this.focusedIdx.get();
// Check if selectedIdx has changed // Check if selectedIdx has changed
if (currFocusedIdx !== this.prevFocusedIdx) { if (currFocusedIdx !== this.prevFocusedIdx) {
let optionElement = this.optionRefs[currFocusedIdx]?.current; const optionElement = this.optionRefs[currFocusedIdx]?.current;
if (optionElement) { if (optionElement) {
optionElement.scrollIntoView({ block: "nearest" }); optionElement.scrollIntoView({ block: "nearest" });
@ -109,7 +127,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@boundMethod @boundMethod
getTabIcon(screen: Screen): string { getTabIcon(screen: Screen): string {
let tabIcon = "default"; let tabIcon = "default";
let screenOpts = screen.opts.get(); const screenOpts = screen.opts.get();
if (screenOpts != null && !util.isBlank(screenOpts.tabicon)) { if (screenOpts != null && !util.isBlank(screenOpts.tabicon)) {
tabIcon = screenOpts.tabicon; tabIcon = screenOpts.tabicon;
} }
@ -119,7 +137,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@boundMethod @boundMethod
getTabColor(screen: Screen): string { getTabColor(screen: Screen): string {
let tabColor = "default"; let tabColor = "default";
let screenOpts = screen.opts.get(); const screenOpts = screen.opts.get();
if (screenOpts != null && !util.isBlank(screenOpts.tabcolor)) { if (screenOpts != null && !util.isBlank(screenOpts.tabcolor)) {
tabColor = screenOpts.tabcolor; tabColor = screenOpts.tabcolor;
} }
@ -132,7 +150,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
this.closeModal(); this.closeModal();
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") { } else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
e.preventDefault(); e.preventDefault();
let newIndex = this.calculateNewIndex(e.key === "ArrowUp"); const newIndex = this.calculateNewIndex(e.key === "ArrowUp");
this.setFocusedIndex(newIndex); this.setFocusedIndex(newIndex);
} else if (e.key === "Enter") { } else if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
@ -142,7 +160,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@boundMethod @boundMethod
calculateNewIndex(isUpKey) { calculateNewIndex(isUpKey) {
let currentIndex = this.focusedIdx.get(); const currentIndex = this.focusedIdx.get();
if (isUpKey) { if (isUpKey) {
return Math.max(currentIndex - 1, 0); return Math.max(currentIndex - 1, 0);
} else { } else {
@ -165,6 +183,11 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@boundMethod @boundMethod
handleSelect(index: number): void { handleSelect(index: number): void {
const selectedOption = this.sOptions[index]; const selectedOption = this.sOptions[index];
if (selectedOption.sessionIdx === -1) {
GlobalCommandRunner.switchView(selectedOption.viewData.value);
this.closeModal();
return;
}
if (selectedOption) { if (selectedOption) {
GlobalCommandRunner.switchScreen(selectedOption.screenId, selectedOption.sessionId); GlobalCommandRunner.switchScreen(selectedOption.screenId, selectedOption.sessionId);
this.closeModal(); this.closeModal();
@ -192,27 +215,28 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@mobx.computed @mobx.computed
@boundMethod @boundMethod
filterOptions(searchInput: string): SwitcherDataType[] { filterOptions(searchInput: string): SwitcherDataType[] {
let filteredScreens = []; const searchLower = searchInput.toLowerCase();
for (let i = 0; i < this.options.length; i++) {
let tab = this.options[i];
let match = false;
let filteredScreens = this.options.filter((tab) => {
if (searchInput.includes("/")) { if (searchInput.includes("/")) {
let [sessionFilter, screenFilter] = searchInput.split("/").map((s) => s.trim().toLowerCase()); const [sessionFilter, screenFilter] = searchInput.split("/").map((s) => s.trim().toLowerCase());
match = return (
tab.sessionName.toLowerCase().includes(sessionFilter) && tab.sessionName.toLowerCase().includes(sessionFilter) &&
tab.screenName.toLowerCase().includes(screenFilter); tab.screenName.toLowerCase().includes(screenFilter)
);
} else { } else {
match = return (
tab.sessionName.toLowerCase().includes(searchInput) || tab.sessionName.toLowerCase().includes(searchLower) ||
tab.screenName.toLowerCase().includes(searchInput); tab.screenName.toLowerCase().includes(searchLower)
);
} }
});
// Add tab to filtered list if it matches the criteria if (searchLower.length > 0) {
if (match) { const additionalFiltered = additionalOptions.filter((item) =>
filteredScreens.push(tab); item.viewData?.label.toLowerCase().includes(searchLower)
} );
filteredScreens = filteredScreens.concat(additionalFiltered);
} }
return filteredScreens; return filteredScreens;
@ -221,9 +245,10 @@ class TabSwitcherModal extends React.Component<{}, {}> {
@mobx.computed @mobx.computed
@boundMethod @boundMethod
sortOptions(options: SwitcherDataType[]): SwitcherDataType[] { sortOptions(options: SwitcherDataType[]): SwitcherDataType[] {
return options.sort((a, b) => { const mainOptions = options.filter((o) => o.sessionIdx !== -1);
let aInCurrentSession = a.sessionIdx === this.activeSessionIdx; mainOptions.sort((a, b) => {
let bInCurrentSession = b.sessionIdx === this.activeSessionIdx; const aInCurrentSession = a.sessionIdx === this.activeSessionIdx;
const bInCurrentSession = b.sessionIdx === this.activeSessionIdx;
// Tabs in the current session are sorted by screenIdx // Tabs in the current session are sorted by screenIdx
if (aInCurrentSession && bInCurrentSession) { if (aInCurrentSession && bInCurrentSession) {
@ -246,11 +271,16 @@ class TabSwitcherModal extends React.Component<{}, {}> {
} }
} }
}); });
const additionalOptions = options.filter((o) => o.sessionIdx === -1);
additionalOptions.sort((a, b) => a.viewData?.label.localeCompare(b.viewData?.label));
return mainOptions.concat(additionalOptions);
} }
@boundMethod @boundMethod
renderIcon(option: SwitcherDataType): React.ReactNode { renderIcon(option: SwitcherDataType): React.ReactNode {
let tabIcon = option.icon; const tabIcon = option.icon;
if (tabIcon === "default" || tabIcon === "square") { if (tabIcon === "default" || tabIcon === "square") {
return <SquareIcon className="left-icon" />; return <SquareIcon className="left-icon" />;
} }
@ -271,10 +301,15 @@ class TabSwitcherModal extends React.Component<{}, {}> {
})} })}
onClick={() => this.handleSelect(index)} onClick={() => this.handleSelect(index)}
> >
<If condition={option.sessionIdx != -1}>
<div className={cn("icon", "color-" + option.color)}>{this.renderIcon(option)}</div> <div className={cn("icon", "color-" + option.color)}>{this.renderIcon(option)}</div>
<div className="tabname"> <div className="tabname">
#{option.sessionName} / {option.screenName} #{option.sessionName} / {option.screenName}
</div> </div>
</If>
<If condition={option.sessionIdx == -1}>
<div className="tabname">{option.viewData?.label}</div>
</If>
</div> </div>
); );
} }
@ -293,13 +328,13 @@ class TabSwitcherModal extends React.Component<{}, {}> {
decoration={{ decoration={{
startDecoration: ( startDecoration: (
<InputDecoration position="start"> <InputDecoration position="start">
<div className="tabswitcher-search-prefix">Switch to Tab:</div> <div className="tabswitcher-search-prefix">Go to:</div>
</InputDecoration> </InputDecoration>
), ),
endDecoration: ( endDecoration: (
<InputDecoration> <InputDecoration>
<Tooltip <Tooltip
message={`Type to filter workspaces and tabs.`} message={`Type to filter workspaces, tabs and views.`}
icon={<i className="fa-sharp fa-regular fa-circle-question" />} icon={<i className="fa-sharp fa-regular fa-circle-question" />}
> >
<i className="fa-sharp fa-regular fa-circle-question" /> <i className="fa-sharp fa-regular fa-circle-question" />

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.tos-modal { .tos-modal {
width: 640px; width: 640px;

View File

@ -4,14 +4,13 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel, GlobalCommandRunner } from "../../../models"; import { GlobalModel, GlobalCommandRunner } from "@/models";
import { Toggle, Modal, Button } from "../elements"; import { Toggle, Modal, Button } from "@/elements";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import { ClientDataType } from "../../../types/types";
import shield from "../../assets/icons/shield_check.svg"; import shield from "@/assets/icons/shield_check.svg";
import help from "../../assets/icons/help_filled.svg"; import help from "@/assets/icons/help_filled.svg";
import github from "../../assets/icons/github.svg"; import github from "@/assets/icons/github.svg";
import "./tos.less"; import "./tos.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.userinput-modal { .userinput-modal {
width: 500px; width: 500px;

View File

@ -1,8 +1,7 @@
import * as React from "react"; import * as React from "react";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { Choose, When, If } from "tsx-control-statements/components"; import { Choose, When, If } from "tsx-control-statements/components";
import { Modal, PasswordField, Markdown } from "../elements"; import { Modal, PasswordField, Markdown } from "@/elements";
import { UserInputRequest } from "../../../types/types";
import "./userinput.less"; import "./userinput.less";

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.rconndetail-modal { .rconndetail-modal {
width: 631px; width: 631px;
@ -54,6 +54,8 @@
} }
.remote-detail { .remote-detail {
width: 100%;
.settings-field { .settings-field {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -91,8 +93,9 @@
} }
.terminal-wrapper { .terminal-wrapper {
width: 100%;
margin-top: 5px; margin-top: 5px;
overflow-x: auto;
overflow-y: hidden;
.terminal-connectelem { .terminal-connectelem {
height: 163px !important; // Needed to override plugin height height: 163px !important; // Needed to override plugin height
@ -111,7 +114,6 @@
.xterm-screen { .xterm-screen {
padding: 10px; padding: 10px;
width: 541px !important; // Needed to override plugin width
} }
} }
} }

View File

@ -7,17 +7,14 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, RemotesModel } from "../../../models"; import { GlobalModel, GlobalCommandRunner, RemotesModel } from "@/models";
import * as T from "../../../types/types"; import { Modal, Tooltip, Button, Status } from "@/elements";
import { Modal, Tooltip, Button, Status } from "../elements"; import * as util from "@/util/util";
import * as util from "../../../util/util"; import * as textmeasure from "@/util/textmeasure";
import * as textmeasure from "../../../util/textmeasure"; import * as appconst from "@/app/appconst";
import "./viewremoteconndetail.less"; import "./viewremoteconndetail.less";
const RemotePtyRows = 9;
const RemotePtyCols = 80;
@mobxReact.observer @mobxReact.observer
class ViewRemoteConnDetailModal extends React.Component<{}, {}> { class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
termRef: React.RefObject<any> = React.createRef(); termRef: React.RefObject<any> = React.createRef();
@ -29,7 +26,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
} }
@mobx.computed @mobx.computed
getSelectedRemote(): T.RemoteType { getSelectedRemote(): RemoteType {
const selectedRemoteId = this.model.selectedRemoteId.get(); const selectedRemoteId = this.model.selectedRemoteId.get();
return GlobalModel.getRemote(selectedRemoteId); return GlobalModel.getRemote(selectedRemoteId);
} }
@ -60,7 +57,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
} }
} }
getRemoteTypeStr(remote: T.RemoteType): string { getRemoteTypeStr(remote: RemoteType): string {
if (!util.isBlank(remote.uname)) { if (!util.isBlank(remote.uname)) {
let unameStr = remote.uname; let unameStr = remote.uname;
unameStr = unameStr.replace("|", ", "); unameStr = unameStr.replace("|", ", ");
@ -138,7 +135,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
this.model.setRecentConnAdded(false); this.model.setRecentConnAdded(false);
} }
renderInstallStatus(remote: T.RemoteType): any { renderInstallStatus(remote: RemoteType): any {
let statusStr: string = null; let statusStr: string = null;
if (remote.installstatus == "disconnected") { if (remote.installstatus == "disconnected") {
if (remote.needsmshellupgrade) { if (remote.needsmshellupgrade) {
@ -162,7 +159,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
); );
} }
renderHeaderBtns(remote: T.RemoteType): React.ReactNode { renderHeaderBtns(remote: RemoteType): React.ReactNode {
let buttons: React.ReactNode[] = []; let buttons: React.ReactNode[] = [];
const disconnectButton = ( const disconnectButton = (
<Button theme="secondary" onClick={() => this.disconnectRemote(remote.remoteid)}> <Button theme="secondary" onClick={() => this.disconnectRemote(remote.remoteid)}>
@ -256,7 +253,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
); );
} }
getMessage(remote: T.RemoteType): string { getMessage(remote: RemoteType): string {
let message = ""; let message = "";
if (remote.status == "connected") { if (remote.status == "connected") {
message = "Connected and ready to run commands."; message = "Connected and ready to run commands.";
@ -295,7 +292,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
let model = this.model; let model = this.model;
let isTermFocused = this.model.remoteTermWrapFocus.get(); let isTermFocused = this.model.remoteTermWrapFocus.get();
let termFontSize = GlobalModel.termFontSize.get(); let termFontSize = GlobalModel.termFontSize.get();
let termWidth = textmeasure.termWidthFromCols(RemotePtyCols, termFontSize); let termWidth = textmeasure.termWidthFromCols(appconst.RemotePtyCols, termFontSize);
let remoteAliasText = util.isBlank(remote.remotealias) ? "(none)" : remote.remotealias; let remoteAliasText = util.isBlank(remote.remotealias) ? "(none)" : remote.remotealias;
let selectedRemoteStatus = this.getSelectedRemote().status; let selectedRemoteStatus = this.getSelectedRemote().status;
@ -373,7 +370,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
ref={this.termRef} ref={this.termRef}
data-remoteid={remote.remoteid} data-remoteid={remote.remoteid}
style={{ style={{
height: textmeasure.termHeightFromRows(RemotePtyRows, termFontSize), height: textmeasure.termHeightFromRows(appconst.RemotePtyRows, termFontSize),
width: termWidth, width: termWidth,
}} }}
></div> ></div>
@ -397,7 +394,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
} }
} }
function getImportTooltip(remote: T.RemoteType): React.ReactElement<any, any> { function getImportTooltip(remote: RemoteType): React.ReactElement<any, any> {
if (remote.sshconfigsrc == "sshconfig-import") { if (remote.sshconfigsrc == "sshconfig-import") {
return ( return (
<Tooltip <Tooltip

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.term-prompt { .term-prompt {
font-weight: 300; font-weight: 300;

View File

@ -3,42 +3,17 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import type {
LineType,
RemoteType,
RemotePtrType,
LineHeightChangeCallbackType,
LineContainerType,
} from "../../../types/types";
import cn from "classnames"; import cn from "classnames";
import { isBlank } from "../../../util/util"; import { isBlank } from "@/util/util";
import { ReactComponent as FolderIcon } from "../../assets/icons/folder.svg"; import { ReactComponent as FolderIcon } from "@/assets/icons/folder.svg";
import "./prompt.less"; import "./prompt.less";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
type OArr<V> = mobx.IObservableArray<V>;
type OMap<K, V> = mobx.ObservableMap<K, V>;
type RendererComponentProps = {
screen: LineContainerType;
line: LineType;
width: number;
staticRender: boolean;
visible: OV<boolean>;
onHeightChange: LineHeightChangeCallbackType;
collapsed: boolean;
};
type RendererComponentType = {
new (props: RendererComponentProps): React.Component<RendererComponentProps, {}>;
};
function makeFullRemoteRef(ownerName: string, remoteRef: string, name: string): string { function makeFullRemoteRef(ownerName: string, remoteRef: string, name: string): string {
if (isBlank(ownerName) && isBlank(name)) { if (isBlank(ownerName) && isBlank(name)) {
return remoteRef; return remoteRef;

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.connections-view { .connections-view {
.no-items { .no-items {

View File

@ -7,16 +7,12 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, RemotesModel, GlobalCommandRunner } from "../../models"; import { GlobalModel, RemotesModel, GlobalCommandRunner } from "@/models";
import { Button, Status, ShowWaveShellInstallPrompt } from "../common/elements"; import { Button, Status, ShowWaveShellInstallPrompt } from "@/common/elements";
import * as T from "../../types/types"; import * as util from "@/util/util";
import * as util from "../../util/util";
import * as appconst from "../appconst";
import "./connections.less"; import "./connections.less";
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class ConnectionsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> { class ConnectionsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> {
tableRef: React.RefObject<any> = React.createRef(); tableRef: React.RefObject<any> = React.createRef();
@ -54,13 +50,13 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
} }
@boundMethod @boundMethod
getName(item: T.RemoteType) { getName(item: RemoteType) {
const { remotealias, remotecanonicalname } = item; const { remotealias, remotecanonicalname } = item;
return remotealias ? `${remotealias} [${remotecanonicalname}]` : remotecanonicalname; return remotealias ? `${remotealias} [${remotecanonicalname}]` : remotecanonicalname;
} }
@boundMethod @boundMethod
getImportSymbol(item: T.RemoteType): React.ReactElement<any, any> { getImportSymbol(item: RemoteType): React.ReactElement<any, any> {
const { sshconfigsrc } = item; const { sshconfigsrc } = item;
if (sshconfigsrc == "sshconfig-import") { if (sshconfigsrc == "sshconfig-import") {
return <i title="Connection Imported from SSH Config" className="fa-sharp fa-solid fa-file-import" />; return <i title="Connection Imported from SSH Config" className="fa-sharp fa-solid fa-file-import" />;
@ -131,7 +127,7 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
} }
let items = util.sortAndFilterRemotes(GlobalModel.remotes.slice()); let items = util.sortAndFilterRemotes(GlobalModel.remotes.slice());
let item: T.RemoteType = null; let item: RemoteType = null;
return ( return (
<div className={cn("view connections-view")}> <div className={cn("view connections-view")}>

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.history-view { .history-view {
background-color: @background-session; background-color: @background-session;
@ -14,7 +14,7 @@
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #3B3F3A; border: 1px solid #3b3f3a;
background: rgba(213, 254, 175, 0.03); background: rgba(213, 254, 175, 0.03);
} }
@ -30,7 +30,7 @@
height: 16px; height: 16px;
fill: rgba(213, 254, 175, 0.03); fill: rgba(213, 254, 175, 0.03);
stroke-width: 1px; stroke-width: 1px;
stroke: #3B3F3A; stroke: #3b3f3a;
} }
} }
@ -264,6 +264,7 @@
margin: 0px 10px 10px 10px; margin: 0px 10px 10px 10px;
table-layout: fixed; table-layout: fixed;
border-top: 2px solid #ccc; border-top: 2px solid #ccc;
width: calc(100% - 20px);
tr.active-history-item { tr.active-history-item {
td { td {
@ -309,9 +310,8 @@
tr.history-item { tr.history-item {
padding: 0 10px 0 10px; padding: 0 10px 0 10px;
display: flex; display: flex;
border-bottom: 1px solid rgba(250, 250, 250, 0.10); border-bottom: 1px solid rgba(250, 250, 250, 0.1);
align-items: center; align-items: center;
height: 40px;
color: @text-secondary; color: @text-secondary;
&.is-selected { &.is-selected {

View File

@ -8,40 +8,30 @@ import { If, For } from "tsx-control-statements/components";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, Cmd } from "../../models"; import { GlobalModel, GlobalCommandRunner, Cmd } from "@/models";
import { HistoryItem, RemotePtrType, LineType, CmdDataType } from "../../types/types";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import customParseFormat from "dayjs/plugin/customParseFormat"; import customParseFormat from "dayjs/plugin/customParseFormat";
import { Line } from "../line/linecomps"; import { Line } from "@/app/line/linecomps";
import { CmdStrCode } from "../common/elements"; import { CmdStrCode } from "@/common/elements";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../util/keyutil"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import { ReactComponent as FavoritesIcon } from "../assets/icons/favourites.svg"; import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg";
import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg"; import { ReactComponent as AngleDownIcon } from "@/assets/icons/history/angle-down.svg";
import { ReactComponent as AngleDownIcon } from "../assets/icons/history/angle-down.svg"; import { ReactComponent as ChevronLeftIcon } from "@/assets/icons/history/chevron-left.svg";
import { ReactComponent as ChevronLeftIcon } from "../assets/icons/history/chevron-left.svg"; import { ReactComponent as ChevronRightIcon } from "@/assets/icons/history/chevron-right.svg";
import { ReactComponent as ChevronRightIcon } from "../assets/icons/history/chevron-right.svg"; import { ReactComponent as RightIcon } from "@/assets/icons/history/right.svg";
import { ReactComponent as RightIcon } from "../assets/icons/history/right.svg"; import { ReactComponent as SearchIcon } from "@/assets/icons/history/search.svg";
import { ReactComponent as SearchIcon } from "../assets/icons/history/search.svg"; import { ReactComponent as TrashIcon } from "@/assets/icons/trash.svg";
import { ReactComponent as SquareCheckIcon } from "../assets/icons/history/square-check.svg"; import { ReactComponent as CheckedCheckbox } from "@/assets/icons/checked-checkbox.svg";
import { ReactComponent as SquareMinusIcon } from "../assets/icons/history/square-minus.svg"; import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg";
import { ReactComponent as SquareIcon } from "../assets/icons/history/square.svg"; import { ReactComponent as CopyIcon } from "@/assets/icons/history/copy.svg";
import { ReactComponent as TrashIcon } from "../assets/icons/trash.svg";
import { ReactComponent as CheckedCheckbox } from "../assets/icons/checked-checkbox.svg";
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
import "./history.less"; import "./history.less";
dayjs.extend(customParseFormat); dayjs.extend(customParseFormat);
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
type OArr<V> = mobx.IObservableArray<V>;
type OMap<K, V> = mobx.ObservableMap<K, V>;
type CV<V> = mobx.IComputedValue<V>;
function isBlank(s: string) { function isBlank(s: string) {
return s == null || s == ""; return s == null || s == "";
} }
@ -429,8 +419,16 @@ class HistoryView extends React.Component<{}, {}> {
let sessionId: string = null; let sessionId: string = null;
let remoteIds = Object.keys(rnames); let remoteIds = Object.keys(rnames);
let remoteId: string = null; let remoteId: string = null;
// TODO: something is weird with how we calculate width for views. Before, history view was not honoring tab width. This fix is copied from workspaceview.tsx, which had a similar issue.
const width = window.innerWidth - 6 - GlobalModel.mainSidebarModel.getWidth();
return ( return (
<div className={cn("history-view", { "is-hidden": isHidden })}> <div
className={cn("history-view", "view", { "is-hidden": isHidden })}
style={{
width: `${width}px`,
}}
>
<div className="header"> <div className="header">
<div className="history-title">History</div> <div className="history-title">History</div>
<div className="history-search"> <div className="history-search">

View File

@ -9,48 +9,36 @@ import { boundMethod } from "autobind-decorator";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, Cmd } from "../../models"; import { GlobalModel, GlobalCommandRunner, Cmd } from "@/models";
import { termHeightFromRows } from "../../util/textmeasure"; import { termHeightFromRows } from "@/util/textmeasure";
import type {
LineType,
RenderModeType,
RendererOpts,
RendererPluginType,
LineHeightChangeCallbackType,
RendererModelInitializeParams,
RendererModel,
LineContainerType,
} from "../../types/types";
import cn from "classnames"; import cn from "classnames";
import { getTermPtyData } from "../../util/modelutil"; import { getTermPtyData } from "@/util/modelutil";
import { renderCmdText } from "../common/elements"; import { renderCmdText } from "@/common/elements";
import { SimpleBlobRenderer } from "../../plugins/core/basicrenderer"; import { SimpleBlobRenderer } from "@/plugins/core/basicrenderer";
import { IncrementalRenderer } from "../../plugins/core/incrementalrenderer"; import { IncrementalRenderer } from "@/plugins/core/incrementalrenderer";
import { TerminalRenderer } from "../../plugins/terminal/terminal"; import { TerminalRenderer } from "@/plugins/terminal/terminal";
import { isBlank } from "../../util/util"; import { isBlank } from "@/util/util";
import { PluginModel } from "../../plugins/plugins"; import { PluginModel } from "@/plugins/plugins";
import { Prompt } from "../common/prompt/prompt"; import { Prompt } from "@/common/prompt/prompt";
import * as lineutil from "./lineutil"; import * as lineutil from "./lineutil";
import { ErrorBoundary } from "../../app/common/error/errorboundary"; import { ErrorBoundary } from "@/common/error/errorboundary";
import * as appconst from "../appconst"; import * as appconst from "@/app/appconst";
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg"; import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg";
import { ReactComponent as CommentIcon } from "../assets/icons/line/comment.svg"; import { ReactComponent as CommentIcon } from "@/assets/icons/line/comment.svg";
import { ReactComponent as QuestionIcon } from "../assets/icons/line/question.svg"; import { ReactComponent as QuestionIcon } from "@/assets/icons/line/question.svg";
import { ReactComponent as WarningIcon } from "../assets/icons/line/triangle-exclamation.svg"; import { ReactComponent as WarningIcon } from "@/assets/icons/line/triangle-exclamation.svg";
import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg"; import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg";
import { ReactComponent as FillIcon } from "../assets/icons/line/fill.svg"; import { ReactComponent as FillIcon } from "@/assets/icons/line/fill.svg";
import { ReactComponent as GearIcon } from "../assets/icons/line/gear.svg"; import { ReactComponent as GearIcon } from "@/assets/icons/line/gear.svg";
import { RotateIcon } from "../common/icons/icons"; import { RotateIcon } from "@/common/icons/icons";
import "./lines.less"; import "./lines.less";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class SmallLineAvatar extends React.Component<{ line: LineType; cmd: Cmd; onRightClick?: (e: any) => void }, {}> { class SmallLineAvatar extends React.Component<{ line: LineType; cmd: Cmd; onRightClick?: (e: any) => void }, {}> {
render() { render() {

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.line.line-text { .line.line-text {
flex-direction: row; flex-direction: row;

View File

@ -11,16 +11,12 @@ import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { debounce, throttle } from "throttle-debounce"; import { debounce, throttle } from "throttle-debounce";
import * as T from "../../types/types"; import * as util from "@/util/util";
import * as util from "../../util/util";
import * as lineutil from "./lineutil"; import * as lineutil from "./lineutil";
import "./lines.less"; import "./lines.less";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
type OArr<V> = mobx.IObservableArray<V>;
type OMap<K, V> = mobx.ObservableMap<K, V>;
const LinesVisiblePadding = 500; const LinesVisiblePadding = 500;
@ -29,20 +25,20 @@ type ScreenInterface = {
getSelectedLine(): number; getSelectedLine(): number;
getAnchor(): { anchorLine: number; anchorOffset: number }; getAnchor(): { anchorLine: number; anchorOffset: number };
isLineIdInSidebar(lineId: string): boolean; isLineIdInSidebar(lineId: string): boolean;
getLineByNum(lineNum: number): T.LineType; getLineByNum(lineNum: number): LineType;
}; };
// <Line key={line.lineid} line={line} screen={screen} width={width} visible={this.visibleMap.get(lineNumStr)} staticRender={this.staticRender.get()} onHeightChange={this.onHeightChange} overrideCollapsed={this.collapsedMap.get(lineNumStr)} topBorder={topBorder} renderMode={renderMode}/>; // <Line key={line.lineid} line={line} screen={screen} width={width} visible={this.visibleMap.get(lineNumStr)} staticRender={this.staticRender.get()} onHeightChange={this.onHeightChange} overrideCollapsed={this.collapsedMap.get(lineNumStr)} topBorder={topBorder} renderMode={renderMode}/>;
type LineCompFactory = (props: T.LineFactoryProps) => JSX.Element; type LineCompFactory = (props: LineFactoryProps) => JSX.Element;
@mobxReact.observer @mobxReact.observer
class LinesView extends React.Component< class LinesView extends React.Component<
{ {
screen: ScreenInterface; screen: ScreenInterface;
width: number; width: number;
lines: T.LineInterface[]; lines: LineInterface[];
renderMode: T.RenderModeType; renderMode: RenderModeType;
lineFactory: LineCompFactory; lineFactory: LineCompFactory;
}, },
{} {}
@ -384,7 +380,7 @@ class LinesView extends React.Component<
this.computeVisibleMap_debounced(); this.computeVisibleMap_debounced();
} }
hasTopBorder(lines: T.LineInterface[], idx: number): boolean { hasTopBorder(lines: LineInterface[], idx: number): boolean {
if (idx == 0) { if (idx == 0) {
return false; return false;
} }
@ -394,7 +390,7 @@ class LinesView extends React.Component<
} }
getDateSepStr( getDateSepStr(
lines: T.LineInterface[], lines: LineInterface[],
idx: number, idx: number,
prevStr: string, prevStr: string,
todayStr: string, todayStr: string,
@ -410,7 +406,7 @@ class LinesView extends React.Component<
return null; return null;
} }
findClosestLineIndex(lineNum: number): { line: T.LineInterface; index: number } { findClosestLineIndex(lineNum: number): { line: LineInterface; index: number } {
let { lines } = this.props; let { lines } = this.props;
if (lines.length == 0) { if (lines.length == 0) {
throw new Error("invalid lines, cannot have 0 length in LinesView"); throw new Error("invalid lines, cannot have 0 length in LinesView");
@ -444,7 +440,7 @@ class LinesView extends React.Component<
render() { render() {
let { screen, width, lines, renderMode } = this.props; let { screen, width, lines, renderMode } = this.props;
let selectedLine = screen.getSelectedLine(); // for re-rendering let selectedLine = screen.getSelectedLine(); // for re-rendering
let line: T.LineInterface = null; let line: LineInterface = null;
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
let key = String(lines[i].linenum); let key = String(lines[i].linenum);
let visObs = this.visibleMap.get(key); let visObs = this.visibleMap.get(key);

View File

@ -3,8 +3,7 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { isBlank, getDateStr } from "../../util/util"; import { isBlank, getDateStr } from "@/util/util";
import { LineType, WebLine, RendererContext } from "../../types/types";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);

View File

@ -4,32 +4,10 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { sprintf } from "sprintf-js";
import { boundMethod } from "autobind-decorator";
import { If, For, When, Otherwise, Choose } from "tsx-control-statements/components";
import type {
RendererModelInitializeParams,
TermOptsType,
RendererContext,
RendererOpts,
SimpleBlobRendererComponent,
RendererModelContainerApi,
RendererPluginType,
PtyDataType,
RendererModel,
RendererOptsUpdate,
LineStateType,
LineType,
TermContextUnion,
RendererContainerType,
ExtBlob,
} from "../../../types/types";
import { debounce } from "throttle-debounce";
import * as util from "../../../util/util";
import { GlobalModel } from "../../../models";
type OV<V> = mobx.IObservableValue<V>; import { debounce } from "throttle-debounce";
type CV<V> = mobx.IComputedValue<V>; import * as util from "@/util";
import { GlobalModel } from "@/models";
class SimpleBlobRendererModel { class SimpleBlobRendererModel {
context: RendererContext; context: RendererContext;

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.plugins-view { .plugins-view {
background-color: @background-session; background-color: @background-session;

View File

@ -5,11 +5,11 @@ import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { GlobalModel } from "../../models"; import { GlobalModel } from "@/models";
import { PluginModel } from "../../plugins/plugins"; import { PluginModel } from "@/plugins/plugins";
import { Markdown } from "../common/elements"; import { Markdown } from "@/common/elements";
import { ReactComponent as XmarkIcon } from "../assets/icons/line/xmark.svg"; import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg";
import "./pluginsview.less"; import "./pluginsview.less";

View File

@ -1,5 +1,5 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
@import "../../app/common/icons/icons.less"; @import "@/common/icons/icons.less";
.main-sidebar { .main-sidebar {
padding: 0; padding: 0;

View File

@ -7,23 +7,22 @@ import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { ClientDataType, RemoteType } from "../../types/types";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { compareLoose } from "semver"; import { compareLoose } from "semver";
import { ReactComponent as LeftChevronIcon } from "../assets/icons/chevron_left.svg"; import { ReactComponent as LeftChevronIcon } from "@/assets/icons/chevron_left.svg";
import { ReactComponent as AppsIcon } from "../assets/icons/apps.svg"; import { ReactComponent as AppsIcon } from "@/assets/icons/apps.svg";
import { ReactComponent as WorkspacesIcon } from "../assets/icons/workspaces.svg"; import { ReactComponent as WorkspacesIcon } from "@/assets/icons/workspaces.svg";
import { ReactComponent as SettingsIcon } from "../assets/icons/settings.svg"; import { ReactComponent as SettingsIcon } from "@/assets/icons/settings.svg";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel, GlobalCommandRunner, Session } from "../../models"; import { GlobalModel, GlobalCommandRunner, Session } from "@/models";
import { isBlank, openLink } from "../../util/util"; import { isBlank, openLink } from "@/util/util";
import { ResizableSidebar } from "../common/elements"; import { ResizableSidebar } from "@/common/elements";
import * as appconst from "../appconst"; import * as appconst from "@/app/appconst";
import "./sidebar.less"; import "./sidebar.less";
import { ActionsIcon, CenteredIcon, FrontIcon, StatusIndicator } from "../common/icons/icons"; import { ActionsIcon, CenteredIcon, FrontIcon, StatusIndicator } from "@/common/icons/icons";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);

View File

@ -4,16 +4,13 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { isBlank } from "../../../util/util"; import { isBlank } from "@/util";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { Prompt } from "../../common/prompt/prompt"; import { For } from "tsx-control-statements/components";
import { TextAreaInput } from "./textareainput"; import { Markdown } from "@/elements";
import { If, For } from "tsx-control-statements/components"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import type { OpenAICmdInfoChatMessageType } from "../../../types/types";
import { Markdown } from "../../common/elements";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../../util/keyutil";
@mobxReact.observer @mobxReact.observer
class AIChat extends React.Component<{}, {}> { class AIChat extends React.Component<{}, {}> {

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.cmd-input { .cmd-input {
border-radius: 6px; border-radius: 6px;

View File

@ -8,16 +8,15 @@ import { boundMethod } from "autobind-decorator";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { RemoteType, RemoteInstanceType, RemotePtrType } from "../../../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel, GlobalCommandRunner, Screen } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
import { renderCmdText } from "../../common/elements"; import { renderCmdText } from "@/elements";
import { TextAreaInput } from "./textareainput"; import { TextAreaInput } from "./textareainput";
import { InfoMsg } from "./infomsg"; import { InfoMsg } from "./infomsg";
import { HistoryInfo } from "./historyinfo"; import { HistoryInfo } from "./historyinfo";
import { Prompt } from "../../common/prompt/prompt"; import { Prompt } from "@/common/prompt/prompt";
import { ReactComponent as ExecIcon } from "../../assets/icons/exec.svg"; import { ReactComponent as ExecIcon } from "@/assets/icons/exec.svg";
import { RotateIcon } from "../../common/icons/icons"; import { RotateIcon } from "@/common/icons/icons";
import { AIChat } from "./aichat"; import { AIChat } from "./aichat";
import "./cmdinput.less"; import "./cmdinput.less";

View File

@ -9,10 +9,9 @@ import { boundMethod } from "autobind-decorator";
import { If, For } from "tsx-control-statements/components"; import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { HistoryItem, HistoryQueryOpts } from "../../../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { isBlank } from "../../../util/util"; import { isBlank } from "@/util/util";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);

View File

@ -7,8 +7,8 @@ import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "../../../models"; import { GlobalModel } from "@/models";
import { makeExternLink } from "../../../util/util"; import { makeExternLink } from "@/util/util";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);

View File

@ -4,16 +4,14 @@
import * as React from "react"; import * as React from "react";
import * as mobxReact from "mobx-react"; import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import type * as T from "../../../types/types"; import * as util from "@/util/util";
import * as util from "../../../util/util";
import { If } from "tsx-control-statements/components"; import { If } from "tsx-control-statements/components";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, Screen } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
import { getMonoFontSize } from "../../../util/textmeasure"; import { getMonoFontSize } from "@/util/textmeasure";
import { isModKeyPress, hasNoModifiers } from "../../../util/util"; import * as appconst from "@/app/appconst";
import * as appconst from "../../appconst"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../../../util/keyutil";
type OV<T> = mobx.IObservableValue<T>; type OV<T> = mobx.IObservableValue<T>;
@ -51,7 +49,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
historyInputRef: React.RefObject<HTMLInputElement> = React.createRef(); historyInputRef: React.RefObject<HTMLInputElement> = React.createRef();
controlRef: React.RefObject<HTMLDivElement> = React.createRef(); controlRef: React.RefObject<HTMLDivElement> = React.createRef();
lastHeight: number = 0; lastHeight: number = 0;
lastSP: T.StrWithPos = { str: "", pos: appconst.NoStrPos }; lastSP: StrWithPos = { str: "", pos: appconst.NoStrPos };
version: OV<number> = mobx.observable.box(0); // forces render updates version: OV<number> = mobx.observable.box(0); // forces render updates
incVersion(): void { incVersion(): void {
@ -59,7 +57,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
mobx.action(() => this.version.set(v + 1))(); mobx.action(() => this.version.set(v + 1))();
} }
getCurSP(): T.StrWithPos { getCurSP(): StrWithPos {
let textarea = this.mainInputRef.current; let textarea = this.mainInputRef.current;
if (textarea == null) { if (textarea == null) {
return this.lastSP; return this.lastSP;
@ -169,7 +167,7 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: ()
@boundMethod @boundMethod
onKeyDown(e: any) { onKeyDown(e: any) {
mobx.action(() => { mobx.action(() => {
if (isModKeyPress(e)) { if (util.isModKeyPress(e)) {
return; return;
} }
let model = GlobalModel; let model = GlobalModel;

View File

@ -1,4 +1,4 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.main-content { .main-content {
.screen-view { .screen-view {
@ -6,7 +6,8 @@
position: relative; position: relative;
} }
.screen-sidebar, .window-view { .screen-sidebar,
.window-view {
transition: width 0.5s ease-in-out; transition: width 0.5s ease-in-out;
} }

View File

@ -10,21 +10,19 @@ import { If, For } from "tsx-control-statements/components";
import cn from "classnames"; import cn from "classnames";
import { debounce } from "throttle-debounce"; import { debounce } from "throttle-debounce";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { GlobalCommandRunner, ForwardLineContainer, GlobalModel, ScreenLines, Screen, Session } from "../../../models"; import { GlobalCommandRunner, ForwardLineContainer, GlobalModel, ScreenLines, Screen, Session } from "@/models";
import type { LineType, RenderModeType, LineFactoryProps } from "../../../types/types";
import * as T from "../../../types/types";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { Button, TextField, Dropdown } from "../../common/elements"; import { Button, TextField, Dropdown } from "@/elements";
import { getRemoteStr } from "../../common/prompt/prompt"; import { getRemoteStr } from "@/common/prompt/prompt";
import { Line } from "../../line/linecomps"; import { Line } from "@/app/line/linecomps";
import { LinesView } from "../../line/linesview"; import { LinesView } from "@/app/line/linesview";
import * as util from "../../../util/util"; import * as util from "@/util/util";
import { ReactComponent as EllipseIcon } from "../../assets/icons/ellipse.svg"; import { ReactComponent as EllipseIcon } from "@/assets/icons/ellipse.svg";
import { ReactComponent as Check12Icon } from "../../assets/icons/check12.svg"; import { ReactComponent as Check12Icon } from "@/assets/icons/check12.svg";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; import { ReactComponent as SquareIcon } from "@/assets/icons/tab/square.svg";
import { ReactComponent as GlobeIcon } from "../../assets/icons/globe.svg"; import { ReactComponent as GlobeIcon } from "@/assets/icons/globe.svg";
import { ReactComponent as StatusCircleIcon } from "../../assets/icons/statuscircle.svg"; import { ReactComponent as StatusCircleIcon } from "@/assets/icons/statuscircle.svg";
import * as appconst from "../../appconst"; import * as appconst from "@/app/appconst";
import "./screenview.less"; import "./screenview.less";
import "./tabs.less"; import "./tabs.less";
@ -32,8 +30,6 @@ import { MagicLayout } from "../../magiclayout";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class ScreenView extends React.Component<{ session: Session; screen: Screen }, {}> { class ScreenView extends React.Component<{ session: Session; screen: Screen }, {}> {
rszObs: ResizeObserver; rszObs: ResizeObserver;
@ -164,7 +160,7 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, {
type SidebarLineContainerPropsType = { type SidebarLineContainerPropsType = {
screen: Screen; screen: Screen;
winSize: T.WindowSize; winSize: WindowSize;
lineId: string; lineId: string;
}; };
@ -230,7 +226,7 @@ class SidebarLineContainer extends React.Component<SidebarLineContainerPropsType
@mobxReact.observer @mobxReact.observer
class ScreenSidebar extends React.Component<{ screen: Screen; width: string }, {}> { class ScreenSidebar extends React.Component<{ screen: Screen; width: string }, {}> {
rszObs: ResizeObserver; rszObs: ResizeObserver;
sidebarSize: OV<T.WindowSize> = mobx.observable.box({ height: 0, width: 0 }, { name: "sidebarSize" }); sidebarSize: OV<WindowSize> = mobx.observable.box({ height: 0, width: 0 }, { name: "sidebarSize" });
sidebarRef: React.RefObject<any> = React.createRef(); sidebarRef: React.RefObject<any> = React.createRef();
handleResize_debounced: (entries: ResizeObserverEntry[]) => void; handleResize_debounced: (entries: ResizeObserverEntry[]) => void;
@ -288,7 +284,7 @@ class ScreenSidebar extends React.Component<{ screen: Screen; width: string }, {
GlobalCommandRunner.screenSidebarOpen("500px"); GlobalCommandRunner.screenSidebarOpen("500px");
} }
getSidebarConfig(): T.ScreenSidebarOptsType { getSidebarConfig(): ScreenSidebarOptsType {
let { screen } = this.props; let { screen } = this.props;
let viewOpts = screen.viewOpts.get(); let viewOpts = screen.viewOpts.get();
return viewOpts?.sidebar; return viewOpts?.sidebar;
@ -344,7 +340,7 @@ class ScreenSidebar extends React.Component<{ screen: Screen; width: string }, {
class NewTabSettings extends React.Component<{ screen: Screen }, {}> { class NewTabSettings extends React.Component<{ screen: Screen }, {}> {
connDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "NewTabSettings-connDropdownActive" }); connDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "NewTabSettings-connDropdownActive" });
errorMessage: OV<string | null> = mobx.observable.box(null, { name: "NewTabSettings-errorMessage" }); errorMessage: OV<string | null> = mobx.observable.box(null, { name: "NewTabSettings-errorMessage" });
remotes: T.RemoteType[]; remotes: RemoteType[];
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -6,13 +6,13 @@ import * as mobxReact from "mobx-react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import cn from "classnames"; import cn from "classnames";
import { GlobalModel, GlobalCommandRunner, Screen } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Screen } from "@/models";
import { ActionsIcon, StatusIndicator, CenteredIcon } from "../../common/icons/icons"; import { ActionsIcon, StatusIndicator, CenteredIcon } from "@/common/icons/icons";
import { renderCmdText } from "../../common/elements"; import { renderCmdText } from "@/elements";
import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"; import { ReactComponent as SquareIcon } from "@/assets/icons/tab/square.svg";
import * as constants from "../../appconst"; import * as constants from "@/app/appconst";
import { Reorder } from "framer-motion"; import { Reorder } from "framer-motion";
import { MagicLayout } from "../../magiclayout"; import { MagicLayout } from "@/app/magiclayout";
@mobxReact.observer @mobxReact.observer
class ScreenTab extends React.Component< class ScreenTab extends React.Component<

View File

@ -1,5 +1,5 @@
@import "../../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
@import "../../../app/common/icons/icons.less"; @import "@/common/icons/icons.less";
#main .screen-tabs .screen-tab { #main .screen-tabs .screen-tab {
border-top: 1px solid transparent; border-top: 1px solid transparent;

View File

@ -7,8 +7,8 @@ import * as mobx from "mobx";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { For } from "tsx-control-statements/components"; import { For } from "tsx-control-statements/components";
import { GlobalModel, GlobalCommandRunner, Session, Screen } from "../../../models"; import { GlobalModel, GlobalCommandRunner, Session, Screen } from "@/models";
import { ReactComponent as AddIcon } from "../../assets/icons/add.svg"; import { ReactComponent as AddIcon } from "@/assets/icons/add.svg";
import { Reorder } from "framer-motion"; import { Reorder } from "framer-motion";
import { ScreenTab } from "./tab"; import { ScreenTab } from "./tab";

View File

@ -1,4 +1,4 @@
@import "../../app/common/themes/themes.less"; @import "@/common/themes/themes.less";
.session-view { .session-view {
flex-grow: 1; flex-grow: 1;

View File

@ -7,18 +7,16 @@ import * as mobx from "mobx";
import cn from "classnames"; import cn from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import { GlobalModel } from "../../models"; import { GlobalModel } from "@/models";
import { CmdInput } from "./cmdinput/cmdinput"; import { CmdInput } from "./cmdinput/cmdinput";
import { ScreenView } from "./screen/screenview"; import { ScreenView } from "./screen/screenview";
import { ScreenTabs } from "./screen/tabs"; import { ScreenTabs } from "./screen/tabs";
import { ErrorBoundary } from "../../app/common/error/errorboundary"; import { ErrorBoundary } from "@/common/error/errorboundary";
import { MagicLayout } from "../magiclayout"; import { MagicLayout } from "../magiclayout";
import "./workspace.less"; import "./workspace.less";
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);
type OV<V> = mobx.IObservableValue<V>;
@mobxReact.observer @mobxReact.observer
class WorkspaceView extends React.Component<{}, {}> { class WorkspaceView extends React.Component<{}, {}> {
render() { render() {

View File

@ -7,12 +7,12 @@ import * as fs from "fs";
import fetch from "node-fetch"; import fetch from "node-fetch";
import * as child_process from "node:child_process"; import * as child_process from "node:child_process";
import { debounce } from "throttle-debounce"; import { debounce } from "throttle-debounce";
import { handleJsonFetchResponse } from "../util/util";
import * as winston from "winston"; import * as winston from "winston";
import * as util from "util";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
import * as util from "util";
import { handleJsonFetchResponse } from "@/util/util";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { checkKeyPressed, adaptFromElectronKeyEvent, setKeyUtilPlatform } from "../util/keyutil"; import { checkKeyPressed, adaptFromElectronKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
import { platform } from "os"; import { platform } from "os";
const WaveAppPathVarName = "WAVETERM_APP_PATH"; const WaveAppPathVarName = "WAVETERM_APP_PATH";
@ -40,19 +40,21 @@ let unameArch: string = process.arch;
if (unameArch == "x64") { if (unameArch == "x64") {
unameArch = "amd64"; unameArch = "amd64";
} }
let logger; let loggerTransports: winston.transport[] = [
new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" }),
];
if (isDev) {
loggerTransports.push(new winston.transports.Console());
}
let loggerConfig = { let loggerConfig = {
level: "info", level: "info",
format: winston.format.combine( format: winston.format.combine(
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.printf((info) => `${info.timestamp} ${info.message}`) winston.format.printf((info) => `${info.timestamp} ${info.message}`)
), ),
transports: [new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" })], transports: loggerTransports,
}; };
if (isDev) { let logger = winston.createLogger(loggerConfig);
loggerConfig.transports.push(new winston.transports.Console());
}
logger = winston.createLogger(loggerConfig);
function log(...msg) { function log(...msg) {
try { try {
logger.info(util.format(...msg)); logger.info(util.format(...msg));
@ -75,7 +77,7 @@ if (isDev) {
} }
let app = electron.app; let app = electron.app;
app.setName(isDev ? "Wave (Dev)" : "Wave"); app.setName(isDev ? "Wave (Dev)" : "Wave");
let waveSrvProc = null; let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
let waveSrvShouldRestart = false; let waveSrvShouldRestart = false;
electron.dialog.showErrorBox = (title, content) => { electron.dialog.showErrorBox = (title, content) => {
@ -101,8 +103,11 @@ function checkPromptMigrate() {
// don't migrate if we're running dev version or if wave home directory already exists // don't migrate if we're running dev version or if wave home directory already exists
return; return;
} }
let homeDir = process.env.HOME; if (process.env.HOME == null) {
let promptHome = path.join(homeDir, "prompt"); return;
}
let homeDir: string = process.env.HOME;
let promptHome: string = path.join(homeDir, "prompt");
if (!fs.existsSync(promptHome) || !fs.existsSync(path.join(promptHome, "prompt.db"))) { if (!fs.existsSync(promptHome) || !fs.existsSync(path.join(promptHome, "prompt.db"))) {
// make sure we have a valid prompt home directory (prompt.db must exist inside) // make sure we have a valid prompt home directory (prompt.db must exist inside)
return; return;
@ -170,7 +175,7 @@ function readAuthKey() {
return authKeyStr.trim(); return authKeyStr.trim();
} }
let menuTemplate = [ let menuTemplate: Electron.MenuItemConstructorOptions[] = [
{ {
role: "appMenu", role: "appMenu",
submenu: [ submenu: [
@ -222,7 +227,7 @@ function getMods(input: any) {
return { meta: input.meta, shift: input.shift, ctrl: input.control, alt: input.alt }; return { meta: input.meta, shift: input.shift, ctrl: input.control, alt: input.alt };
} }
function shNavHandler(event: any, url: any) { function shNavHandler(event: Electron.Event<Electron.WebContentsWillNavigateEventParams>, url: string) {
event.preventDefault(); event.preventDefault();
if (url.startsWith("https://") || url.startsWith("http://") || url.startsWith("file://")) { if (url.startsWith("https://") || url.startsWith("http://") || url.startsWith("file://")) {
console.log("open external, shNav", url); console.log("open external, shNav", url);
@ -232,12 +237,13 @@ function shNavHandler(event: any, url: any) {
} }
} }
function shFrameNavHandler(event: any, url: any) { function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWillFrameNavigateEventParams>) {
if (!event.frame || event.frame.parent == null) { if (!event.frame || event.frame.parent == null) {
// only use this handler to process iframe events (non-iframe events go to shNavHandler) // only use this handler to process iframe events (non-iframe events go to shNavHandler)
return; return;
} }
event.preventDefault(); event.preventDefault();
let url = event.url;
console.log(`frame-navigation url=${url} frame=${event.frame.name}`); console.log(`frame-navigation url=${url} frame=${event.frame.name}`);
if (event.frame.name == "webview") { if (event.frame.name == "webview") {
// "webview" links always open in new window // "webview" links always open in new window
@ -250,7 +256,7 @@ function shFrameNavHandler(event: any, url: any) {
return; return;
} }
function createMainWindow(clientData) { function createMainWindow(clientData: ClientDataType | null) {
let bounds = calcBounds(clientData); let bounds = calcBounds(clientData);
setKeyUtilPlatform(platform()); setKeyUtilPlatform(platform());
let win = new electron.BrowserWindow({ let win = new electron.BrowserWindow({
@ -644,12 +650,11 @@ electron.ipcMain.on("context-editmenu", (event, { x, y }, opts) => {
} }
console.log("context-editmenu"); console.log("context-editmenu");
let menu = new electron.Menu(); let menu = new electron.Menu();
let menuItem = null;
if (opts.showCut) { if (opts.showCut) {
menuItem = new electron.MenuItem({ label: "Cut", role: "cut" }); let menuItem = new electron.MenuItem({ label: "Cut", role: "cut" });
menu.append(menuItem); menu.append(menuItem);
} }
menuItem = new electron.MenuItem({ label: "Copy", role: "copy" }); let menuItem = new electron.MenuItem({ label: "Copy", role: "copy" });
menu.append(menuItem); menu.append(menuItem);
menuItem = new electron.MenuItem({ label: "Paste", role: "paste" }); menuItem = new electron.MenuItem({ label: "Paste", role: "paste" });
menu.append(menuItem); menu.append(menuItem);
@ -657,7 +662,7 @@ electron.ipcMain.on("context-editmenu", (event, { x, y }, opts) => {
}); });
async function createMainWindowWrap() { async function createMainWindowWrap() {
let clientData = null; let clientData: ClientDataType | null = null;
try { try {
clientData = await getClientDataPoll(1); clientData = await getClientDataPoll(1);
} catch (e) { } catch (e) {

View File

@ -5,7 +5,7 @@ import * as mobx from "mobx";
import * as React from "react"; import * as React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
import { App } from "./app/app"; import { App } from "@/app/app";
import * as DOMPurify from "dompurify"; import * as DOMPurify from "dompurify";
import { loadFonts } from "./util/util"; import { loadFonts } from "./util/util";

View File

@ -4,10 +4,8 @@
import * as mobx from "mobx"; import * as mobx from "mobx";
import { sprintf } from "sprintf-js"; import { sprintf } from "sprintf-js";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { genMergeSimpleData } from "../util/util"; import { genMergeSimpleData } from "@/util/util";
import { BookmarkType } from "../types/types"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../util/keyutil";
import { OV, OArr } from "../types/types";
import { GlobalCommandRunner } from "./global"; import { GlobalCommandRunner } from "./global";
import { Model } from "./model"; import { Model } from "./model";

View File

@ -2,20 +2,10 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as mobx from "mobx"; import * as mobx from "mobx";
import { stringToBase64 } from "../util/util"; import { stringToBase64 } from "@/util/util";
import { TermWrap } from "../plugins/terminal/term"; import { TermWrap } from "@/plugins/terminal/term";
import { import { cmdStatusIsRunning } from "@/app/line/lineutil";
RemotePtrType,
CmdDataType,
TermOptsType,
FeInputPacketType,
RendererModel,
WebCmd,
WebRemote,
} from "../types/types";
import { cmdStatusIsRunning } from "../app/line/lineutil";
import { Model } from "./model"; import { Model } from "./model";
import { OV } from "../types/types";
const InputChunkSize = 500; const InputChunkSize = 500;
class Cmd { class Cmd {

View File

@ -2,13 +2,12 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as mobx from "mobx"; import * as mobx from "mobx";
import { RendererContext, CommandRtnType, HistorySearchParams, LineStateType } from "../types/types";
import { GlobalModel } from "./global"; import { GlobalModel } from "./global";
class CommandRunner { class CommandRunner {
private constructor() {} private constructor() {}
static getInstance() { static getInstance(): CommandRunner {
if (!(window as any).GlobalCommandRunner) { if (!(window as any).GlobalCommandRunner) {
(window as any).GlobalCommandRunner = new CommandRunner(); (window as any).GlobalCommandRunner = new CommandRunner();
} }
@ -35,6 +34,12 @@ class CommandRunner {
return prtn; return prtn;
} }
switchView(view: string) {
mobx.action(() => {
GlobalModel.activeMainView.set(view);
})();
}
switchSession(session: string) { switchSession(session: string) {
mobx.action(() => { mobx.action(() => {
GlobalModel.activeMainView.set("session"); GlobalModel.activeMainView.set("session");

View File

@ -1,10 +1,9 @@
// Copyright 2023, Command Line Inc. // Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import { TermWrap } from "../plugins/terminal/term"; import { TermWrap } from "@/plugins/terminal/term";
import * as types from "../types/types"; import { windowWidthToCols, windowHeightToRows } from "@/util/textmeasure";
import { windowWidthToCols, windowHeightToRows } from "../util/textmeasure"; import { MagicLayout } from "@/app/magiclayout";
import { MagicLayout } from "../app/magiclayout";
import { Model } from "./model"; import { Model } from "./model";
import { GlobalCommandRunner } from "./global"; import { GlobalCommandRunner } from "./global";
import { Cmd } from "./cmd"; import { Cmd } from "./cmd";
@ -12,12 +11,12 @@ import { Screen } from "./screen";
class ForwardLineContainer { class ForwardLineContainer {
globalModel: Model; globalModel: Model;
winSize: types.WindowSize; winSize: WindowSize;
screen: Screen; screen: Screen;
containerType: types.LineContainerStrs; containerType: LineContainerStrs;
lineId: string; lineId: string;
constructor(screen: Screen, winSize: types.WindowSize, containerType: types.LineContainerStrs, lineId: string) { constructor(screen: Screen, winSize: WindowSize, containerType: LineContainerStrs, lineId: string) {
this.globalModel = Model.getInstance(); this.globalModel = Model.getInstance();
this.screen = screen; this.screen = screen;
this.winSize = winSize; this.winSize = winSize;
@ -25,7 +24,7 @@ class ForwardLineContainer {
this.lineId = lineId; this.lineId = lineId;
} }
screenSizeCallback(winSize: types.WindowSize): void { screenSizeCallback(winSize: WindowSize): void {
this.winSize = winSize; this.winSize = winSize;
let termWrap = this.getTermWrap(this.lineId); let termWrap = this.getTermWrap(this.lineId);
if (termWrap != null) { if (termWrap != null) {
@ -37,11 +36,11 @@ class ForwardLineContainer {
} }
} }
getContainerType(): types.LineContainerStrs { getContainerType(): LineContainerStrs {
return this.containerType; return this.containerType;
} }
getCmd(line: types.LineType): Cmd { getCmd(line: LineType): Cmd {
return this.screen.getCmd(line); return this.screen.getCmd(line);
} }
@ -57,25 +56,25 @@ class ForwardLineContainer {
this.screen.setLineFocus(lineNum, focus); this.screen.setLineFocus(lineNum, focus);
} }
setContentHeight(context: types.RendererContext, height: number): void { setContentHeight(context: RendererContext, height: number): void {
return; return;
} }
getMaxContentSize(): types.WindowSize { getMaxContentSize(): WindowSize {
let rtn = { width: this.winSize.width, height: this.winSize.height }; let rtn = { width: this.winSize.width, height: this.winSize.height };
rtn.width = rtn.width - MagicLayout.ScreenMaxContentWidthBuffer; rtn.width = rtn.width - MagicLayout.ScreenMaxContentWidthBuffer;
return rtn; return rtn;
} }
getIdealContentSize(): types.WindowSize { getIdealContentSize(): WindowSize {
return this.winSize; return this.winSize;
} }
loadTerminalRenderer(elem: Element, line: types.LineType, cmd: Cmd, width: number): void { loadTerminalRenderer(elem: Element, line: LineType, cmd: Cmd, width: number): void {
this.screen.loadTerminalRenderer(elem, line, cmd, width); this.screen.loadTerminalRenderer(elem, line, cmd, width);
} }
registerRenderer(lineId: string, renderer: types.RendererModel): void { registerRenderer(lineId: string, renderer: RendererModel): void {
this.screen.registerRenderer(lineId, renderer); this.screen.registerRenderer(lineId, renderer);
} }
@ -83,11 +82,11 @@ class ForwardLineContainer {
this.screen.unloadRenderer(lineId); this.screen.unloadRenderer(lineId);
} }
getContentHeight(context: types.RendererContext): number { getContentHeight(context: RendererContext): number {
return this.screen.getContentHeight(context); return this.screen.getContentHeight(context);
} }
getUsedRows(context: types.RendererContext, line: types.LineType, cmd: Cmd, width: number): number { getUsedRows(context: RendererContext, line: LineType, cmd: Cmd, width: number): number {
return this.screen.getUsedRows(context, line, cmd, width); return this.screen.getUsedRows(context, line, cmd, width);
} }
@ -95,7 +94,7 @@ class ForwardLineContainer {
return this.screen.getIsFocused(lineNum); return this.screen.getIsFocused(lineNum);
} }
getRenderer(lineId: string): types.RendererModel { getRenderer(lineId: string): RendererModel {
return this.screen.getRenderer(lineId); return this.screen.getRenderer(lineId);
} }
@ -103,7 +102,7 @@ class ForwardLineContainer {
return this.screen.getTermWrap(lineId); return this.screen.getTermWrap(lineId);
} }
getFocusType(): types.FocusTypeStrs { getFocusType(): FocusTypeStrs {
return this.screen.getFocusType(); return this.screen.getFocusType();
} }

View File

@ -1,6 +1,6 @@
import { Model } from "./model"; import { Model } from "./model";
import { CommandRunner } from "./commandrunner"; import { CommandRunner } from "./commandrunner";
const GlobalModel = Model.getInstance(); const GlobalModel: Model = Model.getInstance();
const GlobalCommandRunner = CommandRunner.getInstance(); const GlobalCommandRunner: CommandRunner = CommandRunner.getInstance();
export { GlobalModel, GlobalCommandRunner }; export { GlobalModel, GlobalCommandRunner };

View File

@ -3,20 +3,11 @@
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { isBlank } from "../util/util"; import { isBlank } from "@/util/util";
import { import { termWidthFromCols, termHeightFromRows } from "@/util/textmeasure";
LineType,
HistoryItem,
CmdDataType,
HistoryViewDataType,
HistorySearchParams,
CommandRtnType,
} from "../types/types";
import { termWidthFromCols, termHeightFromRows } from "../util/textmeasure";
import dayjs from "dayjs"; import dayjs from "dayjs";
import * as appconst from "../app/appconst"; import * as appconst from "@/app/appconst";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "../util/keyutil"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
import { OV, OArr, OMap } from "../types/types";
import { GlobalCommandRunner } from "./global"; import { GlobalCommandRunner } from "./global";
import { Model } from "./model"; import { Model } from "./model";
import { Cmd } from "./cmd"; import { Cmd } from "./cmd";

View File

@ -4,19 +4,8 @@
import type React from "react"; import type React from "react";
import * as mobx from "mobx"; import * as mobx from "mobx";
import { boundMethod } from "autobind-decorator"; import { boundMethod } from "autobind-decorator";
import { isBlank } from "../util/util"; import { isBlank } from "@/util/util";
import { import * as appconst from "@/app/appconst";
HistoryItem,
RemotePtrType,
InfoType,
HistoryInfoType,
HistoryQueryOpts,
HistoryTypeStrs,
OpenAICmdInfoChatMessageType,
} from "../types/types";
import { StrWithPos } from "../types/types";
import * as appconst from "../app/appconst";
import { OV } from "../types/types";
import { Model } from "./model"; import { Model } from "./model";
import { GlobalCommandRunner } from "./global"; import { GlobalCommandRunner } from "./global";
@ -207,7 +196,6 @@ class InputModel {
this.historyQueryOpts.set(opts); this.historyQueryOpts.set(opts);
let bestIndex = this.findBestNewIndex(oldItem); let bestIndex = this.findBestNewIndex(oldItem);
setTimeout(() => this.setHistoryIndex(bestIndex, true), 10); setTimeout(() => this.setHistoryIndex(bestIndex, true), 10);
return;
})(); })();
} }
@ -624,13 +612,17 @@ class InputModel {
} }
openAIAssistantChat(): void { openAIAssistantChat(): void {
mobx.action(() => {
this.aIChatShow.set(true); this.aIChatShow.set(true);
this.setAIChatFocus(); this.setAIChatFocus();
})();
} }
closeAIAssistantChat(): void { closeAIAssistantChat(): void {
mobx.action(() => {
this.aIChatShow.set(false); this.aIChatShow.set(false);
this.giveFocus(); this.giveFocus();
})();
} }
clearAIAssistantChat(): void { clearAIAssistantChat(): void {
@ -721,14 +713,6 @@ class InputModel {
setCurLine(val: string): void { setCurLine(val: string): void {
let hidx = this.historyIndex.get(); let hidx = this.historyIndex.get();
mobx.action(() => { mobx.action(() => {
// if (val == "\" ") {
// this.setInputMode("comment");
// val = "";
// }
// if (val == "//") {
// this.setInputMode("global");
// val = "";
// }
if (this.modHistory.length <= hidx) { if (this.modHistory.length <= hidx) {
this.modHistory.length = hidx + 1; this.modHistory.length = hidx + 1;
} }

View File

@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as mobx from "mobx"; import * as mobx from "mobx";
import { MagicLayout } from "../app/magiclayout"; import { MagicLayout } from "@/app/magiclayout";
import { OV } from "../types/types";
import { Model } from "./model"; import { Model } from "./model";
class MainSidebarModel { class MainSidebarModel {
globalModel: Model = null; globalModel: Model = null;
tempWidth: OV<number> = mobx.observable.box(null, { tempWidth: OV<number> = mobx.observable.box(null, {

View File

@ -3,9 +3,7 @@
import * as mobx from "mobx"; import * as mobx from "mobx";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { ModalStoreEntry } from "../types/types"; import { modalsRegistry } from "@/modals/registry";
import { modalsRegistry } from "../app/common/modals/registry";
import { OArr } from "../types/types";
class ModalsModel { class ModalsModel {
store: OArr<ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store", deep: false }); store: OArr<ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store", deep: false });

View File

@ -12,43 +12,12 @@ import {
genMergeSimpleData, genMergeSimpleData,
isModKeyPress, isModKeyPress,
isBlank, isBlank,
} from "../util/util"; } from "@/util/util";
import {
SessionDataType,
LineType,
RemoteType,
RemoteInstanceType,
CmdDataType,
FeCmdPacketType,
ScreenDataType,
PtyDataUpdateType,
ModelUpdateType,
UpdateMessage,
InfoType,
StrWithPos,
UIContextType,
ContextMenuOpts,
RendererContext,
ClientDataType,
AlertMessageType,
UserInputRequest,
UserInputResponsePacket,
ScreenLinesType,
RemoteViewType,
CommandRtnType,
LineFocusType,
CmdInputTextPacketType,
FileInfoType,
ExtFile,
HistorySearchParams,
LineStateType,
} from "../types/types";
import { WSControl } from "./ws"; import { WSControl } from "./ws";
import { cmdStatusIsRunning } from "../app/line/lineutil"; import { cmdStatusIsRunning } from "@/app/line/lineutil";
import * as appconst from "../app/appconst"; import * as appconst from "@/app/appconst";
import { remotePtrToString, cmdPacketString } from "../util/modelutil"; import { remotePtrToString, cmdPacketString } from "@/util/modelutil";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "../util/keyutil"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
import { OV, OArr, OMap, CV } from "../types/types";
import { Session } from "./session"; import { Session } from "./session";
import { ScreenLines } from "./screenlines"; import { ScreenLines } from "./screenlines";
import { InputModel } from "./input"; import { InputModel } from "./input";
@ -187,7 +156,7 @@ class Model {
this.isDev = getApi().getIsDev(); this.isDev = getApi().getIsDev();
this.authKey = getApi().getAuthKey(); this.authKey = getApi().getAuthKey();
this.ws = new WSControl(this.getBaseWsHostPort(), this.clientId, this.authKey, (message: any) => { this.ws = new WSControl(this.getBaseWsHostPort(), this.clientId, this.authKey, (message: any) => {
let interactive = message?.interactive ?? false; const interactive = message?.interactive ?? false;
this.runUpdate(message, interactive); this.runUpdate(message, interactive);
}); });
this.ws.reconnect(); this.ws.reconnect();
@ -200,17 +169,17 @@ class Model {
this.remotesModel = new RemotesModel(this); this.remotesModel = new RemotesModel(this);
this.modalsModel = new ModalsModel(); this.modalsModel = new ModalsModel();
this.mainSidebarModel = new MainSidebarModel(this); this.mainSidebarModel = new MainSidebarModel(this);
let isWaveSrvRunning = getApi().getWaveSrvStatus(); const isWaveSrvRunning = getApi().getWaveSrvStatus();
this.waveSrvRunning = mobx.observable.box(isWaveSrvRunning, { this.waveSrvRunning = mobx.observable.box(isWaveSrvRunning, {
name: "model-wavesrv-running", name: "model-wavesrv-running",
}); });
this.platform = this.getPlatform(); this.platform = this.getPlatform();
this.termFontSize = mobx.computed(() => { this.termFontSize = mobx.computed(() => {
let cdata = this.clientData.get(); const cdata = this.clientData.get();
if (cdata == null || cdata.feopts == null || cdata.feopts.termfontsize == null) { if (cdata?.feopts?.termfontsize == null) {
return appconst.DefaultTermFontSize; return appconst.DefaultTermFontSize;
} }
let fontSize = Math.ceil(cdata.feopts.termfontsize); const fontSize = Math.ceil(cdata.feopts.termfontsize);
if (fontSize < appconst.MinFontSize) { if (fontSize < appconst.MinFontSize) {
return appconst.MinFontSize; return appconst.MinFontSize;
} }
@ -239,7 +208,7 @@ class Model {
setTimeout(() => this.getClientDataLoop(1), 10); setTimeout(() => this.getClientDataLoop(1), 10);
} }
static getInstance() { static getInstance(): Model {
if (!(window as any).GlobalModel) { if (!(window as any).GlobalModel) {
(window as any).GlobalModel = new Model(); (window as any).GlobalModel = new Model();
} }
@ -265,11 +234,11 @@ class Model {
} }
needsTos(): boolean { needsTos(): boolean {
let cdata = this.clientData.get(); const cdata = this.clientData.get();
if (cdata == null) { if (cdata == null) {
return false; return false;
} }
return cdata.clientopts == null || !cdata.clientopts.acceptedtos; return !cdata.clientopts?.acceptedtos;
} }
refreshClient(): void { refreshClient(): void {
@ -288,7 +257,7 @@ class Model {
refocus() { refocus() {
// givefocus() give back focus to cmd or input // givefocus() give back focus to cmd or input
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
if (screen == null) { if (screen == null) {
return; return;
} }
@ -296,8 +265,8 @@ class Model {
} }
getWebSharedScreens(): Screen[] { getWebSharedScreens(): Screen[] {
let rtn: Screen[] = []; const rtn: Screen[] = [];
for (let screen of this.screenMap.values()) { for (const screen of this.screenMap.values()) {
if (screen.shareMode.get() == "web") { if (screen.shareMode.get() == "web") {
rtn.push(screen); rtn.push(screen);
} }
@ -309,7 +278,7 @@ class Model {
if (this.clientData.get() == null) { if (this.clientData.get() == null) {
return true; return true;
} }
let cdata = this.clientData.get(); const cdata = this.clientData.get();
if (cdata.cmdstoretype == "session") { if (cdata.cmdstoretype == "session") {
return true; return true;
} }
@ -318,8 +287,8 @@ class Model {
showAlert(alertMessage: AlertMessageType): Promise<boolean> { showAlert(alertMessage: AlertMessageType): Promise<boolean> {
if (alertMessage.confirmflag != null) { if (alertMessage.confirmflag != null) {
let cdata = this.clientData.get(); const cdata = this.clientData.get();
let noConfirm = cdata.clientopts?.confirmflags?.[alertMessage.confirmflag]; const noConfirm = cdata.clientopts?.confirmflags?.[alertMessage.confirmflag];
if (noConfirm) { if (noConfirm) {
return Promise.resolve(true); return Promise.resolve(true);
} }
@ -328,7 +297,7 @@ class Model {
this.alertMessage.set(alertMessage); this.alertMessage.set(alertMessage);
this.modalsModel.pushModal(appconst.ALERT); this.modalsModel.pushModal(appconst.ALERT);
})(); })();
let prtn = new Promise<boolean>((resolve, reject) => { const prtn = new Promise<boolean>((resolve, reject) => {
this.alertPromiseResolver = resolve; this.alertPromiseResolver = resolve;
}); });
return prtn; return prtn;
@ -405,7 +374,7 @@ class Model {
} }
docKeyDownHandler(e: KeyboardEvent) { docKeyDownHandler(e: KeyboardEvent) {
let waveEvent = adaptFromReactOrNativeKeyEvent(e); const waveEvent = adaptFromReactOrNativeKeyEvent(e);
if (isModKeyPress(e)) { if (isModKeyPress(e)) {
return; return;
} }
@ -447,7 +416,7 @@ class Model {
if (this.clearModals()) { if (this.clearModals()) {
return; return;
} }
let inputModel = this.inputModel; const inputModel = this.inputModel;
inputModel.toggleInfoMsg(); inputModel.toggleInfoMsg();
if (inputModel.inputMode.get() != null) { if (inputModel.inputMode.get() != null) {
inputModel.resetInputMode(); inputModel.resetInputMode();
@ -460,9 +429,9 @@ class Model {
} }
if (this.activeMainView.get() == "session" && checkKeyPressed(waveEvent, "Cmd:Ctrl:s")) { if (this.activeMainView.get() == "session" && checkKeyPressed(waveEvent, "Cmd:Ctrl:s")) {
e.preventDefault(); e.preventDefault();
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
if (activeScreen != null) { if (activeScreen != null) {
let isSidebarOpen = activeScreen.isSidebarOpen(); const isSidebarOpen = activeScreen.isSidebarOpen();
if (isSidebarOpen) { if (isSidebarOpen) {
GlobalCommandRunner.screenSidebarClose(); GlobalCommandRunner.screenSidebarClose();
} else { } else {
@ -471,7 +440,7 @@ class Model {
} }
} }
if (checkKeyPressed(waveEvent, "Cmd:d")) { if (checkKeyPressed(waveEvent, "Cmd:d")) {
let ranDelete = this.deleteActiveLine(); const ranDelete = this.deleteActiveLine();
if (ranDelete) { if (ranDelete) {
e.preventDefault(); e.preventDefault();
} }
@ -479,22 +448,22 @@ class Model {
} }
deleteActiveLine(): boolean { deleteActiveLine(): boolean {
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
if (activeScreen == null || activeScreen.getFocusType() != "cmd") { if (activeScreen == null || activeScreen.getFocusType() != "cmd") {
return false; return false;
} }
let selectedLine = activeScreen.selectedLine.get(); const selectedLine = activeScreen.selectedLine.get();
if (selectedLine == null || selectedLine <= 0) { if (selectedLine == null || selectedLine <= 0) {
return false; return false;
} }
let line = activeScreen.getLineByNum(selectedLine); const line = activeScreen.getLineByNum(selectedLine);
if (line == null) { if (line == null) {
return false; return false;
} }
let cmd = activeScreen.getCmd(line); const cmd = activeScreen.getCmd(line);
if (cmd != null) { if (cmd != null) {
if (cmd.isRunning()) { if (cmd.isRunning()) {
let info: InfoType = { infomsg: "Cannot delete a running command" }; const info: InfoType = { infomsg: "Cannot delete a running command" };
this.inputModel.flashInfoMsg(info, 2000); this.inputModel.flashInfoMsg(info, 2000);
return false; return false;
} }
@ -507,11 +476,11 @@ class Model {
if (this.activeMainView.get() != "session") { if (this.activeMainView.get() != "session") {
return; return;
} }
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
if (activeScreen == null) { if (activeScreen == null) {
return; return;
} }
let rtnp = this.showAlert({ const rtnp = this.showAlert({
message: "Are you sure you want to delete this screen?", message: "Are you sure you want to delete this screen?",
confirm: true, confirm: true,
}); });
@ -527,7 +496,7 @@ class Model {
if (this.activeMainView.get() != "session") { if (this.activeMainView.get() != "session") {
return; return;
} }
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
if (activeScreen == null) { if (activeScreen == null) {
return; return;
} }
@ -536,7 +505,7 @@ class Model {
GlobalCommandRunner.lineRestart("E", true); GlobalCommandRunner.lineRestart("E", true);
} else { } else {
// restart selected line // restart selected line
let selectedLine = activeScreen.selectedLine.get(); const selectedLine = activeScreen.selectedLine.get();
if (selectedLine == null || selectedLine == 0) { if (selectedLine == null || selectedLine == 0) {
return; return;
} }
@ -585,7 +554,7 @@ class Model {
} }
getCurRemoteInstance(): RemoteInstanceType { getCurRemoteInstance(): RemoteInstanceType {
let screen = this.getActiveScreen(); const screen = this.getActiveScreen();
if (screen == null) { if (screen == null) {
return null; return null;
} }
@ -603,12 +572,12 @@ class Model {
} }
getContentHeight(context: RendererContext): number { getContentHeight(context: RendererContext): number {
let key = context.screenId + "/" + context.lineId; const key = context.screenId + "/" + context.lineId;
return this.termUsedRowsCache[key]; return this.termUsedRowsCache[key];
} }
setContentHeight(context: RendererContext, height: number): void { setContentHeight(context: RendererContext, height: number): void {
let key = context.screenId + "/" + context.lineId; const key = context.screenId + "/" + context.lineId;
this.termUsedRowsCache[key] = height; this.termUsedRowsCache[key] = height;
GlobalCommandRunner.setTermUsedRows(context, height); GlobalCommandRunner.setTermUsedRows(context, height);
} }
@ -622,7 +591,7 @@ class Model {
} }
getUIContext(): UIContextType { getUIContext(): UIContextType {
let rtn: UIContextType = { const rtn: UIContextType = {
sessionid: null, sessionid: null,
screenid: null, screenid: null,
remote: null, remote: null,
@ -630,10 +599,10 @@ class Model {
linenum: null, linenum: null,
build: appconst.VERSION + " " + appconst.BUILD, build: appconst.VERSION + " " + appconst.BUILD,
}; };
let session = this.getActiveSession(); const session = this.getActiveSession();
if (session != null) { if (session != null) {
rtn.sessionid = session.sessionId; rtn.sessionid = session.sessionId;
let screen = session.getActiveScreen(); const screen = session.getActiveScreen();
if (screen != null) { if (screen != null) {
rtn.screenid = screen.screenId; rtn.screenid = screen.screenId;
rtn.remote = screen.curRemote.get(); rtn.remote = screen.curRemote.get();
@ -653,7 +622,7 @@ class Model {
} }
onLCmd(e: any, mods: KeyModsType) { onLCmd(e: any, mods: KeyModsType) {
let screen = this.getActiveScreen(); const screen = this.getActiveScreen();
if (screen != null) { if (screen != null) {
GlobalCommandRunner.screenSetFocus("cmd"); GlobalCommandRunner.screenSetFocus("cmd");
} }
@ -671,11 +640,11 @@ class Model {
if (this.inputModel.hasFocus()) { if (this.inputModel.hasFocus()) {
return { cmdInputFocus: true }; return { cmdInputFocus: true };
} }
let lineElem: any = document.activeElement.closest(".line[data-lineid]"); const lineElem: any = document.activeElement.closest(".line[data-lineid]");
if (lineElem == null) { if (lineElem == null) {
return { cmdInputFocus: false }; return { cmdInputFocus: false };
} }
let lineNum = parseInt(lineElem.dataset.linenum); const lineNum = parseInt(lineElem.dataset.linenum);
return { return {
cmdInputFocus: false, cmdInputFocus: false,
lineid: lineElem.dataset.lineid, lineid: lineElem.dataset.lineid,
@ -685,17 +654,17 @@ class Model {
} }
cmdStatusUpdate(screenId: string, lineId: string, origStatus: string, newStatus: string) { cmdStatusUpdate(screenId: string, lineId: string, origStatus: string, newStatus: string) {
let wasRunning = cmdStatusIsRunning(origStatus); const wasRunning = cmdStatusIsRunning(origStatus);
let isRunning = cmdStatusIsRunning(newStatus); const isRunning = cmdStatusIsRunning(newStatus);
if (wasRunning && !isRunning) { if (wasRunning && !isRunning) {
let ptr = this.getActiveLine(screenId, lineId); const ptr = this.getActiveLine(screenId, lineId);
if (ptr != null) { if (ptr != null) {
let screen = ptr.screen; const screen = ptr.screen;
let renderer = screen.getRenderer(lineId); const renderer = screen.getRenderer(lineId);
if (renderer != null) { if (renderer != null) {
renderer.setIsDone(); renderer.setIsDone();
} }
let term = screen.getTermWrap(lineId); const term = screen.getTermWrap(lineId);
if (term != null) { if (term != null) {
term.cmdDone(); term.cmdDone();
} }
@ -747,21 +716,25 @@ class Model {
runUpdate(genUpdate: UpdateMessage, interactive: boolean) { runUpdate(genUpdate: UpdateMessage, interactive: boolean) {
mobx.action(() => { mobx.action(() => {
let oldContext = this.getUIContext(); const oldContext = this.getUIContext();
try { try {
this.runUpdate_internal(genUpdate, oldContext, interactive); this.runUpdate_internal(genUpdate, oldContext, interactive);
} catch (e) { } catch (e) {
console.log("error running update", e, genUpdate); console.warn("error running update", e, genUpdate);
throw e; throw e;
} }
let newContext = this.getUIContext(); const newContext = this.getUIContext();
if (oldContext.sessionid != newContext.sessionid || oldContext.screenid != newContext.screenid) { if (oldContext.sessionid != newContext.sessionid || oldContext.screenid != newContext.screenid) {
this.inputModel.resetInput(); this.inputModel.resetInput();
if ("cmdline" in genUpdate) { if (!("ptydata64" in genUpdate)) {
const reversedGenUpdate = genUpdate.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. // TODO a bit of a hack since this update gets applied in runUpdate_internal.
// we then undo that update with the resetInput, and then redo it with the line below // we then undo that update with the resetInput, and then redo it with the line below
// not sure how else to handle this for now though // not sure how else to handle this for now though
this.inputModel.updateCmdLine(genUpdate.cmdline); this.inputModel.updateCmdLine(lastCmdLine.cmdline);
}
} }
} else if (remotePtrToString(oldContext.remote) != remotePtrToString(newContext.remote)) { } else if (remotePtrToString(oldContext.remote) != remotePtrToString(newContext.remote)) {
this.inputModel.resetHistory(); this.inputModel.resetHistory();
@ -769,27 +742,10 @@ class Model {
})(); })();
} }
runUpdate_internal(genUpdate: UpdateMessage, uiContext: UIContextType, interactive: boolean) { updateScreens(screens: ScreenDataType[]): void {
if ("ptydata64" in genUpdate) { const mods = genMergeDataMap(
let ptyMsg: PtyDataUpdateType = genUpdate;
if (isBlank(ptyMsg.remoteid)) {
// regular update
this.updatePtyData(ptyMsg);
} else {
// remote update
let ptyData = base64ToArray(ptyMsg.ptydata64);
this.remotesModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
}
return;
}
let update: ModelUpdateType = genUpdate;
if ("screens" in update) {
if (update.connect) {
this.screenMap.clear();
}
let mods = genMergeDataMap(
this.screenMap, this.screenMap,
update.screens, screens,
(s: Screen) => s.screenId, (s: Screen) => s.screenId,
(sdata: ScreenDataType) => sdata.screenid, (sdata: ScreenDataType) => sdata.screenid,
(sdata: ScreenDataType) => new Screen(sdata, this) (sdata: ScreenDataType) => new Screen(sdata, this)
@ -798,116 +754,177 @@ class Model {
this.removeScreenLinesByScreenId(screenId); this.removeScreenLinesByScreenId(screenId);
} }
} }
if ("sessions" in update || "activesessionid" in update) {
if (update.connect) { updateSessions(sessions: SessionDataType[]): void {
this.sessionList.clear();
}
let [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
genMergeData( genMergeData(
this.sessionList, this.sessionList,
update.sessions, sessions,
(s: Session) => s.sessionId, (s: Session) => s.sessionId,
(sdata: SessionDataType) => sdata.sessionid, (sdata: SessionDataType) => sdata.sessionid,
(sdata: SessionDataType) => new Session(sdata, this), (sdata: SessionDataType) => new Session(sdata, this),
(s: Session) => s.sessionIdx.get() (s: Session) => s.sessionIdx.get()
); );
if ("activesessionid" in update) { }
let newSessionId = update.activesessionid;
updateActiveSession(sessionId: string): void {
const [oldActiveSessionId, oldActiveScreenId] = this.getActiveIds();
if (sessionId != null) {
const newSessionId = sessionId;
if (this.activeSessionId.get() != newSessionId) { if (this.activeSessionId.get() != newSessionId) {
this.activeSessionId.set(newSessionId); this.activeSessionId.set(newSessionId);
} }
} }
let [newActiveSessionId, newActiveScreenId] = this.getActiveIds(); const [newActiveSessionId, newActiveScreenId] = this.getActiveIds();
if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) { if (oldActiveSessionId != newActiveSessionId || oldActiveScreenId != newActiveScreenId) {
this.activeMainView.set("session"); this.activeMainView.set("session");
this.deactivateScreenLines(); this.deactivateScreenLines();
this.ws.watchScreen(newActiveSessionId, newActiveScreenId); this.ws.watchScreen(newActiveSessionId, newActiveScreenId);
} }
} }
if ("line" in update) {
this.addLineCmd(update.line, update.cmd, interactive); updateScreenNumRunningCommands(numRunningCommandUpdates: ScreenNumRunningCommandsUpdateType[]) {
} else if ("cmd" in update) { for (const update of numRunningCommandUpdates) {
this.updateCmd(update.cmd); this.getScreenById_single(update.screenid)?.setNumRunningCmds(update.num);
}
if ("lines" in update) {
for (const line of update.lines) {
this.addLineCmd(line, null, interactive);
} }
} }
if ("screenlines" in update) {
this.updateScreenLines(update.screenlines, false); updateScreenStatusIndicators(screenStatusIndicators: ScreenStatusIndicatorUpdateType[]) {
} for (const update of screenStatusIndicators) {
if ("remotes" in update) { this.getScreenById_single(update.screenid)?.setStatusIndicator(update.status);
if (update.connect) {
this.remotes.clear();
}
this.updateRemotes(update.remotes);
// This code's purpose is to show view remote connection modal when a new connection is added
if (update.remotes?.length && this.remotesModel.recentConnAddedState.get()) {
this.remotesModel.openReadModal(update.remotes[0].remoteid);
} }
} }
if ("mainview" in update) {
if (update.mainview == "plugins") { runUpdate_internal(genUpdate: UpdateMessage, uiContext: UIContextType, interactive: boolean) {
this.pluginsModel.showPluginsView(); if ("ptydata64" in genUpdate) {
} else if (update.mainview == "bookmarks") { const ptyMsg: PtyDataUpdateType = genUpdate;
this.bookmarksModel.showBookmarksView(update.bookmarks, update.selectedbookmark); if (isBlank(ptyMsg.remoteid)) {
} else if (update.mainview == "session") { // regular update
this.activeMainView.set("session"); this.updatePtyData(ptyMsg);
} else if (update.mainview == "history") {
this.historyViewModel.showHistoryView(update.historyviewdata);
} else { } else {
console.log("invalid mainview in update:", update.mainview); // remote update
const ptyData = base64ToArray(ptyMsg.ptydata64);
this.remotesModel.receiveData(ptyMsg.remoteid, ptyMsg.ptypos, ptyData);
} }
} else if ("bookmarks" in update) { return;
this.bookmarksModel.mergeBookmarks(update.bookmarks);
} }
if ("clientdata" in update) { 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);
}
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.clientData.set(update.clientdata); this.clientData.set(update.clientdata);
} } else if (update.cmdline != null) {
if (interactive && "info" in update) { this.inputModel.updateCmdLine(update.cmdline);
let info: InfoType = update.info; } 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); this.inputModel.flashInfoMsg(info, info.timeoutms);
} } else if (update.remoteview != null) {
if (interactive && "remoteview" in update) { const rview: RemoteViewType = update.remoteview;
let rview: RemoteViewType = update.remoteview;
if (rview.remoteedit != null) { if (rview.remoteedit != null) {
this.remotesModel.openEditModal({ ...rview.remoteedit }); this.remotesModel.openEditModal({ ...rview.remoteedit });
} }
} } else if (update.alertmessage != null) {
if (interactive && "alertmessage" in update) { const alertMessage: AlertMessageType = update.alertmessage;
let alertMessage: AlertMessageType = update.alertmessage;
this.showAlert(alertMessage); this.showAlert(alertMessage);
} } else if (update.history != null) {
if ("cmdline" in update) { if (
this.inputModel.updateCmdLine(update.cmdline); uiContext.sessionid == update.history.sessionid &&
} uiContext.screenid == update.history.screenid
if (interactive && "history" in update) { ) {
if (uiContext.sessionid == update.history.sessionid && uiContext.screenid == update.history.screenid) {
this.inputModel.setHistoryInfo(update.history); this.inputModel.setHistoryInfo(update.history);
} }
} else if (this.isDev) {
console.log("did not match update", update);
} }
if ("connect" in update) { } else if (this.isDev) {
this.sessionListLoaded.set(true); console.log("did not match update", update);
this.remotesLoaded.set(true);
}
if ("openaicmdinfochat" in update) {
this.inputModel.setOpenAICmdInfoChat(update.openaicmdinfochat);
}
if ("screenstatusindicators" in update) {
for (const indicator of update.screenstatusindicators) {
this.getScreenById_single(indicator.screenid)?.setStatusIndicator(indicator.status);
}
}
if ("screennumrunningcommands" in update) {
for (const snc of update.screennumrunningcommands) {
this.getScreenById_single(snc.screenid)?.setNumRunningCmds(snc.num);
}
}
if ("userinputrequest" in update) {
let userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} }
});
} }
updateRemotes(remotes: RemoteType[]): void { updateRemotes(remotes: RemoteType[]): void {
@ -919,7 +936,7 @@ class Model {
} }
getSessionNames(): Record<string, string> { getSessionNames(): Record<string, string> {
let rtn: Record<string, string> = {}; const rtn: Record<string, string> = {};
for (const session of this.sessionList) { for (const session of this.sessionList) {
rtn[session.sessionId] = session.name.get(); rtn[session.sessionId] = session.name.get();
} }
@ -927,8 +944,8 @@ class Model {
} }
getScreenNames(): Record<string, string> { getScreenNames(): Record<string, string> {
let rtn: Record<string, string> = {}; const rtn: Record<string, string> = {};
for (let screen of this.screenMap.values()) { for (const screen of this.screenMap.values()) {
rtn[screen.screenId] = screen.name.get(); rtn[screen.screenId] = screen.name.get();
} }
return rtn; return rtn;
@ -958,13 +975,13 @@ class Model {
updateScreenLines(slines: ScreenLinesType, load: boolean) { updateScreenLines(slines: ScreenLinesType, load: boolean) {
mobx.action(() => { mobx.action(() => {
let existingWin = this.screenLines.get(slines.screenid); const existingWin = this.screenLines.get(slines.screenid);
if (existingWin == null) { if (existingWin == null) {
if (!load) { if (!load) {
console.log("cannot update screen-lines that does not exist", slines.screenid); console.log("cannot update screen-lines that does not exist", slines.screenid);
return; return;
} }
let newWindow = new ScreenLines(slines.screenid); const newWindow = new ScreenLines(slines.screenid);
this.screenLines.set(slines.screenid, newWindow); this.screenLines.set(slines.screenid, newWindow);
newWindow.updateData(slines, load); newWindow.updateData(slines, load);
} else { } else {
@ -989,8 +1006,8 @@ class Model {
} }
getSessionScreens(sessionId: string): Screen[] { getSessionScreens(sessionId: string): Screen[] {
let rtn: Screen[] = []; const rtn: Screen[] = [];
for (let screen of this.screenMap.values()) { for (const screen of this.screenMap.values()) {
if (screen.sessionId == sessionId) { if (screen.sessionId == sessionId) {
rtn.push(screen); rtn.push(screen);
} }
@ -999,7 +1016,7 @@ class Model {
} }
getScreenLinesForActiveScreen(): ScreenLines { getScreenLinesForActiveScreen(): ScreenLines {
let screen = this.getActiveScreen(); const screen = this.getActiveScreen();
if (screen == null) { if (screen == null) {
return null; return null;
} }
@ -1007,7 +1024,7 @@ class Model {
} }
getActiveScreen(): Screen { getActiveScreen(): Screen {
let session = this.getActiveSession(); const session = this.getActiveSession();
if (session == null) { if (session == null) {
return null; return null;
} }
@ -1018,11 +1035,11 @@ class Model {
if (cmd == null || !cmd.restarted) { if (cmd == null || !cmd.restarted) {
return; return;
} }
let screen = this.screenMap.get(cmd.screenid); const screen = this.screenMap.get(cmd.screenid);
if (screen == null) { if (screen == null) {
return; return;
} }
let termWrap = screen.getTermWrap(cmd.lineid); const termWrap = screen.getTermWrap(cmd.lineid);
if (termWrap == null) { if (termWrap == null) {
return; return;
} }
@ -1030,7 +1047,7 @@ class Model {
} }
addLineCmd(line: LineType, cmd: CmdDataType, interactive: boolean) { addLineCmd(line: LineType, cmd: CmdDataType, interactive: boolean) {
let slines = this.getScreenLinesById(line.screenid); const slines = this.getScreenLinesById(line.screenid);
if (slines == null) { if (slines == null) {
return; return;
} }
@ -1039,7 +1056,7 @@ class Model {
} }
updateCmd(cmd: CmdDataType) { updateCmd(cmd: CmdDataType) {
let slines = this.screenLines.get(cmd.screenid); const slines = this.screenLines.get(cmd.screenid);
if (slines != null) { if (slines != null) {
slines.updateCmd(cmd); slines.updateCmd(cmd);
} }
@ -1050,12 +1067,12 @@ class Model {
if (update == null || "ptydata64" in update) { if (update == null || "ptydata64" in update) {
return false; return false;
} }
return update.info != null || update.history != null; return update.some((u) => u.info != null || u.history != null);
} }
getClientDataLoop(loopNum: number): void { getClientDataLoop(loopNum: number): void {
this.getClientData(); this.getClientData();
let clientStop = this.getHasClientStop(); const clientStop = this.getHasClientStop();
if (this.clientData.get() != null && !clientStop) { if (this.clientData.get() != null && !clientStop) {
return; return;
} }
@ -1073,13 +1090,13 @@ class Model {
} }
getClientData(): void { getClientData(): void {
let url = new URL(this.getBaseHostPort() + "/api/get-client-data"); const url = new URL(this.getBaseHostPort() + "/api/get-client-data");
let fetchHeaders = this.getFetchHeaders(); const fetchHeaders = this.getFetchHeaders();
fetch(url, { method: "post", body: null, headers: fetchHeaders }) fetch(url, { method: "post", body: null, headers: fetchHeaders })
.then((resp) => handleJsonFetchResponse(url, resp)) .then((resp) => handleJsonFetchResponse(url, resp))
.then((data) => { .then((data) => {
mobx.action(() => { mobx.action(() => {
let clientData: ClientDataType = data.data; const clientData: ClientDataType = data.data;
this.clientData.set(clientData); this.clientData.set(clientData);
})(); })();
}) })
@ -1096,10 +1113,10 @@ class Model {
} }
} }
// adding cmdStr for debugging only (easily filter run-command calls in the network tab of debugger) // adding cmdStr for debugging only (easily filter run-command calls in the network tab of debugger)
let cmdStr = cmdPk.metacmd + (cmdPk.metasubcmd ? ":" + cmdPk.metasubcmd : ""); const cmdStr = cmdPk.metacmd + (cmdPk.metasubcmd ? ":" + cmdPk.metasubcmd : "");
let url = new URL(this.getBaseHostPort() + "/api/run-command?cmd=" + cmdStr); const url = new URL(this.getBaseHostPort() + "/api/run-command?cmd=" + cmdStr);
let fetchHeaders = this.getFetchHeaders(); const fetchHeaders = this.getFetchHeaders();
let prtn = fetch(url, { const prtn = fetch(url, {
method: "post", method: "post",
body: JSON.stringify(cmdPk), body: JSON.stringify(cmdPk),
headers: fetchHeaders, headers: fetchHeaders,
@ -1107,7 +1124,7 @@ class Model {
.then((resp) => handleJsonFetchResponse(url, resp)) .then((resp) => handleJsonFetchResponse(url, resp))
.then((data) => { .then((data) => {
mobx.action(() => { mobx.action(() => {
let update = data.data; const update = data.data;
if (update != null) { if (update != null) {
this.runUpdate(update, interactive); this.runUpdate(update, interactive);
} }
@ -1135,7 +1152,7 @@ class Model {
kwargs: Record<string, string>, kwargs: Record<string, string>,
interactive: boolean interactive: boolean
): Promise<CommandRtnType> { ): Promise<CommandRtnType> {
let pk: FeCmdPacketType = { const pk: FeCmdPacketType = {
type: "fecmd", type: "fecmd",
metacmd: metaCmd, metacmd: metaCmd,
metasubcmd: metaSubCmd, metasubcmd: metaSubCmd,
@ -1157,9 +1174,9 @@ class Model {
} }
submitChatInfoCommand(chatMsg: string, curLineStr: string, clear: boolean): Promise<CommandRtnType> { submitChatInfoCommand(chatMsg: string, curLineStr: string, clear: boolean): Promise<CommandRtnType> {
let commandStr = "/chat " + chatMsg; const commandStr = "/chat " + chatMsg;
let interactive = false; const interactive = false;
let pk: FeCmdPacketType = { const pk: FeCmdPacketType = {
type: "fecmd", type: "fecmd",
metacmd: "eval", metacmd: "eval",
args: [commandStr], args: [commandStr],
@ -1179,7 +1196,7 @@ class Model {
} }
submitRawCommand(cmdStr: string, addToHistory: boolean, interactive: boolean): Promise<CommandRtnType> { submitRawCommand(cmdStr: string, addToHistory: boolean, interactive: boolean): Promise<CommandRtnType> {
let pk: FeCmdPacketType = { const pk: FeCmdPacketType = {
type: "fecmd", type: "fecmd",
metacmd: "eval", metacmd: "eval",
args: [cmdStr], args: [cmdStr],
@ -1196,16 +1213,16 @@ class Model {
// returns [sessionId, screenId] // returns [sessionId, screenId]
getActiveIds(): [string, string] { getActiveIds(): [string, string] {
let activeSession = this.getActiveSession(); const activeSession = this.getActiveSession();
let activeScreen = this.getActiveScreen(); const activeScreen = this.getActiveScreen();
return [activeSession?.sessionId, activeScreen?.screenId]; return [activeSession?.sessionId, activeScreen?.screenId];
} }
_loadScreenLinesAsync(newWin: ScreenLines) { _loadScreenLinesAsync(newWin: ScreenLines) {
this.screenLines.set(newWin.screenId, newWin); this.screenLines.set(newWin.screenId, newWin);
let usp = new URLSearchParams({ screenid: newWin.screenId }); const usp = new URLSearchParams({ screenid: newWin.screenId });
let url = new URL(this.getBaseHostPort() + "/api/get-screen-lines?" + usp.toString()); const url = new URL(this.getBaseHostPort() + "/api/get-screen-lines?" + usp.toString());
let fetchHeaders = this.getFetchHeaders(); const fetchHeaders = this.getFetchHeaders();
fetch(url, { headers: fetchHeaders }) fetch(url, { headers: fetchHeaders })
.then((resp) => handleJsonFetchResponse(url, resp)) .then((resp) => handleJsonFetchResponse(url, resp))
.then((data) => { .then((data) => {
@ -1213,7 +1230,7 @@ class Model {
console.log("null screen-lines returned from get-screen-lines"); console.log("null screen-lines returned from get-screen-lines");
return; return;
} }
let slines: ScreenLinesType = data.data; const slines: ScreenLinesType = data.data;
this.updateScreenLines(slines, true); this.updateScreenLines(slines, true);
}) })
.catch((err) => { .catch((err) => {
@ -1222,7 +1239,7 @@ class Model {
} }
loadScreenLines(screenId: string): ScreenLines { loadScreenLines(screenId: string): ScreenLines {
let newWin = new ScreenLines(screenId); const newWin = new ScreenLines(screenId);
setTimeout(() => this._loadScreenLinesAsync(newWin), 0); setTimeout(() => this._loadScreenLinesAsync(newWin), 0);
return newWin; return newWin;
} }
@ -1235,7 +1252,7 @@ class Model {
} }
getRemoteNames(): Record<string, string> { getRemoteNames(): Record<string, string> {
let rtn: Record<string, string> = {}; const rtn: Record<string, string> = {};
for (const remote of this.remotes) { for (const remote of this.remotes) {
if (!isBlank(remote.remotealias)) { if (!isBlank(remote.remotealias)) {
rtn[remote.remoteid] = remote.remotealias; rtn[remote.remoteid] = remote.remotealias;
@ -1260,7 +1277,7 @@ class Model {
} }
getCmdByScreenLine(screenId: string, lineId: string): Cmd { getCmdByScreenLine(screenId: string, lineId: string): Cmd {
let slines = this.getScreenLinesById(screenId); const slines = this.getScreenLinesById(screenId);
if (slines == null) { if (slines == null) {
return null; return null;
} }
@ -1268,14 +1285,14 @@ class Model {
} }
getActiveLine(screenId: string, lineid: string): SWLinePtr { getActiveLine(screenId: string, lineid: string): SWLinePtr {
let slines = this.screenLines.get(screenId); const slines = this.screenLines.get(screenId);
if (slines == null) { if (slines == null) {
return null; return null;
} }
if (!slines.loaded.get()) { if (!slines.loaded.get()) {
return null; return null;
} }
let cmd = slines.getCmd(lineid); const cmd = slines.getCmd(lineid);
if (cmd == null) { if (cmd == null) {
return null; return null;
} }
@ -1289,12 +1306,12 @@ class Model {
if (line == null) { if (line == null) {
return null; return null;
} }
let screen = this.getScreenById_single(slines.screenId); const screen = this.getScreenById_single(slines.screenId);
return { line: line, slines: slines, screen: screen }; return { line: line, slines: slines, screen: screen };
} }
updatePtyData(ptyMsg: PtyDataUpdateType): void { updatePtyData(ptyMsg: PtyDataUpdateType): void {
let linePtr = this.getActiveLine(ptyMsg.screenid, ptyMsg.lineid); const linePtr = this.getActiveLine(ptyMsg.screenid, ptyMsg.lineid);
if (linePtr != null) { if (linePtr != null) {
linePtr.screen.updatePtyData(ptyMsg); linePtr.screen.updatePtyData(ptyMsg);
} }
@ -1320,7 +1337,7 @@ class Model {
} }
sendCmdInputText(screenId: string, sp: StrWithPos) { sendCmdInputText(screenId: string, sp: StrWithPos) {
let pk: CmdInputTextPacketType = { const pk: CmdInputTextPacketType = {
type: "cmdinputtext", type: "cmdinputtext",
seqnum: this.getNextPacketSeqNum(), seqnum: this.getNextPacketSeqNum(),
screenid: screenId, screenid: screenId,
@ -1334,7 +1351,7 @@ class Model {
} }
resolveRemoteIdToRef(remoteId: string) { resolveRemoteIdToRef(remoteId: string) {
let remote = this.getRemote(remoteId); const remote = this.getRemote(remoteId);
if (remote == null) { if (remote == null) {
return "[unknown]"; return "[unknown]";
} }
@ -1345,7 +1362,7 @@ class Model {
} }
resolveRemoteIdToFullRef(remoteId: string) { resolveRemoteIdToFullRef(remoteId: string) {
let remote = this.getRemote(remoteId); const remote = this.getRemote(remoteId);
if (remote == null) { if (remote == null) {
return "[unknown]"; return "[unknown]";
} }
@ -1356,17 +1373,17 @@ class Model {
} }
readRemoteFile(screenId: string, lineId: string, path: string): Promise<ExtFile> { readRemoteFile(screenId: string, lineId: string, path: string): Promise<ExtFile> {
let urlParams = { const urlParams = {
screenid: screenId, screenid: screenId,
lineid: lineId, lineid: lineId,
path: path, path: path,
}; };
let usp = new URLSearchParams(urlParams); const usp = new URLSearchParams(urlParams);
let url = new URL(this.getBaseHostPort() + "/api/read-file?" + usp.toString()); const url = new URL(this.getBaseHostPort() + "/api/read-file?" + usp.toString());
let fetchHeaders = this.getFetchHeaders(); const fetchHeaders = this.getFetchHeaders();
let fileInfo: FileInfoType = null; let fileInfo: FileInfoType = null;
let badResponseStr: string = null; let badResponseStr: string = null;
let prtn = fetch(url, { method: "get", headers: fetchHeaders }) const prtn = fetch(url, { method: "get", headers: fetchHeaders })
.then((resp) => { .then((resp) => {
if (!resp.ok) { if (!resp.ok) {
badResponseStr = sprintf( badResponseStr = sprintf(
@ -1381,14 +1398,14 @@ class Model {
}) })
.then((blobOrText: any) => { .then((blobOrText: any) => {
if (blobOrText instanceof Blob) { if (blobOrText instanceof Blob) {
let blob: Blob = blobOrText; const blob: Blob = blobOrText;
let file = new File([blob], fileInfo.name, { type: blob.type, lastModified: fileInfo.modts }); const file = new File([blob], fileInfo.name, { type: blob.type, lastModified: fileInfo.modts });
let isWriteable = (fileInfo.perm & 0o222) > 0; // checks for unix permission "w" bits const isWriteable = (fileInfo.perm & 0o222) > 0; // checks for unix permission "w" bits
(file as any).readOnly = !isWriteable; (file as any).readOnly = !isWriteable;
(file as any).notFound = !!fileInfo.notfound; (file as any).notFound = !!fileInfo.notfound;
return file as ExtFile; return file as ExtFile;
} else { } else {
let textError: string = blobOrText; const textError: string = blobOrText;
if (textError == null || textError.length == 0) { if (textError == null || textError.length == 0) {
throw new Error(badResponseStr); throw new Error(badResponseStr);
} }
@ -1398,7 +1415,7 @@ class Model {
return prtn; return prtn;
} }
writeRemoteFile( async writeRemoteFile(
screenId: string, screenId: string,
lineId: string, lineId: string,
path: string, path: string,
@ -1406,24 +1423,21 @@ class Model {
opts?: { useTemp?: boolean } opts?: { useTemp?: boolean }
): Promise<void> { ): Promise<void> {
opts = opts || {}; opts = opts || {};
let params = { const params = {
screenid: screenId, screenid: screenId,
lineid: lineId, lineid: lineId,
path: path, path: path,
usetemp: !!opts.useTemp, usetemp: !!opts.useTemp,
}; };
let formData = new FormData(); const formData = new FormData();
formData.append("params", JSON.stringify(params)); formData.append("params", JSON.stringify(params));
let blob = new Blob([data], { type: "application/octet-stream" }); const blob = new Blob([data], { type: "application/octet-stream" });
formData.append("data", blob); formData.append("data", blob);
let url = new URL(this.getBaseHostPort() + "/api/write-file"); const url = new URL(this.getBaseHostPort() + "/api/write-file");
let fetchHeaders = this.getFetchHeaders(); const fetchHeaders = this.getFetchHeaders();
let prtn = fetch(url, { method: "post", headers: fetchHeaders, body: formData }); const prtn = fetch(url, { method: "post", headers: fetchHeaders, body: formData });
return prtn const resp = await prtn;
.then((resp) => handleJsonFetchResponse(url, resp)) const _ = await handleJsonFetchResponse(url, resp);
.then((_) => {
return;
});
} }
} }

View File

@ -18,9 +18,9 @@ import {
genMergeSimpleData, genMergeSimpleData,
boundInt, boundInt,
isModKeyPress, isModKeyPress,
} from "../util/util"; } from "@/util/util";
import { TermWrap } from "../plugins/terminal/term"; import { TermWrap } from "@/plugins/terminal/term";
import { PluginModel } from "../plugins/plugins"; import { PluginModel } from "@/plugins/plugins";
import { import {
SessionDataType, SessionDataType,
LineType, LineType,
@ -76,15 +76,15 @@ import {
windowHeightToRows, windowHeightToRows,
termWidthFromCols, termWidthFromCols,
termHeightFromRows, termHeightFromRows,
} from "../util/textmeasure"; } from "@/util/textmeasure";
import dayjs from "dayjs"; import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat"; import localizedFormat from "dayjs/plugin/localizedFormat";
import customParseFormat from "dayjs/plugin/customParseFormat"; import customParseFormat from "dayjs/plugin/customParseFormat";
import { getRendererContext, cmdStatusIsRunning } from "../app/line/lineutil"; import { getRendererContext, cmdStatusIsRunning } from "@/app/line/lineutil";
import { MagicLayout } from "../app/magiclayout"; import { MagicLayout } from "@/app/magiclayout";
import { modalsRegistry } from "../app/common/modals/registry"; import { modalsRegistry } from "@/modals/registry";
import * as appconst from "../app/appconst"; import * as appconst from "@/app/appconst";
import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "../util/keyutil"; import { checkKeyPressed, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
dayjs.extend(customParseFormat); dayjs.extend(customParseFormat);
dayjs.extend(localizedFormat); dayjs.extend(localizedFormat);

View File

@ -2,10 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as mobx from "mobx"; import * as mobx from "mobx";
import { PluginModel } from "../plugins/plugins"; import { PluginModel } from "@/plugins/plugins";
import { RendererPluginType } from "../types/types";
import { OV } from "../types/types";
import { GlobalCommandRunner } from "./global";
import { Model } from "./model"; import { Model } from "./model";
class PluginsModel { class PluginsModel {

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