Compare commits

..

51 Commits

Author SHA1 Message Date
Red Adaya 1d0b7bd770 split models/model 2024-02-08 16:40:15 +08:00
Red Adaya a9b9ff3362 fix conflicts 2024-02-08 14:48:29 +08:00
Red Adaya d2829c466d fix ptyDataSource errors 2024-02-08 14:26:49 +08:00
Red Adaya f65e632c35 move model_old to models 2024-02-08 13:37:42 +08:00
Red Adaya acb3bd303b rename model to models 2024-02-08 13:37:02 +08:00
Red Adaya 3ad7feedad replace /model_old imports with /model 2024-02-08 13:34:46 +08:00
Red Adaya 24690ff931 add index.ts inside model dir 2024-02-08 09:35:52 +08:00
Red Adaya f510e1c904 cleanup connectionsview model 2024-02-08 09:23:44 +08:00
Red Adaya 35fa59aadd cleanup screenlinecontainer model 2024-02-08 09:20:30 +08:00
Red Adaya d414657252 cleanup session model 2024-02-08 09:16:02 +08:00
Red Adaya b59dd6960f cleanup screenlines model 2024-02-08 09:14:30 +08:00
Red Adaya 1ae74b767d cleanup screen model 2024-02-08 09:13:04 +08:00
Red Adaya 7fa862b402 cleanup remote model 2024-02-08 09:09:16 +08:00
Red Adaya 970c5c30fd cleanup plugins model 2024-02-08 09:07:26 +08:00
Red Adaya 2bcc8756e7 cleanup main model 2024-02-08 09:06:09 +08:00
Red Adaya 5ad26b10cd cleanup mainsidebar model 2024-02-08 09:02:31 +08:00
Red Adaya ef7acda1bb cleanup linecontainer model 2024-02-08 09:01:08 +08:00
Red Adaya 29cd754358 cleanup input model 2024-02-08 08:59:40 +08:00
Red Adaya 648d98f065 cleanup historyview model 2024-02-08 08:55:56 +08:00
Red Adaya 5770e482a1 cleanup commandrunner model 2024-02-08 08:44:01 +08:00
Red Adaya 23b3250f18 remove dayjs import from bookmarks model 2024-02-08 08:41:22 +08:00
Red Adaya b62977470b cleanup cmd model 2024-02-08 08:40:33 +08:00
Red Adaya 07f2794b1b cleanup clientsettingsview model 2024-02-08 08:37:56 +08:00
Red Adaya cf27a8baff cleanup bookmarks model 2024-02-08 08:37:17 +08:00
Red Adaya aace48d42a init 2024-02-08 08:35:30 +08:00
Red Adaya 15d02f3357 remove styles not related to Tooltip 2024-02-06 13:42:09 +08:00
Red Adaya 39d371681c remove styles not related to Toggle 2024-02-06 13:41:23 +08:00
Red Adaya 412c3ac5a2 remove styles not related to TextField 2024-02-06 13:37:15 +08:00
Red Adaya 36484621ec remove styles not related to Status 2024-02-06 13:35:15 +08:00
Red Adaya 72231ead1d SettingsError doesn't need styles cos it uses classnames in app.less 2024-02-06 13:33:42 +08:00
Red Adaya 81c1fd43f7 remove styles not related to ResizableSidebar 2024-02-06 13:31:20 +08:00
Red Adaya c9a73c2204 RemoteStatusLight no longer used. It's replaced by Status component. 2024-02-06 13:27:58 +08:00
Red Adaya 434f980b49 remove styles not related to PasswordField 2024-02-06 13:26:22 +08:00
Red Adaya 75d462764d NumberField doesn't need styles cos it extends TextField 2024-02-06 13:24:50 +08:00
Red Adaya c58c88af4e remove styles not related to modal 2024-02-06 13:23:32 +08:00
Red Adaya 05be301ea2 remove styles not related to markdown 2024-02-06 13:22:04 +08:00
Red Adaya 27e5a9154d LinkButton doesn't need styles cos it's extends Button component 2024-02-06 13:19:31 +08:00
Red Adaya a7d2148bc0 remove styles not related to InputDecoration 2024-02-06 10:28:39 +08:00
Red Adaya ead75880e1 remove styles not related to InlineSettingsTextEdit 2024-02-06 10:17:18 +08:00
Red Adaya 0bdb50cf91 fix import error 2024-02-06 10:12:32 +08:00
Red Adaya abf3448f07 InfoMessage conmponent no longer needed 2024-02-06 10:09:01 +08:00
Red Adaya 8988d26867 remove old connections 2024-02-06 09:50:57 +08:00
Red Adaya a084ca4801 IconButton doesn't need styles because it extends Button 2024-02-06 09:30:10 +08:00
Red Adaya 36e2578a03 remove styles not related to dropdown 2024-02-06 09:26:46 +08:00
Red Adaya a49487c035 renderCmdText doesn't need styles 2024-02-06 09:15:51 +08:00
Red Adaya 03296043fb remove styles not related to CmdStrCode 2024-02-06 09:10:35 +08:00
Red Adaya ec797f55ff remove styles not related to checkbox 2024-02-06 09:05:18 +08:00
Red Adaya 550461167b init 2024-02-05 23:08:59 +08:00
Red Adaya cf5d2ecddf Merge branch 'main' of github.com:wavetermdev/waveterm into split-components-in-common 2024-02-05 23:07:48 +08:00
Red Adaya c4128cd266 init 2024-02-02 23:26:33 +08:00
Red Adaya 17bb07a47d split settings modals 2024-02-02 16:50:44 +08:00
33 changed files with 1002 additions and 2416 deletions
-1
View File
@@ -8,7 +8,6 @@ export const SESSION_SETTINGS = "sessionSettings";
export const LINE_SETTINGS = "lineSettings"; export const LINE_SETTINGS = "lineSettings";
export const CLIENT_SETTINGS = "clientSettings"; export const CLIENT_SETTINGS = "clientSettings";
export const TAB_SWITCHER = "tabSwitcher"; export const TAB_SWITCHER = "tabSwitcher";
export const USER_INPUT = "userInput";
export const LineContainer_Main = "main"; export const LineContainer_Main = "main";
export const LineContainer_History = "history"; export const LineContainer_History = "history";
-1
View File
@@ -5,7 +5,6 @@
margin-bottom: 10px; margin-bottom: 10px;
font-family: @markdown-font; font-family: @markdown-font;
font-size: 14px; font-size: 14px;
overflow-wrap: break-word;
code { code {
background-color: @markdown-highlight; background-color: @markdown-highlight;
+1 -7
View File
@@ -60,13 +60,7 @@ class PasswordField extends TextField {
}; };
return ( return (
<div <div className={cn(`wave-textfield wave-password ${className || ""}`, { focused: focused, error: error })}>
className={cn(`wave-textfield wave-password ${className || ""}`, {
focused: focused,
error: error,
"no-label": !label,
})}
>
{decoration?.startDecoration && <>{decoration.startDecoration}</>} {decoration?.startDecoration && <>{decoration.startDecoration}</>}
<div className="wave-textfield-inner"> <div className="wave-textfield-inner">
<label <label
-1
View File
@@ -9,4 +9,3 @@ export { TabSwitcherModal } from "./tabswitcher";
export { SessionSettingsModal } from "./sessionsettings"; export { SessionSettingsModal } from "./sessionsettings";
export { ScreenSettingsModal } from "./screensettings"; export { ScreenSettingsModal } from "./screensettings";
export { LineSettingsModal } from "./linesettings"; export { LineSettingsModal } from "./linesettings";
export { UserInputModal } from "./userinput";
+1 -1
View File
@@ -17,7 +17,7 @@ class ModalsProvider extends React.Component {
for (let i = 0; i < store.length; i++) { for (let i = 0; i < store.length; i++) {
let entry = store[i]; let entry = store[i];
let Comp = entry.component; let Comp = entry.component;
rtn.push(<Comp key={entry.uniqueKey} {...entry.props} />); rtn.push(<Comp key={entry.uniqueKey} />);
} }
return <>{rtn}</>; return <>{rtn}</>;
} }
+10 -12
View File
@@ -12,21 +12,19 @@ import {
SessionSettingsModal, SessionSettingsModal,
ScreenSettingsModal, ScreenSettingsModal,
LineSettingsModal, LineSettingsModal,
UserInputModal,
} from "../modals"; } from "../modals";
import * as constants from "../../appconst"; import * as constants from "../../appconst";
const modalsRegistry: { [key: string]: React.ComponentType } = { const modalsRegistry: { [key: string]: () => React.ReactElement } = {
[constants.ABOUT]: AboutModal, [constants.ABOUT]: () => <AboutModal />,
[constants.CREATE_REMOTE]: CreateRemoteConnModal, [constants.CREATE_REMOTE]: () => <CreateRemoteConnModal />,
[constants.VIEW_REMOTE]: ViewRemoteConnDetailModal, [constants.VIEW_REMOTE]: () => <ViewRemoteConnDetailModal />,
[constants.EDIT_REMOTE]: EditRemoteConnModal, [constants.EDIT_REMOTE]: () => <EditRemoteConnModal />,
[constants.ALERT]: AlertModal, [constants.ALERT]: () => <AlertModal />,
[constants.SCREEN_SETTINGS]: ScreenSettingsModal, [constants.SCREEN_SETTINGS]: () => <ScreenSettingsModal />,
[constants.SESSION_SETTINGS]: SessionSettingsModal, [constants.SESSION_SETTINGS]: () => <SessionSettingsModal />,
[constants.LINE_SETTINGS]: LineSettingsModal, [constants.LINE_SETTINGS]: () => <LineSettingsModal />,
[constants.TAB_SWITCHER]: TabSwitcherModal, [constants.TAB_SWITCHER]: () => <TabSwitcherModal />,
[constants.USER_INPUT]: UserInputModal,
}; };
export { modalsRegistry }; export { modalsRegistry };
-15
View File
@@ -1,15 +0,0 @@
@import "../../../app/common/themes/themes.less";
.userinput-modal {
width: 500px;
.wave-modal-content {
.wave-modal-body {
padding: 20px 20px;
.userinput-query {
margin-bottom: 10px;
}
}
}
}
-88
View File
@@ -1,88 +0,0 @@
import * as React from "react";
import { GlobalModel } from "../../../models";
import { Choose, When, If } from "tsx-control-statements/components";
import { Modal, PasswordField, Markdown } from "../elements";
import { UserInputRequest } from "../../../types/types";
import "./userinput.less";
export const UserInputModal = (userInputRequest: UserInputRequest) => {
const [responseText, setResponseText] = React.useState("");
const [countdown, setCountdown] = React.useState(Math.floor(userInputRequest.timeoutms / 1000));
const closeModal = React.useCallback(() => {
GlobalModel.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
errormsg: "Canceled by the user",
});
GlobalModel.remotesModel.closeModal();
}, [responseText, userInputRequest]);
const handleSendText = React.useCallback(() => {
GlobalModel.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
text: responseText,
});
GlobalModel.remotesModel.closeModal();
}, [responseText, userInputRequest]);
const handleSendConfirm = React.useCallback(
(response: boolean) => {
GlobalModel.sendUserInput({
type: "userinputresp",
requestid: userInputRequest.requestid,
confirm: response,
});
GlobalModel.remotesModel.closeModal();
},
[userInputRequest]
);
React.useEffect(() => {
let timeout: ReturnType<typeof setTimeout>;
if (countdown == 0) {
timeout = setTimeout(() => {
GlobalModel.remotesModel.closeModal();
}, 300);
} else {
timeout = setTimeout(() => {
setCountdown(countdown - 1);
}, 1000);
}
return () => clearTimeout(timeout);
}, [countdown]);
return (
<Modal className="userinput-modal">
<Modal.Header onClose={closeModal} title={userInputRequest.title + ` (${countdown})`} />
<div className="wave-modal-body">
<div className="userinput-query">
<If condition={userInputRequest.markdown}>
<Markdown text={userInputRequest.querytext} />
</If>
<If condition={!userInputRequest.markdown}>{userInputRequest.querytext}</If>
</div>
<Choose>
<When condition={userInputRequest.responsetype == "text"}>
<PasswordField onChange={setResponseText} value={responseText} maxLength={400} />
</When>
</Choose>
</div>
<Choose>
<When condition={userInputRequest.responsetype == "text"}>
<Modal.Footer onCancel={closeModal} onOk={handleSendText} okLabel="Continue" />
</When>
<When condition={userInputRequest.responsetype == "confirm"}>
<Modal.Footer
onCancel={() => handleSendConfirm(false)}
onOk={() => handleSendConfirm(true)}
okLabel="Yes"
cancelLabel="No"
/>
</When>
</Choose>
</Modal>
);
};
@@ -54,8 +54,6 @@
} }
.remote-detail { .remote-detail {
width: 100%;
.settings-field { .settings-field {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -93,9 +91,8 @@
} }
.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
@@ -114,6 +111,7 @@
.xterm-screen { .xterm-screen {
padding: 10px; padding: 10px;
width: 541px !important; // Needed to override plugin width
} }
} }
} }
@@ -12,10 +12,12 @@ 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 "../../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();
@@ -291,7 +293,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(appconst.RemotePtyCols, termFontSize); let termWidth = textmeasure.termWidthFromCols(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;
@@ -369,7 +371,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(appconst.RemotePtyRows, termFontSize), height: textmeasure.termHeightFromRows(RemotePtyRows, termFontSize),
width: termWidth, width: termWidth,
}} }}
></div> ></div>
+14 -21
View File
@@ -14,7 +14,6 @@ import { sprintf } from "sprintf-js";
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";
import * as T from "../types/types";
const WaveAppPathVarName = "WAVETERM_APP_PATH"; const WaveAppPathVarName = "WAVETERM_APP_PATH";
const WaveDevVarName = "WAVETERM_DEV"; const WaveDevVarName = "WAVETERM_DEV";
@@ -38,25 +37,21 @@ ensureDir(waveHome);
// normalize darwin/x64 to darwin/amd64 for GOARCH compatibility // normalize darwin/x64 to darwin/amd64 for GOARCH compatibility
let unamePlatform = process.platform; let unamePlatform = process.platform;
let unameArch: string = process.arch; let unameArch: string = process.arch;
``;
if (unameArch == "x64") { if (unameArch == "x64") {
unameArch = "amd64"; unameArch = "amd64";
} }
let logger: winston.Logger; let logger;
let transports: winston.transport[] = [ let loggerConfig = {
new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" }),
];
if (isDev) {
transports.push(new winston.transports.Console());
}
let loggerConfig: winston.LoggerOptions = {
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: transports, transports: [new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" })],
}; };
if (isDev) {
loggerConfig.transports.push(new winston.transports.Console());
}
logger = winston.createLogger(loggerConfig); logger = winston.createLogger(loggerConfig);
function log(...msg) { function log(...msg) {
try { try {
@@ -80,7 +75,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: child_process.ChildProcessWithoutNullStreams | null = null; let waveSrvProc = null;
let waveSrvShouldRestart = false; let waveSrvShouldRestart = false;
electron.dialog.showErrorBox = (title, content) => { electron.dialog.showErrorBox = (title, content) => {
@@ -106,8 +101,8 @@ 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: string = process.env.HOME as string; let homeDir = process.env.HOME;
let promptHome: string = path.join(homeDir, "prompt"); let promptHome = 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;
@@ -175,7 +170,7 @@ function readAuthKey() {
return authKeyStr.trim(); return authKeyStr.trim();
} }
let menuTemplate: (Electron.MenuItem | Electron.MenuItemConstructorOptions)[] = [ let menuTemplate = [
{ {
role: "appMenu", role: "appMenu",
submenu: [ submenu: [
@@ -255,7 +250,7 @@ function shFrameNavHandler(event: any, url: any) {
return; return;
} }
function createMainWindow(clientData: T.ClientDataType | null) { function createMainWindow(clientData) {
let bounds = calcBounds(clientData); let bounds = calcBounds(clientData);
setKeyUtilPlatform(platform()); setKeyUtilPlatform(platform());
let win = new electron.BrowserWindow({ let win = new electron.BrowserWindow({
@@ -614,9 +609,7 @@ function runWaveSrv() {
pReject(new Error(sprintf("failed to start local server (%s)", waveSrvCmd))); pReject(new Error(sprintf("failed to start local server (%s)", waveSrvCmd)));
if (waveSrvShouldRestart) { if (waveSrvShouldRestart) {
waveSrvShouldRestart = false; waveSrvShouldRestart = false;
// small timeout here so we can reuse the listen socket this.runWaveSrv();
// this is normally only used in dev mode for debugging
setTimeout(runWaveSrv, 500);
} }
}); });
proc.on("spawn", (e) => { proc.on("spawn", (e) => {
@@ -651,7 +644,7 @@ 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: Electron.MenuItem; let menuItem = null;
if (opts.showCut) { if (opts.showCut) {
menuItem = new electron.MenuItem({ label: "Cut", role: "cut" }); menuItem = new electron.MenuItem({ label: "Cut", role: "cut" });
menu.append(menuItem); menu.append(menuItem);
@@ -664,7 +657,7 @@ electron.ipcMain.on("context-editmenu", (event, { x, y }, opts) => {
}); });
async function createMainWindowWrap() { async function createMainWindowWrap() {
let clientData: T.ClientDataType | null = null; let clientData = null;
try { try {
clientData = await getClientDataPoll(1); clientData = await getClientDataPoll(1);
} catch (e) { } catch (e) {
+15 -10
View File
@@ -13,10 +13,10 @@ import {
HistoryQueryOpts, HistoryQueryOpts,
HistoryTypeStrs, HistoryTypeStrs,
OpenAICmdInfoChatMessageType, OpenAICmdInfoChatMessageType,
OV,
StrWithPos,
} from "../types/types"; } from "../types/types";
import { StrWithPos } from "../types/types";
import * as appconst from "../app/appconst"; 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,6 +207,7 @@ 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;
})(); })();
} }
@@ -623,17 +624,13 @@ 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 {
@@ -724,6 +721,14 @@ 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;
} }
+3 -3
View File
@@ -8,14 +8,14 @@ import { modalsRegistry } from "../app/common/modals/registry";
import { OArr } from "../types/types"; 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" });
pushModal(modalId: string, props?: any) { pushModal(modalId: string) {
const modalFactory = modalsRegistry[modalId]; const modalFactory = modalsRegistry[modalId];
if (modalFactory && !this.store.some((modal) => modal.id === modalId)) { if (modalFactory && !this.store.some((modal) => modal.id === modalId)) {
mobx.action(() => { mobx.action(() => {
this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4(), props }); this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4() });
})(); })();
} }
} }
+243 -300
View File
File diff suppressed because it is too large Load Diff
@@ -51,8 +51,6 @@ import {
HistoryViewDataType, HistoryViewDataType,
AlertMessageType, AlertMessageType,
HistorySearchParams, HistorySearchParams,
UserInputRequest,
UserInputResponsePacket,
FocusTypeStrs, FocusTypeStrs,
ScreenLinesType, ScreenLinesType,
HistoryTypeStrs, HistoryTypeStrs,
@@ -3378,14 +3376,14 @@ class RemotesModel {
} }
class ModalsModel { class ModalsModel {
store: OArr<T.ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store", deep: false }); store: OArr<T.ModalStoreEntry> = mobx.observable.array([], { name: "ModalsModel-store" });
pushModal(modalId: string, props?: any) { pushModal(modalId: string) {
const modalFactory = modalsRegistry[modalId]; const modalFactory = modalsRegistry[modalId];
if (modalFactory && !this.store.some((modal) => modal.id === modalId)) { if (modalFactory && !this.store.some((modal) => modal.id === modalId)) {
mobx.action(() => { mobx.action(() => {
this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4(), props: props }); this.store.push({ id: modalId, component: modalFactory, uniqueKey: uuidv4() });
})(); })();
} }
} }
@@ -4186,10 +4184,6 @@ class Model {
this.getScreenById_single(snc.screenid)?.setNumRunningCmds(snc.num); 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 {
@@ -4597,10 +4591,6 @@ class Model {
this.ws.pushMessage(inputPacket); this.ws.pushMessage(inputPacket);
} }
sendUserInput(userInputResponsePacket: UserInputResponsePacket) {
this.ws.pushMessage(userInputResponsePacket);
}
sendCmdInputText(screenId: string, sp: T.StrWithPos) { sendCmdInputText(screenId: string, sp: T.StrWithPos) {
let pk: T.CmdInputTextPacketType = { let pk: T.CmdInputTextPacketType = {
type: "cmdinputtext", type: "cmdinputtext",
+6 -3
View File
@@ -12,6 +12,9 @@ import { GlobalCommandRunner } from "./global";
import { Model } from "./model"; import { Model } from "./model";
import { getTermPtyData } from "../util/modelutil"; import { getTermPtyData } from "../util/modelutil";
const RemotePtyRows = 8; // also in main.tsx
const RemotePtyCols = 80;
class RemotesModel { class RemotesModel {
globalModel: Model; globalModel: Model;
selectedRemoteId: OV<string> = mobx.observable.box(null, { selectedRemoteId: OV<string> = mobx.observable.box(null, {
@@ -177,14 +180,14 @@ class RemotesModel {
return; return;
} }
let termOpts = { let termOpts = {
rows: appconst.RemotePtyRows, rows: RemotePtyRows,
cols: appconst.RemotePtyCols, cols: RemotePtyCols,
flexrows: false, flexrows: false,
maxptysize: 64 * 1024, maxptysize: 64 * 1024,
}; };
let termWrap = new TermWrap(elem, { let termWrap = new TermWrap(elem, {
termContext: { remoteId: remoteId }, termContext: { remoteId: remoteId },
usedRows: appconst.RemotePtyRows, usedRows: RemotePtyRows,
termOpts: termOpts, termOpts: termOpts,
winSize: null, winSize: null,
keyHandler: (e, termWrap) => { keyHandler: (e, termWrap) => {
+1 -1
View File
@@ -139,7 +139,7 @@ class ScreenLines {
return; return;
} }
let lineIdx = 0; let lineIdx = 0;
for (lineIdx; lineIdx < lines.length; lineIdx++) { for (lineIdx = 0; lineIdx < lines.length; lineIdx++) {
let lineId = lines[lineIdx].lineid; let lineId = lines[lineIdx].lineid;
let curTs = lines[lineIdx].ts; let curTs = lines[lineIdx].ts;
if (lineId == line.lineid) { if (lineId == line.lineid) {
+2 -2
View File
@@ -3,10 +3,10 @@
.terminal-wrapper { .terminal-wrapper {
.xterm-viewport { .xterm-viewport {
overflow: hidden; overflow-y: hidden;
} }
&:hover .xterm-viewport, &:hover .xterm-viewport,
&:focus-within .xterm-viewport { &:focus-within .xterm-viewport {
overflow: auto; overflow-y: auto;
} }
} }
+15 -66
View File
@@ -29,6 +29,7 @@ type SessionDataType = {
// for updates // for updates
remove?: boolean; remove?: boolean;
full?: boolean;
}; };
type LineStateType = { [k: string]: any }; type LineStateType = { [k: string]: any };
@@ -91,6 +92,7 @@ type ScreenDataType = {
anchor: { anchorline: number; anchoroffset: number }; anchor: { anchorline: number; anchoroffset: number };
// for updates // for updates
full?: boolean;
remove?: boolean; remove?: boolean;
}; };
@@ -258,11 +260,6 @@ type CmdDataType = {
restarted?: boolean; restarted?: boolean;
}; };
type LineUpdateType = {
line: LineType;
cmd: CmdDataType;
};
type PtyDataUpdateType = { type PtyDataUpdateType = {
screenid: string; screenid: string;
lineid: string; lineid: string;
@@ -312,48 +309,30 @@ type ScreenNumRunningCommandsUpdateType = {
num: number; num: number;
}; };
type ConnectUpdateType = {
sessions: SessionDataType[];
screens: ScreenDataType[];
remotes: RemoteType[];
screenstatusindicators: ScreenStatusIndicatorUpdateType[];
screennumrunningcommands: ScreenNumRunningCommandsUpdateType[];
activesessionid: string;
};
type BookmarksUpdateType = {
bookmarks: BookmarkType[];
selectedbookmark: string;
};
type MainViewUpdateType = {
mainview: string;
historyview?: HistoryViewDataType;
bookmarksview?: BookmarksUpdateType;
};
type ModelUpdateType = { type ModelUpdateType = {
interactive: boolean; interactive: boolean;
session?: SessionDataType; sessions?: SessionDataType[];
activesessionid?: string; activesessionid?: string;
screen?: ScreenDataType; screens?: ScreenDataType[];
screenlines?: ScreenLinesType; screenlines?: ScreenLinesType;
line?: LineUpdateType; line?: LineType;
lines?: LineType[];
cmd?: CmdDataType; cmd?: CmdDataType;
info?: InfoType; info?: InfoType;
cmdline?: StrWithPos; cmdline?: StrWithPos;
remote?: RemoteType; remotes?: RemoteType[];
history?: HistoryInfoType; history?: HistoryInfoType;
connect?: ConnectUpdateType; connect?: boolean;
mainview?: MainViewUpdateType; mainview?: string;
bookmarks?: BookmarksUpdateType; bookmarks?: BookmarkType[];
selectedbookmark?: string;
clientdata?: ClientDataType; clientdata?: ClientDataType;
historyviewdata?: HistoryViewDataType;
remoteview?: RemoteViewType; remoteview?: RemoteViewType;
openaicmdinfochat?: OpenAICmdInfoChatMessageType[]; openaicmdinfochat?: OpenAICmdInfoChatMessageType[];
alertmessage?: AlertMessageType; alertmessage?: AlertMessageType;
screenstatusindicator?: ScreenStatusIndicatorUpdateType; screenstatusindicators?: ScreenStatusIndicatorUpdateType[];
screennumrunningcommands?: ScreenNumRunningCommandsUpdateType; screennumrunningcommands?: ScreenNumRunningCommandsUpdateType[];
userinputrequest?: UserInputRequest;
}; };
type HistoryViewDataType = { type HistoryViewDataType = {
@@ -435,7 +414,7 @@ type ContextMenuOpts = {
showCut?: boolean; showCut?: boolean;
}; };
type UpdateMessage = PtyDataUpdateType | ModelUpdateType[]; type UpdateMessage = PtyDataUpdateType | ModelUpdateType;
type RendererContext = { type RendererContext = {
screenId: string; screenId: string;
@@ -560,14 +539,6 @@ type ReleaseInfoType = {
latestversion: string; latestversion: string;
}; };
type ClientWinSizeType = {
width: number;
height: number;
top: number;
left: number;
fullscreen: boolean;
};
type ClientDataType = { type ClientDataType = {
clientid: string; clientid: string;
userid: string; userid: string;
@@ -577,7 +548,6 @@ type ClientDataType = {
dbversion: number; dbversion: number;
openaiopts?: OpenAIOptsType; openaiopts?: OpenAIOptsType;
releaseinfo?: ReleaseInfoType; releaseinfo?: ReleaseInfoType;
winsize: ClientWinSizeType;
}; };
type OpenAIOptsType = { type OpenAIOptsType = {
@@ -625,23 +595,6 @@ type HistorySearchParams = {
filterCmds?: boolean; filterCmds?: boolean;
}; };
type UserInputRequest = {
requestid: string;
querytext: string;
responsetype: string;
title: string;
markdown: boolean;
timeoutms: number;
};
type UserInputResponsePacket = {
type: string;
requestid: string;
text?: string;
confirm?: boolean;
errormsg?: string;
};
type RenderModeType = "normal" | "collapsed" | "expanded"; type RenderModeType = "normal" | "collapsed" | "expanded";
type WebScreen = { type WebScreen = {
@@ -777,7 +730,6 @@ type ModalStoreEntry = {
id: string; id: string;
component: React.ComponentType; component: React.ComponentType;
uniqueKey: string; uniqueKey: string;
props?: any;
}; };
type StrWithPos = { type StrWithPos = {
@@ -857,8 +809,6 @@ export type {
RenderModeType, RenderModeType,
AlertMessageType, AlertMessageType,
HistorySearchParams, HistorySearchParams,
UserInputRequest,
UserInputResponsePacket,
ScreenLinesType, ScreenLinesType,
FocusTypeStrs, FocusTypeStrs,
HistoryTypeStrs, HistoryTypeStrs,
@@ -895,7 +845,6 @@ export type {
CmdInputTextPacketType, CmdInputTextPacketType,
OpenAICmdInfoChatMessageType, OpenAICmdInfoChatMessageType,
ScreenStatusIndicatorUpdateType, ScreenStatusIndicatorUpdateType,
ScreenNumRunningCommandsUpdateType,
OV, OV,
OArr, OArr,
OMap, OMap,
+69 -53
View File
@@ -1,4 +1,4 @@
// Copyright 2024, Command Line Inc. // Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import * as mobx from "mobx"; import * as mobx from "mobx";
@@ -17,7 +17,7 @@ function isBlank(s: string): boolean {
} }
function handleNotOkResp(resp: any, url: URL): Promise<any> { function handleNotOkResp(resp: any, url: URL): Promise<any> {
const errMsg = sprintf( let errMsg = sprintf(
"Bad status code response from fetch '%s': code=%d %s", "Bad status code response from fetch '%s': code=%d %s",
url.toString(), url.toString(),
resp.status, resp.status,
@@ -41,18 +41,18 @@ function handleNotOkResp(resp: any, url: URL): Promise<any> {
} }
function fetchJsonData(resp: any, ctErr: boolean): Promise<any> { function fetchJsonData(resp: any, ctErr: boolean): Promise<any> {
const contentType = resp.headers.get("Content-Type"); let contentType = resp.headers.get("Content-Type");
if (contentType?.startsWith("application/json")) { if (contentType != null && contentType.startsWith("application/json")) {
return resp.text().then((textData) => { return resp.text().then((textData) => {
let rtnData: any = null; let rtnData: any = null;
try { try {
rtnData = JSON.parse(textData); rtnData = JSON.parse(textData);
} catch (err) { } catch (err) {
const errMsg = sprintf("Unparseable JSON: " + err.message); let errMsg = sprintf("Unparseable JSON: " + err.message);
const rtnErr = new Error(errMsg); let rtnErr = new Error(errMsg);
throw rtnErr; throw rtnErr;
} }
if (rtnData?.error) { if (rtnData != null && rtnData.error) {
throw new Error(rtnData.error); throw new Error(rtnData.error);
} }
return rtnData; return rtnData;
@@ -67,23 +67,23 @@ function handleJsonFetchResponse(url: URL, resp: any): Promise<any> {
if (!resp.ok) { if (!resp.ok) {
return handleNotOkResp(resp, url); return handleNotOkResp(resp, url);
} }
const rtnData = fetchJsonData(resp, true); let rtnData = fetchJsonData(resp, true);
return rtnData; return rtnData;
} }
function base64ToString(b64: string): string { function base64ToString(b64: string): string {
const stringBytes = base64.toByteArray(b64); let stringBytes = base64.toByteArray(b64);
return new TextDecoder().decode(stringBytes); return new TextDecoder().decode(stringBytes);
} }
function stringToBase64(input: string): string { function stringToBase64(input: string): string {
const stringBytes = new TextEncoder().encode(input); let stringBytes = new TextEncoder().encode(input);
return base64.fromByteArray(stringBytes); return base64.fromByteArray(stringBytes);
} }
function base64ToArray(b64: string): Uint8Array { function base64ToArray(b64: string): Uint8Array {
const rawStr = atob(b64); let rawStr = atob(b64);
const rtnArr = new Uint8Array(new ArrayBuffer(rawStr.length)); let rtnArr = new Uint8Array(new ArrayBuffer(rawStr.length));
for (let i = 0; i < rawStr.length; i++) { for (let i = 0; i < rawStr.length; i++) {
rtnArr[i] = rawStr.charCodeAt(i); rtnArr[i] = rawStr.charCodeAt(i);
} }
@@ -92,6 +92,7 @@ function base64ToArray(b64: string): Uint8Array {
interface IDataType { interface IDataType {
remove?: boolean; remove?: boolean;
full?: boolean;
} }
interface IObjType<DataType> { interface IObjType<DataType> {
@@ -112,28 +113,31 @@ function genMergeSimpleData<T extends ISimpleDataType>(
if (dataArr == null || dataArr.length == 0) { if (dataArr == null || dataArr.length == 0) {
return; return;
} }
const objMap: Record<string, T> = {}; let objMap: Record<string, T> = {};
for (const obj of objs) { for (let i = 0; i < objs.length; i++) {
const id = idFn(obj); let obj = objs[i];
let id = idFn(obj);
objMap[id] = obj; objMap[id] = obj;
} }
for (const dataItem of dataArr) { for (let i = 0; i < dataArr.length; i++) {
let dataItem = dataArr[i];
if (dataItem == null) { if (dataItem == null) {
console.log("genMergeSimpleData, null item"); console.log("genMergeSimpleData, null item");
console.trace(); console.trace();
} }
const id = idFn(dataItem); let id = idFn(dataItem);
if (dataItem.remove) { if (dataItem.remove) {
delete objMap[id]; delete objMap[id];
continue;
} else { } else {
objMap[id] = dataItem; objMap[id] = dataItem;
} }
} }
const newObjs = Object.values(objMap); let newObjs = Object.values(objMap);
if (sortIdxFn) { if (sortIdxFn) {
newObjs.sort((a, b) => { newObjs.sort((a, b) => {
const astr = sortIdxFn(a); let astr = sortIdxFn(a);
const bstr = sortIdxFn(b); let bstr = sortIdxFn(b);
return astr.localeCompare(bstr); return astr.localeCompare(bstr);
}); });
} }
@@ -151,18 +155,20 @@ function genMergeData<ObjType extends IObjType<DataType>, DataType extends IData
if (dataArr == null || dataArr.length == 0) { if (dataArr == null || dataArr.length == 0) {
return; return;
} }
const objMap: Record<string, ObjType> = {}; let objMap: Record<string, ObjType> = {};
for (const obj of objs) { for (let i = 0; i < objs.length; i++) {
const id = objIdFn(obj); let obj = objs[i];
let id = objIdFn(obj);
objMap[id] = obj; objMap[id] = obj;
} }
for (const dataItem of dataArr) { for (let i = 0; i < dataArr.length; i++) {
let dataItem = dataArr[i];
if (dataItem == null) { if (dataItem == null) {
console.log("genMergeData, null item"); console.log("genMergeData, null item");
console.trace(); console.trace();
continue; continue;
} }
const id = dataIdFn(dataItem); let id = dataIdFn(dataItem);
let obj = objMap[id]; let obj = objMap[id];
if (dataItem.remove) { if (dataItem.remove) {
if (obj != null) { if (obj != null) {
@@ -172,13 +178,17 @@ function genMergeData<ObjType extends IObjType<DataType>, DataType extends IData
continue; continue;
} }
if (obj == null) { if (obj == null) {
if (!dataItem.full) {
console.log("cannot create object, dataitem is not full", objs, dataItem);
continue;
}
obj = ctorFn(dataItem); obj = ctorFn(dataItem);
objMap[id] = obj; objMap[id] = obj;
continue; continue;
} }
obj.mergeData(dataItem); obj.mergeData(dataItem);
} }
const newObjs = Object.values(objMap); let newObjs = Object.values(objMap);
if (sortIdxFn) { if (sortIdxFn) {
newObjs.sort((a, b) => { newObjs.sort((a, b) => {
return sortIdxFn(a) - sortIdxFn(b); return sortIdxFn(a) - sortIdxFn(b);
@@ -194,17 +204,18 @@ function genMergeDataMap<ObjType extends IObjType<DataType>, DataType extends ID
dataIdFn: (data: DataType) => string, dataIdFn: (data: DataType) => string,
ctorFn: (data: DataType) => ObjType ctorFn: (data: DataType) => ObjType
): { added: string[]; removed: string[] } { ): { added: string[]; removed: string[] } {
const rtn: { added: string[]; removed: string[] } = { added: [], removed: [] }; let rtn: { added: string[]; removed: string[] } = { added: [], removed: [] };
if (dataArr == null || dataArr.length == 0) { if (dataArr == null || dataArr.length == 0) {
return rtn; return rtn;
} }
for (const dataItem of dataArr) { for (let i = 0; i < dataArr.length; i++) {
let dataItem = dataArr[i];
if (dataItem == null) { if (dataItem == null) {
console.log("genMergeDataMap, null item"); console.log("genMergeDataMap, null item");
console.trace(); console.trace();
continue; continue;
} }
const id = dataIdFn(dataItem); let id = dataIdFn(dataItem);
let obj = objMap.get(id); let obj = objMap.get(id);
if (dataItem.remove) { if (dataItem.remove) {
if (obj != null) { if (obj != null) {
@@ -215,6 +226,10 @@ function genMergeDataMap<ObjType extends IObjType<DataType>, DataType extends ID
continue; continue;
} }
if (obj == null) { if (obj == null) {
if (!dataItem.full) {
console.log("cannot create object, dataitem is not full", dataItem);
continue;
}
obj = ctorFn(dataItem); obj = ctorFn(dataItem);
objMap.set(id, obj); objMap.set(id, obj);
rtn.added.push(id); rtn.added.push(id);
@@ -247,23 +262,23 @@ function incObs(inum: mobx.IObservableValue<number>) {
// @check:font // @check:font
function loadFonts() { function loadFonts() {
const jbmFontNormal = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-regular.woff2')", { let jbmFontNormal = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-regular.woff2')", {
style: "normal", style: "normal",
weight: "400", weight: "400",
}); });
const jbmFont200 = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-200.woff2')", { let jbmFont200 = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-200.woff2')", {
style: "normal", style: "normal",
weight: "200", weight: "200",
}); });
const jbmFont700 = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-700.woff2')", { let jbmFont700 = new FontFace("JetBrains Mono", "url('public/fonts/jetbrains-mono-v13-latin-700.woff2')", {
style: "normal", style: "normal",
weight: "700", weight: "700",
}); });
const faFont = new FontFace("FontAwesome", "url(public/fonts/fontawesome-webfont-4.7.woff2)", { let faFont = new FontFace("FontAwesome", "url(public/fonts/fontawesome-webfont-4.7.woff2)", {
style: "normal", style: "normal",
weight: "normal", weight: "normal",
}); });
const docFonts: any = document.fonts; // work around ts typing issue let docFonts: any = document.fonts; // work around ts typing issue
docFonts.add(jbmFontNormal); docFonts.add(jbmFontNormal);
docFonts.add(jbmFont200); docFonts.add(jbmFont200);
docFonts.add(jbmFont700); docFonts.add(jbmFont700);
@@ -281,13 +296,13 @@ function getTodayStr(): string {
} }
function getYesterdayStr(): string { function getYesterdayStr(): string {
const d = new Date(); let d = new Date();
d.setDate(d.getDate() - 1); d.setDate(d.getDate() - 1);
return getDateStr(d); return getDateStr(d);
} }
function getDateStr(d: Date): string { function getDateStr(d: Date): string {
const yearStr = String(d.getFullYear()); let yearStr = String(d.getFullYear());
let monthStr = String(d.getMonth() + 1); let monthStr = String(d.getMonth() + 1);
if (monthStr.length == 1) { if (monthStr.length == 1) {
monthStr = "0" + monthStr; monthStr = "0" + monthStr;
@@ -296,30 +311,31 @@ function getDateStr(d: Date): string {
if (dayStr.length == 1) { if (dayStr.length == 1) {
dayStr = "0" + dayStr; dayStr = "0" + dayStr;
} }
const dowStr = DOW_STRS[d.getDay()]; let dowStr = DOW_STRS[d.getDay()];
return dowStr + " " + yearStr + "-" + monthStr + "-" + dayStr; return dowStr + " " + yearStr + "-" + monthStr + "-" + dayStr;
} }
function getRemoteConnVal(r: RemoteType): number { function getRemoteConnVal(r: RemoteType): number {
switch (r.status) { if (r.status == "connected") {
case "connected": return 1;
return 1;
case "connecting":
return 2;
case "disconnected":
return 3;
case "error":
return 4;
default:
return 5;
} }
if (r.status == "connecting") {
return 2;
}
if (r.status == "disconnected") {
return 3;
}
if (r.status == "error") {
return 4;
}
return 5;
} }
function sortAndFilterRemotes(origRemotes: RemoteType[]): RemoteType[] { function sortAndFilterRemotes(origRemotes: RemoteType[]): RemoteType[] {
const remotes = origRemotes.filter((r) => !r.archived); let remotes = origRemotes.filter((r) => !r.archived);
remotes.sort((a, b) => { remotes.sort((a, b) => {
const connValA = getRemoteConnVal(a); let connValA = getRemoteConnVal(a);
const connValB = getRemoteConnVal(b); let connValB = getRemoteConnVal(b);
if (connValA != connValB) { if (connValA != connValB) {
return connValA - connValB; return connValA - connValB;
} }
@@ -356,7 +372,7 @@ function openLink(url: string): void {
window.open(url, "_blank"); window.open(url, "_blank");
} }
function getColorRGB(colorInput: string) { function getColorRGB(colorInput) {
const tempElement = document.createElement("div"); const tempElement = document.createElement("div");
tempElement.style.color = colorInput; tempElement.style.color = colorInput;
document.body.appendChild(tempElement); document.body.appendChild(tempElement);
@@ -383,7 +399,7 @@ function getRemoteName(remote: RemoteType): string {
if (remote == null) { if (remote == null) {
return ""; return "";
} }
const { remotealias, remotecanonicalname } = remote; let { remotealias, remotecanonicalname } = remote;
return remotealias ? `${remotealias} [${remotecanonicalname}]` : remotecanonicalname; return remotealias ? `${remotealias} [${remotecanonicalname}]` : remotecanonicalname;
} }

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