mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-08 00:21:23 +01:00
662 lines
22 KiB
TypeScript
662 lines
22 KiB
TypeScript
import * as mobx from "mobx";
|
|
import {sprintf} from "sprintf-js";
|
|
import {boundMethod} from "autobind-decorator";
|
|
import {handleJsonFetchResponse, isModKeyPress, base64ToArray} from "./util";
|
|
import * as T from "./types";
|
|
import {TermWrap} from "./term";
|
|
import * as lineutil from "./lineutil";
|
|
import * as util from "./util";
|
|
import {windowWidthToCols, windowHeightToRows, termWidthFromCols, termHeightFromRows} from "./textmeasure";
|
|
import {WebShareWSControl} from "./webshare-ws";
|
|
|
|
// @ts-ignore
|
|
let PROMPT_DEV = __PROMPT_DEV__;
|
|
// @ts-ignore
|
|
let PROMPT_VERSION = __PROMPT_VERSION__;
|
|
// @ts-ignore
|
|
let PROMPT_BULILD = __PROMPT_BUILD__;
|
|
// @ts-ignore
|
|
let PROMPT_API_ENDPOINT = __PROMPT_API_ENDPOINT__;
|
|
// @ts-ignore
|
|
let PROMPT_WSAPI_ENDPOINT = __PROMPT_WSAPI_ENDPOINT__;
|
|
|
|
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>;
|
|
|
|
type PtyListener = {
|
|
receiveData(ptyPos : number, data : Uint8Array, reason? : string);
|
|
};
|
|
|
|
function isBlank(s : string) {
|
|
return (s == null || s == "");
|
|
}
|
|
|
|
function getBaseUrl() {
|
|
return PROMPT_API_ENDPOINT;
|
|
}
|
|
|
|
function getBaseWSUrl() {
|
|
return PROMPT_WSAPI_ENDPOINT;
|
|
}
|
|
|
|
class WebShareModelClass {
|
|
viewKey : string;
|
|
screenId : string;
|
|
errMessage : OV<string> = mobx.observable.box(null, {name: "errMessage"});
|
|
fullScreen : OV<T.WebFullScreen> = mobx.observable.box(null, {name: "webScreen"});
|
|
terminals : Record<string, TermWrap> = {}; // lineid => TermWrap
|
|
renderers : Record<string, T.RendererModel> = {}; // lineid => RendererModel
|
|
contentHeightCache : Record<string, number> = {}; // lineid => height
|
|
wsControl : WebShareWSControl;
|
|
anchor : {anchorLine : number, anchorOffset : number} = {anchorLine: 0, anchorOffset: 0};
|
|
selectedLine : OV<number> = mobx.observable.box(0, {name: "selectedLine"});
|
|
syncSelectedLine : OV<boolean> = mobx.observable.box(true, {name: "syncSelectedLine"});
|
|
lastScreenSize : T.WindowSize = null;
|
|
activePtyFetch : Record<string, boolean> = {}; // lineid -> active
|
|
localPtyOffsetMap : Record<string, number> = {};
|
|
remotePtyOffsetMap : Record<string, number> = {};
|
|
activeUpdateFetch : boolean = false;
|
|
remoteScreenVts : number = 0;
|
|
isDev : boolean = PROMPT_DEV;
|
|
|
|
constructor() {
|
|
let pathName = window.location.pathname;
|
|
let screenMatch = pathName.match(/\/share\/([a-f0-9-]+)/);
|
|
if (screenMatch != null) {
|
|
this.screenId = screenMatch[1];
|
|
}
|
|
let urlParams = new URLSearchParams(window.location.search);
|
|
this.viewKey = urlParams.get("viewkey");
|
|
if (this.screenId == null) {
|
|
this.screenId = urlParams.get("screenid");
|
|
}
|
|
setTimeout(() => this.loadFullScreenData(false), 10);
|
|
this.wsControl = new WebShareWSControl(getBaseWSUrl(), this.screenId, this.viewKey, this.wsMessageCallback.bind(this));
|
|
document.addEventListener("keydown", this.docKeyDownHandler.bind(this));
|
|
}
|
|
|
|
setErrMessage(msg : string) : void {
|
|
mobx.action(() => {
|
|
this.errMessage.set(msg);
|
|
})();
|
|
}
|
|
|
|
setSyncSelectedLine(val : boolean) : void {
|
|
mobx.action(() => {
|
|
this.syncSelectedLine.set(val);
|
|
if (val) {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen != null) {
|
|
this.selectedLine.set(fullScreen.screen.selectedline);
|
|
}
|
|
}
|
|
})();
|
|
}
|
|
|
|
setLastScreenSize(winSize : T.WindowSize) {
|
|
if (winSize == null || winSize.height == 0 || winSize.width == 0) {
|
|
return;
|
|
}
|
|
this.lastScreenSize = winSize;
|
|
}
|
|
|
|
getMaxContentSize() : T.WindowSize {
|
|
if (this.lastScreenSize == null) {
|
|
let width = termWidthFromCols(80, WebShareModel.getTermFontSize());
|
|
let height = termHeightFromRows(25, WebShareModel.getTermFontSize());
|
|
return {width, height};
|
|
}
|
|
let winSize = this.lastScreenSize;
|
|
let width = util.boundInt(winSize.width-50, 100, 5000);
|
|
let height = util.boundInt(winSize.height-100, 100, 5000);
|
|
return {width, height};
|
|
}
|
|
|
|
getIdealContentSize() : T.WindowSize {
|
|
if (this.lastScreenSize == null) {
|
|
let width = termWidthFromCols(80, WebShareModel.getTermFontSize());
|
|
let height = termHeightFromRows(25, WebShareModel.getTermFontSize());
|
|
return {width, height};
|
|
}
|
|
let winSize = this.lastScreenSize;
|
|
let width = util.boundInt(Math.ceil((winSize.width-50)*0.7), 100, 5000);
|
|
let height = util.boundInt(Math.ceil((winSize.height-100)*0.5), 100, 5000);
|
|
return {width, height};
|
|
}
|
|
|
|
getSelectedLine() : number {
|
|
return this.selectedLine.get();
|
|
}
|
|
|
|
getServerSelectedLine() : number {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen != null) {
|
|
return fullScreen.screen.selectedline;
|
|
}
|
|
}
|
|
|
|
setSelectedLine(lineNum : number) : void {
|
|
mobx.action(() => {
|
|
this.selectedLine.set(lineNum);
|
|
})();
|
|
}
|
|
|
|
updateSelectedLineIndex(delta : number) : void {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return;
|
|
}
|
|
let lineIndex = this.getLineIndex(this.selectedLine.get());
|
|
if (lineIndex == -1) {
|
|
return;
|
|
}
|
|
lineIndex += delta;
|
|
let lines = fullScreen.lines;
|
|
if (lineIndex < 0 || lineIndex >= lines.length) {
|
|
return;
|
|
}
|
|
this.setSelectedLine(lines[lineIndex].linenum);
|
|
}
|
|
|
|
setAnchorFields(anchorLine : number, anchorOffset : number, reason : string) : void {
|
|
this.anchor.anchorLine = anchorLine;
|
|
this.anchor.anchorOffset = anchorOffset;
|
|
}
|
|
|
|
getAnchor() : {anchorLine : number, anchorOffset : number} {
|
|
return this.anchor;
|
|
}
|
|
|
|
getTermFontSize() : number {
|
|
return 12;
|
|
}
|
|
|
|
resizeWindow(winSize : T.WindowSize) : void {
|
|
let cols = windowWidthToCols(winSize.width, this.getTermFontSize());
|
|
for (let lineId in this.terminals) {
|
|
let termWrap = this.terminals[lineId];
|
|
termWrap.resizeCols(cols);
|
|
}
|
|
}
|
|
|
|
mergeLine(fullScreen : T.WebFullScreen, newLine : T.WebLine) {
|
|
for (let i=0; i<fullScreen.lines.length; i++) {
|
|
let line = fullScreen.lines[i];
|
|
if (line.lineid == newLine.lineid) {
|
|
fullScreen.lines[i] = newLine;
|
|
return;
|
|
}
|
|
if (line.linenum > newLine.linenum) {
|
|
fullScreen.lines.splice(i, 0, newLine);
|
|
return;
|
|
}
|
|
}
|
|
fullScreen.lines.push(newLine);
|
|
}
|
|
|
|
removeLine(fullScreen : T.WebFullScreen, lineId : string) {
|
|
for (let i=0; i<fullScreen.lines.length; i++) {
|
|
let line = fullScreen.lines[i];
|
|
if (line.lineid == lineId) {
|
|
fullScreen.lines.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
for (let i=0; i<fullScreen.cmds.length; i++) {
|
|
let cmd = fullScreen.cmds[i];
|
|
if (cmd.lineid == lineId) {
|
|
fullScreen.cmds.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
this.unloadRenderer(lineId);
|
|
}
|
|
|
|
setCmdDone(lineId : string) : void {
|
|
let termWrap = this.getTermWrap(lineId);
|
|
if (termWrap != null) {
|
|
termWrap.cmdDone();
|
|
}
|
|
}
|
|
|
|
mergeCmd(fullScreen : T.WebFullScreen, newCmd : T.WebCmd) {
|
|
for (let i=0; i<fullScreen.cmds.length; i++) {
|
|
let cmd = fullScreen.cmds[i];
|
|
if (cmd.lineid == newCmd.lineid) {
|
|
let wasRunning = lineutil.cmdStatusIsRunning(cmd.status);
|
|
let isRunning = lineutil.cmdStatusIsRunning(newCmd.status);
|
|
if (wasRunning && !isRunning) {
|
|
setTimeout(() => this.setCmdDone(cmd.lineid), 300);
|
|
}
|
|
fullScreen.cmds[i] = newCmd;
|
|
return;
|
|
}
|
|
}
|
|
fullScreen.cmds.push(newCmd);
|
|
}
|
|
|
|
mergeUpdate(msg : T.WebFullScreen) {
|
|
if (msg.screenid != this.screenId) {
|
|
console.log("bad WebFullScreen update, wrong screenid", msg.screenid);
|
|
return;
|
|
}
|
|
// console.log("merge screen-update", "vts=" + msg.vts);
|
|
// console.log("merge", "vts=" + msg.vts, msg);
|
|
mobx.action(() => {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen.vts >= msg.vts) {
|
|
console.log("stale merge", "cur-vts=" + fullScreen.vts, "merge-vts=" + msg.vts);
|
|
return;
|
|
}
|
|
fullScreen.vts = msg.vts;
|
|
if (msg.screen) {
|
|
fullScreen.screen = msg.screen;
|
|
if (this.syncSelectedLine.get()) {
|
|
this.selectedLine.set(msg.screen.selectedline);
|
|
}
|
|
}
|
|
if (msg.lines != null && msg.lines.length > 0) {
|
|
for (let line of msg.lines) {
|
|
if (line.archived) {
|
|
this.removeLine(fullScreen, line.lineid);
|
|
continue;
|
|
}
|
|
this.mergeLine(fullScreen, line);
|
|
}
|
|
}
|
|
if (msg.cmds != null && msg.cmds.length > 0) {
|
|
for (let cmd of msg.cmds) {
|
|
this.mergeCmd(fullScreen, cmd);
|
|
}
|
|
}
|
|
this.handleCmdPtyMap(msg.cmdptymap);
|
|
})();
|
|
}
|
|
|
|
handleCmdPtyMap(ptyMap : Record<string, number>) {
|
|
if (ptyMap == null) {
|
|
return;
|
|
}
|
|
for (let lineId in ptyMap) {
|
|
let newOffset = ptyMap[lineId];
|
|
this.remotePtyOffsetMap[lineId] = newOffset;
|
|
let localOffset = this.localPtyOffsetMap[lineId];
|
|
if (localOffset != null && localOffset < newOffset) {
|
|
this.runPtyFetch(lineId);
|
|
}
|
|
}
|
|
}
|
|
|
|
runPtyFetch(lineId : string) {
|
|
let prtn = this.checkFetchPtyData(lineId, false);
|
|
let ptyListener = this.getPtyListener(lineId);
|
|
if (ptyListener != null) {
|
|
prtn.then((ptydata) => {
|
|
ptyListener.receiveData(ptydata.pos, ptydata.data, "model-fetch");
|
|
if (ptydata.data.length > 0) {
|
|
setTimeout(() => this.checkFetchPtyData(lineId, false), 100);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
getPtyListener(lineId : string) {
|
|
let termWrap = this.getTermWrap(lineId);
|
|
if (termWrap != null) {
|
|
return termWrap;
|
|
}
|
|
let renderer = this.getRenderer(lineId);
|
|
if (renderer != null) {
|
|
return renderer;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
receivePtyData(lineId : string, ptyPos : number, data : Uint8Array, reason? : string) : void {
|
|
let termWrap = this.getTermWrap(lineId);
|
|
if (termWrap != null) {
|
|
termWrap.receiveData(ptyPos, data, reason);
|
|
}
|
|
let renderer = this.getRenderer(lineId);
|
|
if (renderer != null) {
|
|
renderer.receiveData(ptyPos, data, reason);
|
|
}
|
|
}
|
|
|
|
checkFetchPtyData(lineId : string, reload : boolean) : Promise<T.PtyDataType> {
|
|
let lineNum = this.getLineNumFromId(lineId);
|
|
if (this.activePtyFetch[lineId]) {
|
|
// console.log("check-fetch", lineNum, "already running");
|
|
return;
|
|
}
|
|
if (reload) {
|
|
this.localPtyOffsetMap[lineId] = 0;
|
|
}
|
|
let ptyOffset = this.localPtyOffsetMap[lineId];
|
|
if (ptyOffset == null) {
|
|
// console.log("check-fetch", lineNum, "no local offset");
|
|
return;
|
|
}
|
|
let remotePtyOffset = this.remotePtyOffsetMap[lineId];
|
|
if (ptyOffset >= remotePtyOffset) {
|
|
// up to date
|
|
return Promise.resolve({pos: ptyOffset, data: new Uint8Array(0)});
|
|
}
|
|
this.activePtyFetch[lineId] = true;
|
|
let viewKey = WebShareModel.viewKey;
|
|
// console.log("fetch pty", lineNum, "pos=" + ptyOffset);
|
|
let usp = new URLSearchParams({screenid: this.screenId, viewkey: viewKey, lineid: lineId, pos: String(ptyOffset)});
|
|
let url = new URL(getBaseUrl() + "/webshare/ptydata?" + usp.toString());
|
|
return fetch(url, {method: "GET", mode: "cors", cache: "no-cache"}).then((resp) => {
|
|
if (!resp.ok) {
|
|
throw new Error(sprintf("Bad fetch response for /webshare/ptydata: %d %s", resp.status, resp.statusText));
|
|
}
|
|
let ptyOffsetStr = resp.headers.get("X-PtyDataOffset");
|
|
if (ptyOffsetStr != null && !isNaN(parseInt(ptyOffsetStr))) {
|
|
ptyOffset = parseInt(ptyOffsetStr);
|
|
}
|
|
return resp.arrayBuffer();
|
|
}).then((buf) => {
|
|
let dataArr = new Uint8Array(buf);
|
|
let newOffset = ptyOffset + dataArr.length;
|
|
// console.log("fetch pty success", lineNum, "len=" + dataArr.length, "pos => " + newOffset);
|
|
this.localPtyOffsetMap[lineId] = newOffset;
|
|
return {pos: ptyOffset, data: dataArr};
|
|
}).finally(() => {
|
|
this.activePtyFetch[lineId] = false;
|
|
});
|
|
}
|
|
|
|
wsMessageCallback(msg : any) {
|
|
if (msg.type == "webscreen:update") {
|
|
// console.log("[ws] update vts", msg.vts);
|
|
if (msg.vts > this.remoteScreenVts) {
|
|
this.remoteScreenVts = msg.vts;
|
|
setTimeout(() => this.checkUpdateScreenData(), 10);
|
|
}
|
|
return;
|
|
}
|
|
if (msg.type == "success:webshare") {
|
|
return;
|
|
}
|
|
console.log("[ws] unhandled message", msg);
|
|
}
|
|
|
|
setWebFullScreen(screen : T.WebFullScreen) {
|
|
// console.log("got initial screen", "vts=" + screen.vts);
|
|
mobx.action(() => {
|
|
if (screen.lines == null) {
|
|
screen.lines = [];
|
|
}
|
|
if (screen.cmds == null) {
|
|
screen.cmds = [];
|
|
}
|
|
this.handleCmdPtyMap(screen.cmdptymap);
|
|
screen.cmdptymap = null;
|
|
this.fullScreen.set(screen);
|
|
this.wsControl.reconnect(true);
|
|
if (this.syncSelectedLine.get()) {
|
|
this.selectedLine.set(screen.screen.selectedline);
|
|
}
|
|
})();
|
|
|
|
}
|
|
|
|
loadTerminalRenderer(elem : Element, line : T.WebLine, cmd : T.WebCmd, width : number) : void {
|
|
let lineId = cmd.lineid;
|
|
let termWrap = this.getTermWrap(lineId);
|
|
if (termWrap != null) {
|
|
console.log("term-wrap already exists for", lineId);
|
|
return;
|
|
}
|
|
let cols = windowWidthToCols(width, this.getTermFontSize());
|
|
let usedRows = this.getContentHeight(lineutil.getWebRendererContext(line));
|
|
if (line.contentheight != null && line.contentheight != -1) {
|
|
usedRows = line.contentheight;
|
|
}
|
|
let termContext = lineutil.getWebRendererContext(line);
|
|
termWrap = new TermWrap(elem, {
|
|
termContext: termContext,
|
|
usedRows: usedRows,
|
|
termOpts: cmd.termopts,
|
|
winSize: {height: 0, width: width},
|
|
dataHandler: null,
|
|
focusHandler: (focus : boolean) => this.setTermFocus(line.linenum, focus),
|
|
isRunning: lineutil.cmdStatusIsRunning(cmd.status),
|
|
customKeyHandler: this.termCustomKeyHandler.bind(this),
|
|
fontSize: this.getTermFontSize(),
|
|
ptyDataSource: getTermPtyData,
|
|
onUpdateContentHeight: (termContext : T.RendererContext, height : number) => { this.setContentHeight(termContext, height); },
|
|
});
|
|
this.terminals[lineId] = termWrap;
|
|
if (this.localPtyOffsetMap[lineId] == null) {
|
|
this.localPtyOffsetMap[lineId] = 0;
|
|
}
|
|
this.localPtyOffsetMap[lineId] = 0;
|
|
if (this.getSelectedLine() == line.linenum) {
|
|
termWrap.giveFocus();
|
|
}
|
|
return;
|
|
}
|
|
|
|
termCustomKeyHandler(e : any, termWrap : TermWrap) : boolean {
|
|
if (e.type != "keydown" || isModKeyPress(e)) {
|
|
return false;
|
|
}
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
if (e.code == "ArrowUp") {
|
|
termWrap.terminal.scrollLines(-1);
|
|
return false;
|
|
}
|
|
if (e.code == "ArrowDown") {
|
|
termWrap.terminal.scrollLines(1);
|
|
return false;
|
|
}
|
|
if (e.code == "PageUp") {
|
|
termWrap.terminal.scrollPages(-1);
|
|
return false;
|
|
}
|
|
if (e.code == "PageDown") {
|
|
termWrap.terminal.scrollPages(1);
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
setTermFocus(lineNum : number, focus : boolean) : void {
|
|
}
|
|
|
|
getContentHeight(context : T.RendererContext) : number {
|
|
let key = context.lineId;
|
|
return this.contentHeightCache[key];
|
|
}
|
|
|
|
setContentHeight(context : T.RendererContext, height : number) : void {
|
|
let key = context.lineId;
|
|
this.contentHeightCache[key] = height;
|
|
}
|
|
|
|
unloadRenderer(lineId : string) : void {
|
|
let rmodel = this.renderers[lineId];
|
|
if (rmodel != null) {
|
|
rmodel.dispose();
|
|
delete this.renderers[lineId];
|
|
}
|
|
let term = this.terminals[lineId];
|
|
if (term != null) {
|
|
term.dispose();
|
|
delete this.terminals[lineId];
|
|
}
|
|
delete this.localPtyOffsetMap[lineId];
|
|
}
|
|
|
|
getUsedRows(context : T.RendererContext, line : T.WebLine, cmd : T.WebCmd, width : number) : number {
|
|
if (cmd == null) {
|
|
return 0;
|
|
}
|
|
let termOpts = cmd.termopts;
|
|
if (!termOpts.flexrows) {
|
|
return termOpts.rows;
|
|
}
|
|
let termWrap = this.getTermWrap(cmd.lineid);
|
|
if (termWrap == null) {
|
|
let cols = windowWidthToCols(width, this.getTermFontSize());
|
|
let usedRows = this.getContentHeight(context);
|
|
if (usedRows != null) {
|
|
return usedRows;
|
|
}
|
|
if (line.contentheight != null && line.contentheight != -1) {
|
|
return line.contentheight;
|
|
}
|
|
return (lineutil.cmdStatusIsRunning(cmd.status) ? 1 : 0);
|
|
}
|
|
return termWrap.getUsedRows();
|
|
}
|
|
|
|
getTermWrap(lineId : string) : TermWrap {
|
|
return this.terminals[lineId];
|
|
}
|
|
|
|
getRenderer(lineId : string) : T.RendererModel {
|
|
return this.renderers[lineId];
|
|
}
|
|
|
|
registerRenderer(lineId : string, renderer : T.RendererModel) {
|
|
this.renderers[lineId] = renderer;
|
|
if (this.localPtyOffsetMap[lineId] == null) {
|
|
this.localPtyOffsetMap[lineId] = 0;
|
|
}
|
|
}
|
|
|
|
checkUpdateScreenData() : void {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return;
|
|
}
|
|
// console.log("check-update", "vts=" + fullScreen.vts, "remote-vts=" + this.remoteScreenVts);
|
|
if (fullScreen.vts >= this.remoteScreenVts) {
|
|
return;
|
|
}
|
|
this.loadFullScreenData(true);
|
|
}
|
|
|
|
loadFullScreenData(update : boolean) : void {
|
|
if (isBlank(this.screenId)) {
|
|
this.setErrMessage("No ScreenId Specified, Cannot Load.");
|
|
return;
|
|
}
|
|
if (isBlank(this.viewKey)) {
|
|
this.setErrMessage("No ViewKey Specified, Cannot Load.");
|
|
return;
|
|
}
|
|
if (this.activeUpdateFetch) {
|
|
// console.log("there is already an active update fetch");
|
|
return;
|
|
}
|
|
// console.log("running screen-data update");
|
|
this.activeUpdateFetch = true;
|
|
let urlParams : Record<string, string> = {screenid: this.screenId, viewkey: this.viewKey};
|
|
if (update) {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen != null) {
|
|
urlParams.vts = String(fullScreen.vts);
|
|
}
|
|
}
|
|
let usp = new URLSearchParams(urlParams);
|
|
let url = new URL(getBaseUrl() + "/webshare/screen?" + usp.toString());
|
|
fetch(url, {method: "GET", mode: "cors", cache: "no-cache"}).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
|
let screen : T.WebFullScreen = data;
|
|
if (update) {
|
|
this.mergeUpdate(screen);
|
|
}
|
|
else {
|
|
this.setWebFullScreen(screen);
|
|
}
|
|
setTimeout(() => this.checkUpdateScreenData(), 300);
|
|
}).catch((err) => {
|
|
this.errMessage.set("Cannot get screen: " + err.message);
|
|
}).finally(() => {
|
|
this.activeUpdateFetch = false;
|
|
});
|
|
}
|
|
|
|
getLineNumFromId(lineId : string) : number {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return -1;
|
|
}
|
|
for (let i=0; i<fullScreen.lines.length; i++) {
|
|
let line = fullScreen.lines[i];
|
|
if (line.lineid == lineId) {
|
|
return line.linenum;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
getLineIndex(lineNum : number) : number {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return -1;
|
|
}
|
|
for (let i=0; i<fullScreen.lines.length; i++) {
|
|
let line = fullScreen.lines[i];
|
|
if (line.linenum == lineNum) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
getNumLines() : number {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return 0;
|
|
}
|
|
return fullScreen.lines.length;
|
|
}
|
|
|
|
getCmdById(lineId : string) : T.WebCmd {
|
|
let fullScreen = this.fullScreen.get();
|
|
if (fullScreen == null) {
|
|
return null;
|
|
}
|
|
for (let cmd of fullScreen.cmds) {
|
|
if (cmd.lineid == lineId) {
|
|
return cmd;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
docKeyDownHandler(e : any) : void {
|
|
if (isModKeyPress(e)) {
|
|
return;
|
|
}
|
|
if (e.code == "PageUp" && e.getModifierState("Meta")) {
|
|
this.updateSelectedLineIndex(-1);
|
|
}
|
|
if (e.code == "PageDown" && e.getModifierState("Meta")) {
|
|
this.updateSelectedLineIndex(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getTermPtyData(termContext : T.TermContextUnion) : Promise<T.PtyDataType> {
|
|
if ("remoteId" in termContext) {
|
|
throw new Error("remote term ptydata is not supported in webshare");
|
|
}
|
|
return WebShareModel.checkFetchPtyData(termContext.lineId, true);
|
|
}
|
|
|
|
let WebShareModel : WebShareModelClass = null;
|
|
if ((window as any).WebShareModel == null) {
|
|
WebShareModel = new WebShareModelClass();
|
|
(window as any).WebShareModel = WebShareModel;
|
|
}
|
|
|
|
export {WebShareModel, getTermPtyData};
|