mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-04 18:59:08 +01:00
working on resizing terminal to match window size
This commit is contained in:
parent
d6d7e8bb25
commit
bc476c3c23
@ -13,6 +13,7 @@
|
|||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.1.0",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
|
"throttle-debounce": "^5.0.0",
|
||||||
"tsx-control-statements": "^4.1.1",
|
"tsx-control-statements": "^4.1.1",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"xterm": "^4.18.0"
|
"xterm": "^4.18.0"
|
||||||
|
@ -4,9 +4,9 @@ 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 {v4 as uuidv4} from "uuid";
|
import {v4 as uuidv4} from "uuid";
|
||||||
import dayjs from 'dayjs'
|
import dayjs from "dayjs";
|
||||||
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
||||||
import cn from "classnames"
|
import cn from "classnames";
|
||||||
import {TermWrap} from "./term";
|
import {TermWrap} from "./term";
|
||||||
import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts} from "./types";
|
import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts} from "./types";
|
||||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
|
27
src/model.ts
27
src/model.ts
@ -1,10 +1,11 @@
|
|||||||
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 {debounce} from "throttle-debounce";
|
||||||
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData} from "./util";
|
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData} from "./util";
|
||||||
import {TermWrap} from "./term";
|
import {TermWrap} from "./term";
|
||||||
import {v4 as uuidv4} from "uuid";
|
import {v4 as uuidv4} from "uuid";
|
||||||
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts} from "./types";
|
import type {SessionDataType, WindowDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenWindowType, ScreenOptsType, LayoutType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize} from "./types";
|
||||||
import {WSControl} from "./ws";
|
import {WSControl} from "./ws";
|
||||||
|
|
||||||
var GlobalUser = "sawka";
|
var GlobalUser = "sawka";
|
||||||
@ -153,11 +154,11 @@ class Cmd {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data = this.data.get();
|
let data = this.data.get();
|
||||||
let inputPacket = {
|
let inputPacket : FeInputPacketType = {
|
||||||
type: "feinput",
|
type: "feinput",
|
||||||
ck: this.sessionId + "/" + this.cmdId,
|
ck: this.sessionId + "/" + this.cmdId,
|
||||||
inputdata: btoa(event.key),
|
|
||||||
remote: this.remote,
|
remote: this.remote,
|
||||||
|
inputdata64: btoa(event.key),
|
||||||
};
|
};
|
||||||
GlobalModel.sendInputPacket(inputPacket);
|
GlobalModel.sendInputPacket(inputPacket);
|
||||||
}
|
}
|
||||||
@ -253,6 +254,8 @@ class ScreenWindow {
|
|||||||
layout : OV<LayoutType>;
|
layout : OV<LayoutType>;
|
||||||
shouldFollow : OV<boolean> = mobx.observable.box(true);
|
shouldFollow : OV<boolean> = mobx.observable.box(true);
|
||||||
width : OV<number> = mobx.observable.box(0);
|
width : OV<number> = mobx.observable.box(0);
|
||||||
|
widthInCols : number;
|
||||||
|
colsCallback_debounced : (cols : number) => void;
|
||||||
|
|
||||||
// cmdid => TermWrap
|
// cmdid => TermWrap
|
||||||
terms : Record<string, TermWrap> = {};
|
terms : Record<string, TermWrap> = {};
|
||||||
@ -263,6 +266,8 @@ class ScreenWindow {
|
|||||||
this.windowId = swdata.windowid;
|
this.windowId = swdata.windowid;
|
||||||
this.name = mobx.observable.box(swdata.name);
|
this.name = mobx.observable.box(swdata.name);
|
||||||
this.layout = mobx.observable.box(swdata.layout);
|
this.layout = mobx.observable.box(swdata.layout);
|
||||||
|
this.colsCallback_debounced = debounce(1000, this.colsCallback.bind(this));
|
||||||
|
this.widthInCols = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePtyData(ptyMsg : PtyDataUpdateType) {
|
updatePtyData(ptyMsg : PtyDataUpdateType) {
|
||||||
@ -275,9 +280,21 @@ class ScreenWindow {
|
|||||||
term.updatePtyData(ptyMsg.ptypos, data);
|
term.updatePtyData(ptyMsg.ptypos, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
colsCallback(cols : number) : void {
|
||||||
|
console.log("cols set", cols);
|
||||||
|
for (let cmdid in this.terms) {
|
||||||
|
this.terms[cmdid].resizeCols(cols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setWidth(width : number) : void {
|
setWidth(width : number) : void {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
|
let oldCols = this.widthInCols;
|
||||||
this.width.set(width);
|
this.width.set(width);
|
||||||
|
this.widthInCols = widthToCols(width);
|
||||||
|
if (this.widthInCols > 0 && this.widthInCols != oldCols) {
|
||||||
|
this.colsCallback_debounced(this.widthInCols);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1149,8 +1166,8 @@ class Model {
|
|||||||
return this.termUsedRowsCache[key];
|
return this.termUsedRowsCache[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
setTUR(sessionId : string, cmdId : string, cols : number, usedRows : number) : void {
|
setTUR(sessionId : string, cmdId : string, size : TermWinSize, usedRows : number) : void {
|
||||||
let key = sessionId + "/" + cmdId + "/" + cols;
|
let key = sessionId + "/" + cmdId + "/" + size.cols;
|
||||||
this.termUsedRowsCache[key] = usedRows;
|
this.termUsedRowsCache[key] = usedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
src/term.ts
46
src/term.ts
@ -4,7 +4,7 @@ import {sprintf} from "sprintf-js";
|
|||||||
import {boundMethod} from "autobind-decorator";
|
import {boundMethod} from "autobind-decorator";
|
||||||
import {v4 as uuidv4} from "uuid";
|
import {v4 as uuidv4} from "uuid";
|
||||||
import {GlobalModel} from "./model";
|
import {GlobalModel} from "./model";
|
||||||
import type {TermOptsType} from "./types";
|
import type {TermOptsType, TermWinSize} from "./types";
|
||||||
|
|
||||||
type DataUpdate = {
|
type DataUpdate = {
|
||||||
data : Uint8Array,
|
data : Uint8Array,
|
||||||
@ -18,6 +18,18 @@ type WindowSize = {
|
|||||||
|
|
||||||
const DefaultCellWidth = 8;
|
const DefaultCellWidth = 8;
|
||||||
const DefaultCellHeight = 16;
|
const DefaultCellHeight = 16;
|
||||||
|
const MinTermCols = 10;
|
||||||
|
const MaxTermCols = 1024;
|
||||||
|
|
||||||
|
function boundInt(ival : number, minVal : number, maxVal : number) : number {
|
||||||
|
if (ival < minVal) {
|
||||||
|
return minVal;
|
||||||
|
}
|
||||||
|
if (ival > maxVal) {
|
||||||
|
return maxVal;
|
||||||
|
}
|
||||||
|
return ival;
|
||||||
|
}
|
||||||
|
|
||||||
// cmd-instance
|
// cmd-instance
|
||||||
class TermWrap {
|
class TermWrap {
|
||||||
@ -35,7 +47,7 @@ class TermWrap {
|
|||||||
loadError : mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
loadError : mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||||
winSize : WindowSize;
|
winSize : WindowSize;
|
||||||
numParseErrors : number = 0;
|
numParseErrors : number = 0;
|
||||||
termCols : number;
|
termSize : TermWinSize;
|
||||||
|
|
||||||
constructor(elem : Element, sessionId : string, cmdId : string, usedRows : number, termOpts : TermOptsType, winSize : WindowSize, keyHandler : (event : any) => void) {
|
constructor(elem : Element, sessionId : string, cmdId : string, usedRows : number, termOpts : TermOptsType, winSize : WindowSize, keyHandler : (event : any) => void) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
@ -51,13 +63,10 @@ class TermWrap {
|
|||||||
this.atRowMax = true;
|
this.atRowMax = true;
|
||||||
this.usedRows = mobx.observable.box(termOpts.rows);
|
this.usedRows = mobx.observable.box(termOpts.rows);
|
||||||
}
|
}
|
||||||
let cols = termOpts.cols;
|
let cols = Math.trunc((winSize.width - 25) / DefaultCellWidth) - 1;
|
||||||
let maxCols = Math.trunc((winSize.width - 25) / DefaultCellWidth) - 1;
|
cols = boundInt(cols, MinTermCols, MaxTermCols);
|
||||||
if (maxCols > cols) {
|
this.termSize = {rows: termOpts.rows, cols: cols};
|
||||||
cols = maxCols;
|
this.terminal = new Terminal({rows: this.termSize.rows, cols: this.termSize.cols, fontSize: 14, theme: {foreground: "#d3d7cf"}});
|
||||||
}
|
|
||||||
this.termCols = maxCols;
|
|
||||||
this.terminal = new Terminal({rows: termOpts.rows, cols: maxCols, fontSize: 14, theme: {foreground: "#d3d7cf"}});
|
|
||||||
this.terminal._core._inputHandler._parser.setErrorHandler((state) => {
|
this.terminal._core._inputHandler._parser.setErrorHandler((state) => {
|
||||||
this.numParseErrors++;
|
this.numParseErrors++;
|
||||||
return state;
|
return state;
|
||||||
@ -144,7 +153,7 @@ class TermWrap {
|
|||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
let oldUsedRows = this.usedRows.get();
|
let oldUsedRows = this.usedRows.get();
|
||||||
this.usedRows.set(tur);
|
this.usedRows.set(tur);
|
||||||
GlobalModel.setTUR(this.sessionId, this.cmdId, this.termCols, tur);
|
GlobalModel.setTUR(this.sessionId, this.cmdId, this.termSize, tur);
|
||||||
if (this.connectedElem) {
|
if (this.connectedElem) {
|
||||||
let resizeEvent = new CustomEvent("termresize", {
|
let resizeEvent = new CustomEvent("termresize", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@ -160,6 +169,23 @@ class TermWrap {
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resizeCols(cols : number) {
|
||||||
|
this.resize({rows: this.termSize.rows, cols: cols});
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(size : TermWinSize) {
|
||||||
|
if (this.terminal == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let newSize = {rows: size.rows, cols: size.cols};
|
||||||
|
newSize.cols = boundInt(newSize.cols, MinTermCols, MaxTermCols);
|
||||||
|
if (newSize.rows == this.termSize.rows && newSize.cols == this.termSize.cols) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.termSize = newSize;
|
||||||
|
this.terminal.resize(newSize.cols, newSize.rows);
|
||||||
|
}
|
||||||
|
|
||||||
reloadTerminal(delayMs : number) {
|
reloadTerminal(delayMs : number) {
|
||||||
if (this.terminal == null) {
|
if (this.terminal == null) {
|
||||||
return;
|
return;
|
||||||
|
16
src/types.ts
16
src/types.ts
@ -164,6 +164,15 @@ type FeCmdPacketType = {
|
|||||||
interactive : boolean,
|
interactive : boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FeInputPacketType = {
|
||||||
|
type : string,
|
||||||
|
ck : string,
|
||||||
|
remote : RemotePtrType,
|
||||||
|
inputdata64? : string,
|
||||||
|
signum? : number,
|
||||||
|
winsize? : TermWinSize,
|
||||||
|
};
|
||||||
|
|
||||||
type WatchScreenPacketType = {
|
type WatchScreenPacketType = {
|
||||||
type : string,
|
type : string,
|
||||||
sessionid : string,
|
sessionid : string,
|
||||||
@ -171,6 +180,11 @@ type WatchScreenPacketType = {
|
|||||||
connect : boolean,
|
connect : boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TermWinSize = {
|
||||||
|
rows : number,
|
||||||
|
cols : number,
|
||||||
|
}
|
||||||
|
|
||||||
type TermOptsType = {
|
type TermOptsType = {
|
||||||
rows : number,
|
rows : number,
|
||||||
cols : number,
|
cols : number,
|
||||||
@ -268,4 +282,4 @@ type HistoryQueryOpts = {
|
|||||||
|
|
||||||
type UpdateMessage = PtyDataUpdateType | ModelUpdateType;
|
type UpdateMessage = PtyDataUpdateType | ModelUpdateType;
|
||||||
|
|
||||||
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType, ScreenDataType, ScreenOptsType, ScreenWindowType, LayoutType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType};
|
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, WindowDataType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDonePacketType, CmdDataType, ScreenDataType, ScreenOptsType, ScreenWindowType, LayoutType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType};
|
||||||
|
@ -5911,6 +5911,11 @@ terser@^5.14.1:
|
|||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map-support "~0.5.20"
|
source-map-support "~0.5.20"
|
||||||
|
|
||||||
|
throttle-debounce@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933"
|
||||||
|
integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==
|
||||||
|
|
||||||
through@^2.3.6:
|
through@^2.3.6:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
|
Loading…
Reference in New Issue
Block a user