mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
sort working image renderer
This commit is contained in:
+61
-5
@@ -1,18 +1,32 @@
|
||||
import * as mobx from "mobx";
|
||||
import {WindowSize} from "./types";
|
||||
import {WindowSize, RendererContext, TermOptsType} from "./types";
|
||||
import {getPtyData, termWidthFromCols, termHeightFromRows} from "./model";
|
||||
import {incObs} from "./util";
|
||||
|
||||
const InitialSize = 10*1024;
|
||||
const IncreaseFactor = 1.5;
|
||||
|
||||
class ImageRenderer {
|
||||
class ImageRendererModel {
|
||||
context : RendererContext;
|
||||
dataSize : number;
|
||||
imageData : Uint8Array;
|
||||
brokenData : boolean;
|
||||
isDone : mobx.IObservableValue<boolean>;
|
||||
reloading : boolean = false;
|
||||
dataVersion : mobx.IObservableValue<number>;
|
||||
htmlImgDivElem : any;
|
||||
htmlImg : any;
|
||||
termOpts : TermOptsType;
|
||||
|
||||
constructor() {
|
||||
constructor(imgDivElem : any, context : RendererContext, termOpts : TermOptsType, isDone : boolean) {
|
||||
this.htmlImgDivElem = imgDivElem;
|
||||
this.termOpts = termOpts;
|
||||
this.context = context;
|
||||
this._resetData();
|
||||
this.isDone = mobx.observable.box(false, {name: "isDone"});
|
||||
this.isDone = mobx.observable.box(isDone, {name: "isDone"});
|
||||
this.dataVersion = mobx.observable.box(0, {name: "dataVersion"});
|
||||
this.reload(0);
|
||||
console.log("image", this.termOpts);
|
||||
}
|
||||
|
||||
_resetData() {
|
||||
@@ -23,9 +37,44 @@ class ImageRenderer {
|
||||
|
||||
dispose() : void {
|
||||
this._resetData();
|
||||
this.removeImage();
|
||||
}
|
||||
|
||||
removeImage() : void {
|
||||
this.htmlImg = null;
|
||||
this.htmlImgDivElem.replaceChildren();
|
||||
}
|
||||
|
||||
renderImage() : void {
|
||||
if (!this.isDone.get()) {
|
||||
return;
|
||||
}
|
||||
let blob = new Blob([this.imageData.slice(0, this.dataSize)], {type: "image/jpeg"});
|
||||
this.htmlImg = new Image();
|
||||
this.htmlImg.src = URL.createObjectURL(blob);
|
||||
this.htmlImg.style.maxHeight = termHeightFromRows(this.termOpts.rows) + "px";
|
||||
this.htmlImg.style.maxWidth = termWidthFromCols(this.termOpts.cols) + "px";
|
||||
this.htmlImgDivElem.replaceChildren(this.htmlImg);
|
||||
}
|
||||
|
||||
reload(delayMs : number) : void {
|
||||
if (this.reloading) {
|
||||
return;
|
||||
}
|
||||
this._resetData();
|
||||
this.reloading = true;
|
||||
let rtnp = getPtyData(this.context.sessionId, this.context.cmdId);
|
||||
rtnp.then((ptydata) => {
|
||||
setTimeout(() => {
|
||||
this.reloading = false;
|
||||
this.receiveData(ptydata.pos, ptydata.data, "reload");
|
||||
this.renderImage();
|
||||
}, delayMs);
|
||||
}).catch((e) => {
|
||||
this.brokenData = true;
|
||||
this.reloading = false;
|
||||
console.log("error reloading image data", e);
|
||||
});
|
||||
}
|
||||
|
||||
_growArray(minSize : number) : void {
|
||||
@@ -48,18 +97,25 @@ class ImageRenderer {
|
||||
}
|
||||
this.imageData.set(data, pos);
|
||||
this.dataSize += data.length;
|
||||
incObs(this.dataVersion);
|
||||
}
|
||||
|
||||
cmdDone() : void {
|
||||
mobx.action(() => {
|
||||
this.isDone.set(true);
|
||||
})();
|
||||
}
|
||||
|
||||
resizeWindow(size : WindowSize) : void {
|
||||
return;
|
||||
}
|
||||
|
||||
resizeCols(cols : number) : void {
|
||||
return;
|
||||
}
|
||||
|
||||
giveFocus() : void {
|
||||
return;
|
||||
}
|
||||
|
||||
getUsedRows() : number {
|
||||
@@ -67,5 +123,5 @@ class ImageRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
export {ImageRenderer};
|
||||
export {ImageRendererModel};
|
||||
|
||||
|
||||
+117
-1
@@ -13,6 +13,7 @@ import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import {GlobalModel, GlobalCommandRunner, Session, Cmd, Window, Screen, ScreenWindow, riToRPtr, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols} from "./model";
|
||||
import {isModKeyPress} from "./util";
|
||||
import {ImageRendererModel} from "./imagerenderer";
|
||||
|
||||
dayjs.extend(localizedFormat)
|
||||
|
||||
@@ -29,6 +30,9 @@ type OMap<K,V> = mobx.ObservableMap<K,V>;
|
||||
|
||||
type HeightChangeCallbackType = (lineNum : number, newHeight : number, oldHeight : number) => void;
|
||||
|
||||
type RendererComponentProps = {sw : ScreenWindow, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : HeightChangeCallbackType};
|
||||
type RendererComponentType = { new(props : RendererComponentProps) : React.Component<RendererComponentProps, {}> };
|
||||
|
||||
type InterObsValue = {
|
||||
sessionid : string,
|
||||
windowid : string,
|
||||
@@ -213,6 +217,114 @@ class Prompt extends React.Component<{rptr : RemotePtrType, festate : FeStateTyp
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class ImageRenderer extends React.Component<{sw : ScreenWindow, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : () => void}, {}> {
|
||||
elemRef : React.RefObject<any> = React.createRef();
|
||||
imageDivRef : React.RefObject<any> = React.createRef();
|
||||
imageLoaded : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "imageLoaded"});
|
||||
imageModel : ImageRendererModel;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.componentDidUpdate(null, null, null);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.imageLoaded.get()) {
|
||||
this.unloadImage(true);
|
||||
}
|
||||
}
|
||||
|
||||
getSnapshotBeforeUpdate(prevProps, prevState) : {height : number} {
|
||||
let elem = this.elemRef.current;
|
||||
if (elem == null) {
|
||||
return {height: 0};
|
||||
}
|
||||
return {height: elem.offsetHeight};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot : {height : number}) : void {
|
||||
if (this.props.onHeightChange == null) {
|
||||
return;
|
||||
}
|
||||
let {line} = this.props;
|
||||
let curHeight = 0;
|
||||
let elem = this.elemRef.current;
|
||||
if (elem != null) {
|
||||
curHeight = elem.offsetHeight;
|
||||
}
|
||||
if (snapshot == null) {
|
||||
snapshot = {height: 0};
|
||||
}
|
||||
if (snapshot.height != curHeight) {
|
||||
this.props.onHeightChange();
|
||||
// console.log("image-render height change: ", line.linenum, snapshot.height, "=>", curHeight);
|
||||
}
|
||||
this.checkLoad();
|
||||
}
|
||||
|
||||
checkLoad() : void {
|
||||
let {line, staticRender, visible} = this.props;
|
||||
if (staticRender) {
|
||||
return;
|
||||
}
|
||||
let vis = visible && visible.get();
|
||||
let curVis = this.imageLoaded.get();
|
||||
if (vis && !curVis) {
|
||||
this.loadImage();
|
||||
}
|
||||
else if (!vis && curVis) {
|
||||
this.unloadImage(false);
|
||||
}
|
||||
}
|
||||
|
||||
loadImage() : void {
|
||||
let {sw, line} = this.props;
|
||||
let model = GlobalModel;
|
||||
let cmd = model.getCmd(line);
|
||||
if (cmd == null) {
|
||||
return;
|
||||
}
|
||||
let elem = this.imageDivRef.current;
|
||||
if (elem == null) {
|
||||
console.log("cannot load image, no elem found");
|
||||
return;
|
||||
}
|
||||
this.imageModel = sw.loadImageRenderer(this.imageDivRef.current, line, cmd);
|
||||
mobx.action(() => this.imageLoaded.set(true))();
|
||||
}
|
||||
|
||||
unloadImage(unmount : boolean) : void {
|
||||
let {sw, line} = this.props;
|
||||
sw.unloadRenderer(line.cmdid);
|
||||
this.imageModel = null;
|
||||
if (!unmount) {
|
||||
mobx.action(() => this.imageLoaded.set(false))();
|
||||
if (this.imageDivRef.current != null) {
|
||||
this.imageDivRef.current.replaceChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let imageModel = this.imageModel;
|
||||
let isLoaded = this.imageLoaded.get();
|
||||
let isDone = (imageModel != null && imageModel.isDone.get());
|
||||
if (imageModel != null) {
|
||||
let dataVersion = imageModel.dataVersion.get();
|
||||
}
|
||||
return (
|
||||
<div ref={this.elemRef} className={"image-wrapper"}>
|
||||
<div key="imagediv" ref={this.imageDivRef}></div>
|
||||
<If condition={!isDone}><div className="loading-div">...</div></If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class TerminalRenderer extends React.Component<{sw : ScreenWindow, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : () => void}, {}> {
|
||||
termLoaded : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "linecmd-term-loaded"});
|
||||
@@ -545,6 +657,10 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
{"cmd-done": !cmd.isRunning()},
|
||||
{"has-rtnstate": cmd.getRtnState()},
|
||||
);
|
||||
let RendererComponent : RendererComponentType = TerminalRenderer;
|
||||
if (line.renderer == "image") {
|
||||
RendererComponent = ImageRenderer;
|
||||
}
|
||||
return (
|
||||
<div className={mainDivCn} id={"line-" + getLineId(line)}
|
||||
ref={this.lineRef} onClick={this.handleClick}
|
||||
@@ -575,7 +691,7 @@ class LineCmd extends React.Component<{sw : ScreenWindow, line : LineType, width
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
<TerminalRenderer sw={sw} line={line} width={width} staticRender={staticRender} visible={visible} onHeightChange={this.handleHeightChange}/>
|
||||
<RendererComponent sw={sw} line={line} width={width} staticRender={staticRender} visible={visible} onHeightChange={this.handleHeightChange}/>
|
||||
<If condition={cmd.getRtnState()}>
|
||||
<div key="rtnstate" className="cmd-rtnstate" style={{visibility: ((cmd.getStatus() == "done") ? "visible" : "hidden")}}>
|
||||
<If condition={rsdiff == null || rsdiff == ""}>
|
||||
|
||||
+9
-1
@@ -7,7 +7,7 @@ import {TermWrap} from "./term";
|
||||
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, FeInputPacketType, TermWinSize, RemoteInputPacketType, FeStateType, ContextMenuOpts, RendererContext, RendererModel, PtyDataType} from "./types";
|
||||
import {WSControl} from "./ws";
|
||||
import {ImageRenderer} from "./imagerenderer";
|
||||
import {ImageRendererModel} from "./imagerenderer";
|
||||
|
||||
var GlobalUser = "sawka";
|
||||
const DefaultCellWidth = 7.203125;
|
||||
@@ -550,6 +550,14 @@ class ScreenWindow {
|
||||
return false;
|
||||
}
|
||||
|
||||
loadImageRenderer(imageDivElem : any, line : LineType, cmd : Cmd) : ImageRendererModel {
|
||||
let cmdId = cmd.cmdId;
|
||||
let context = {sessionId: this.sessionId, screenId: this.screenId, windowId: this.windowId, cmdId: cmdId, lineId : line.lineid, lineNum: line.linenum};
|
||||
let imageModel = new ImageRendererModel(imageDivElem, context, cmd.getTermOpts(), !cmd.isRunning());
|
||||
this.renderers[cmdId] = imageModel;
|
||||
return imageModel;
|
||||
}
|
||||
|
||||
loadTerminalRenderer(elem : Element, line : LineType, cmd : Cmd, width : number) {
|
||||
let cmdId = cmd.cmdId;
|
||||
let termWrap = this.getRenderer(cmdId);
|
||||
|
||||
@@ -474,6 +474,17 @@ html, body, #main {
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
padding: 2px 10px 5px 4px;
|
||||
margin: 4px 8px 0 -4px;
|
||||
|
||||
.loading-div {
|
||||
height: 20px;
|
||||
margin-left: 50px;
|
||||
.mono-font();
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-wrapper {
|
||||
background-color: #000;
|
||||
padding: 2px 10px 5px 4px;
|
||||
|
||||
@@ -26,6 +26,7 @@ type LineType = {
|
||||
linelocal : boolean,
|
||||
linetype : string,
|
||||
text : string,
|
||||
renderer : string,
|
||||
cmdid? : string,
|
||||
contentheight? : number,
|
||||
star? : number,
|
||||
|
||||
+7
-1
@@ -176,4 +176,10 @@ function isModKeyPress(e : any) {
|
||||
return e.code.match(/^(Control|Meta|Alt|Shift)(Left|Right)$/);
|
||||
}
|
||||
|
||||
export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, parseEnv0, boundInt, isModKeyPress};
|
||||
function incObs(inum : mobx.IObservableValue<number>) {
|
||||
mobx.action(() => {
|
||||
inum.set(inum.get() + 1);
|
||||
})();
|
||||
}
|
||||
|
||||
export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeSimpleData, parseEnv0, boundInt, isModKeyPress, incObs};
|
||||
|
||||
Reference in New Issue
Block a user