getting markdown/image simple renderers working

This commit is contained in:
sawka 2023-03-16 21:48:30 -07:00
parent efb63a1466
commit bc96a1bbed
11 changed files with 739 additions and 347 deletions

View File

@ -1,97 +0,0 @@
import * as mobx from "mobx";
import {WindowSize, RendererContext, TermOptsType} from "./types";
import {getPtyData, termWidthFromCols, termHeightFromRows} from "./model";
import {incObs} from "./util";
import {PtyDataBuffer} from "./ptydata";
class ImageRendererModel {
context : RendererContext;
isDone : mobx.IObservableValue<boolean>;
reloading : boolean = false;
htmlImgDivElem : any;
htmlImg : any;
termOpts : TermOptsType;
dataBuf : PtyDataBuffer;
fontSize : number;
constructor(imgDivElem : any, context : RendererContext, termOpts : TermOptsType, isDone : boolean, fontSize : number) {
this.dataBuf = new PtyDataBuffer();
this.htmlImgDivElem = imgDivElem;
this.termOpts = termOpts;
this.context = context;
this.isDone = mobx.observable.box(isDone, {name: "isDone"});
this.fontSize = fontSize;
this.reload(0);
}
dispose() : void {
this.dataBuf.reset();
this.removeImage();
}
removeImage() : void {
this.htmlImg = null;
this.htmlImgDivElem.replaceChildren();
}
renderImage() : void {
if (!this.isDone.get()) {
return;
}
let blob = new Blob([this.dataBuf.getData()], {type: "image/jpeg"});
this.htmlImg = new Image();
this.htmlImg.src = URL.createObjectURL(blob);
this.htmlImg.style.maxHeight = termHeightFromRows(this.termOpts.rows, this.fontSize) + "px";
this.htmlImg.style.maxWidth = termWidthFromCols(this.termOpts.cols, this.fontSize) + "px";
this.htmlImgDivElem.replaceChildren(this.htmlImg);
}
reload(delayMs : number) : void {
if (this.reloading) {
return;
}
this.dataBuf.reset();
this.reloading = true;
let rtnp = getPtyData(this.context.sessionId, this.context.cmdId);
rtnp.then((ptydata) => {
setTimeout(() => {
this.reloading = false;
this.dataBuf.receiveData(ptydata.pos, ptydata.data, "reload");
this.renderImage();
}, delayMs);
}).catch((e) => {
this.dataBuf.brokenData = true;
this.reloading = false;
console.log("error reloading image data", e);
});
}
receiveData(pos : number, data : Uint8Array, reason? : string) : void {
this.dataBuf.receiveData(pos, data, reason);
}
cmdDone() : void {
mobx.action(() => {
this.isDone.set(true);
})();
}
resizeWindow(size : WindowSize) : void {
return;
}
resizeCols(cols : number) : void {
return;
}
giveFocus() : void {
return;
}
getUsedRows() : number {
return -1;
}
}
export {ImageRendererModel};

56
src/imagerenderer.tsx Normal file
View File

@ -0,0 +1,56 @@
import * as React from "react";
import * as mobx from "mobx";
import * as mobxReact from "mobx-react";
import cn from "classnames";
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import {WindowSize, RendererContext, TermOptsType, LineType, RendererOpts} from "./types";
import {getPtyData, termWidthFromCols, termHeightFromRows, GlobalModel, LineContainerModel} from "./model";
import {incObs} from "./util";
import {PtyDataBuffer} from "./ptydata";
type OV<V> = mobx.IObservableValue<V>;
type CV<V> = mobx.IComputedValue<V>;
// ctor(RendererContext, RenderOpts, isDone);
// type RendererModel = {
// dispose : () => void,
// reload : (delayMs : number) => void,
// receiveData : (pos : number, data : Uint8Array, reason? : string) => void,
// cmdDone : () => void,
// resizeWindow : (size : WindowSize) => void,
// resizeCols : (cols : number) => void,
// giveFocus : () => void,
// getUsedRows : () => number,
// };
// two types of renderers
// JSON
// blob
//
@mobxReact.observer
class SimpleImageRenderer extends React.Component<{data : Blob, context : RendererContext, opts : RendererOpts}, {}> {
objUrl : string = null;
componentWillUnmount() {
if (this.objUrl != null) {
URL.revokeObjectURL(this.objUrl);
}
}
render() {
if (this.objUrl == null) {
let dataBlob = this.props.data;
this.objUrl = URL.createObjectURL(dataBlob);
}
let opts = this.props.opts;
return (
<div className="simple-image-renderer">
<img style={{maxHeight: opts.idealSize.height, maxWidth: opts.idealSize.width}} src={this.objUrl}/>
</div>
);
}
}
export {SimpleImageRenderer};

View File

@ -5,14 +5,14 @@ import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import dayjs from "dayjs";
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {ImageRendererModel} from "./imagerenderer";
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols} from "./model";
import type {LineType, CmdDataType, FeStateType, RemoteType, RemotePtrType, RenderModeType} from "./types";
import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols, getRendererContext, getRendererType} from "./model";
import type {LineType, CmdDataType, FeStateType, RemoteType, RemotePtrType, RenderModeType, RendererContext, RendererOpts, SimpleBlobRendererComponent, RendererPluginType} from "./types";
import cn from "classnames";
import {TermWrap} from "./term";
import type {LineContainerModel} from "./model";
import {renderCmdText} from "./elements";
import {SimpleBlobRendererModel, SimpleBlobRenderer} from "./simplerenderer";
dayjs.extend(localizedFormat)
@ -377,16 +377,9 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
return (renderMode == "collapsed" && !overrideCollapsed.get());
}
renderSimple() {
getTerminalRendererHeight(cmd : Cmd) : number {
let {screen, line, width, topBorder, renderMode} = this.props;
let cmd = screen.getCmd(line);
let isCollapsed = this.isCollapsed();
let mainDivCn = cn(
"line",
"line-cmd",
{"top-border": topBorder},
{"collapsed": isCollapsed},
);
// header is 36px tall, padding+border = 6px
// collapsed header is 24px tall + 6px
// zero-terminal is 0px
@ -395,11 +388,39 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
// else: 53+(lines*lineheight)
let height = (isCollapsed ? 30 : 42); // height of zero height terminal
if (!isCollapsed) {
let usedRows = screen.getUsedRows(line, cmd, width);
let usedRows = screen.getUsedRows(getRendererContext(line), line, cmd, width);
if (usedRows > 0) {
height = 53 + termHeightFromRows(usedRows, GlobalModel.termFontSize.get());
}
}
return height;
}
renderSimple() {
let {screen, line, topBorder} = this.props;
let cmd = screen.getCmd(line);
let isCollapsed = this.isCollapsed();
let height : number = 0;
if (isBlank(line.renderer) || line.renderer == "terminal") {
height = this.getTerminalRendererHeight(cmd);
}
else {
let isCollapsed = this.isCollapsed();
if (isCollapsed) {
height = 24;
}
else {
let {screen, line, width} = this.props;
let usedRows = screen.getUsedRows(getRendererContext(line), line, cmd, width);
height = 36 + usedRows;
}
}
let mainDivCn = cn(
"line",
"line-cmd",
{"top-border": topBorder},
{"collapsed": isCollapsed},
);
return (
<div className={mainDivCn} id={this.getLineDomId()} ref={this.lineRef} data-lineid={line.lineid} data-linenum={line.linenum} data-screenid={line.screenid} style={{height: height}}>
<LineAvatar line={line} cmd={cmd}/>
@ -413,11 +434,15 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
let formattedTime = getLineDateTimeStr(line.ts);
let termOpts = cmd.getTermOpts();
let remote = model.getRemote(cmd.remoteId);
let renderer = line.renderer;
return (
<div key="meta" className="meta-wrap">
<div key="meta1" className="meta meta-line1">
<div className="ts">{formattedTime}</div>
<div>&nbsp;</div>
<If condition={!isBlank(renderer) && renderer != "terminal"}>
<div className="renderer"><i className="fa-sharp fa-solid fa-fill"/>{renderer}&nbsp;</div>
</If>
<div className="termopts">
({termOpts.rows}x{termOpts.cols})
</div>
@ -471,10 +496,11 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
{"collapsed": isCollapsed},
{"top-border": topBorder},
);
let RendererComponent : RendererComponentType = TerminalRenderer;
if (line.renderer == "image") {
RendererComponent = ImageRenderer;
}
let rendererPlugin : RendererPluginType = null;
if (!isBlank(line.renderer) && line.renderer != "terminal") {
rendererPlugin = GlobalModel.getRendererPluginByName(line.renderer);
}
let rendererType = getRendererType(line);
return (
<div className={mainDivCn} id={"line-" + getLineId(line)}
ref={this.lineRef} onClick={this.handleClick}
@ -501,7 +527,12 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
</If>
</div>
</div>
<RendererComponent screen={screen} line={line} width={width} staticRender={staticRender} visible={visible} onHeightChange={this.handleHeightChange} collapsed={isCollapsed}/>
<If condition={rendererPlugin == null}>
<TerminalRenderer screen={screen} line={line} width={width} staticRender={staticRender} visible={visible} onHeightChange={this.handleHeightChange} collapsed={isCollapsed}/>
</If>
<If condition={rendererPlugin != null}>
<SimpleBlobRenderer lcm={screen} line={line} cmd={cmd} plugin={rendererPlugin}/>
</If>
<If condition={!isCollapsed && cmd.getRtnState()}>
<div key="rtnstate" className="cmd-rtnstate" style={{visibility: ((cmd.getStatus() == "done") ? "visible" : "hidden")}}>
<If condition={rsdiff == null || rsdiff == ""}>
@ -515,7 +546,7 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
</If>
</div>
</If>
<If condition={isSelected && !isFocused}>
<If condition={isSelected && !isFocused && rendererType == "terminal"}>
<div className="cmd-hints">
<div className="hint-item color-nohover-white">focus line ({renderCmdText("L")})</div>
</div>
@ -542,13 +573,6 @@ class Line extends React.Component<{screen : LineContainerModel, line : LineType
}
}
@mobxReact.observer
class MarkdownRenderer extends React.Component<{s : Screen, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : HeightChangeCallbackType}, {}> {
render() {
return null;
}
}
@mobxReact.observer
class Prompt extends React.Component<{rptr : RemotePtrType, festate : FeStateType}, {}> {
render() {
@ -620,115 +644,6 @@ class LineText extends React.Component<{screen : LineContainerModel, line : Line
}
}
@mobxReact.observer
class ImageRenderer extends React.Component<{screen : LineContainerModel, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : () => void, collapsed : boolean}, {}> {
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, collapsed} = this.props;
if (staticRender) {
return;
}
let vis = visible && visible.get() && !collapsed;
let curVis = this.imageLoaded.get();
if (vis && !curVis) {
this.loadImage();
}
else if (!vis && curVis) {
this.unloadImage(false);
}
}
loadImage() : void {
let {screen, line} = this.props;
let model = GlobalModel;
let cmd = screen.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 = screen.loadImageRenderer(this.imageDivRef.current, line, cmd);
mobx.action(() => this.imageLoaded.set(true))();
}
unloadImage(unmount : boolean) : void {
let {screen, line} = this.props;
screen.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.dataBuf.dataVersion.get();
}
let collapsed = this.props.collapsed;
return (
<div ref={this.elemRef} className={cn("image-wrapper", {"collapsed": collapsed})}>
<div key="imagediv" ref={this.imageDivRef}></div>
<If condition={!isDone}><div className="loading-div">...</div></If>
</div>
);
}
}
@mobxReact.observer
class TerminalRenderer extends React.Component<{screen : LineContainerModel, line : LineType, width : number, staticRender : boolean, visible : OV<boolean>, onHeightChange : () => void, collapsed : boolean}, {}> {
termLoaded : mobx.IObservableValue<boolean> = mobx.observable.box(false, {name: "linecmd-term-loaded"});
@ -840,7 +755,7 @@ class TerminalRenderer extends React.Component<{screen : LineContainerModel, lin
return isPhysicalFocused && (screenFocusType == "cmd" || screenFocusType == "cmd-fg")
}, {name: "computed-isFocused"}).get();
let cmd = screen.getCmd(line); // will not be null
let usedRows = screen.getUsedRows(line, cmd, width);
let usedRows = screen.getUsedRows(getRendererContext(line), line, cmd, width);
let termHeight = termHeightFromRows(usedRows, GlobalModel.termFontSize.get());
let termLoaded = this.termLoaded.get();
return (

View File

@ -1990,16 +1990,13 @@ class ScreenWindowView extends React.Component<{screen : Screen}, {}> {
if (screen == null) {
return;
}
if (width == null || height == null || width == 0 || height == 0) {
return;
}
mobx.action(() => {
this.width.set(width);
this.height.set(height);
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let rows = windowHeightToRows(height, GlobalModel.termFontSize.get());
if (cols == 0 || rows == 0) {
console.log("cannot set screen size", rows, cols);
return;
}
screen.termSizeCallback(rows, cols);
screen.screenSizeCallback({height: height, width: width});
})();
}

64
src/markdownrenderer.tsx Normal file
View File

@ -0,0 +1,64 @@
import * as React from "react";
import * as mobx from "mobx";
import * as mobxReact from "mobx-react";
import cn from "classnames";
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
import {WindowSize, RendererContext, TermOptsType, LineType, RendererOpts} from "./types";
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
type OV<V> = mobx.IObservableValue<V>;
function LinkRenderer(props : any) : any {
let newUrl = "https://extern?" + encodeURIComponent(props.href);
return <a href={newUrl} target="_blank">{props.children}</a>
}
function HeaderRenderer(props : any, hnum : number) : any {
return (
<div className={cn("title", "is-" + hnum)}>{props.children}</div>
);
}
function CodeRenderer(props : any) : any {
return (
<code className={cn({"inline": props.inline})}>{props.children}</code>
);
}
@mobxReact.observer
class SimpleMarkdownRenderer extends React.Component<{data : Blob, context : RendererContext, opts : RendererOpts}, {}> {
markdownText : OV<string> = mobx.observable.box(null, {name: "markdownText"});
componentDidMount() {
let prtn = this.props.data.text()
prtn.then((text) => {
mobx.action(() => {
this.markdownText.set(text);
})();
});
}
render() {
if (this.markdownText.get() == null) {
return null;
}
let markdownComponents = {
a: LinkRenderer,
h1: (props) => HeaderRenderer(props, 1),
h2: (props) => HeaderRenderer(props, 2),
h3: (props) => HeaderRenderer(props, 3),
h4: (props) => HeaderRenderer(props, 4),
h5: (props) => HeaderRenderer(props, 5),
h6: (props) => HeaderRenderer(props, 6),
code: CodeRenderer,
};
let markdownText = this.markdownText.get();
return (
<div className="markdown-renderer markdown content">
<ReactMarkdown children={this.markdownText.get()} remarkPlugins={[remarkGfm]} components={markdownComponents}/>
</div>
);
}
}
export {SimpleMarkdownRenderer};

View File

@ -5,9 +5,8 @@ import {debounce} from "throttle-debounce";
import {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeDataMap, genMergeSimpleData, boundInt, isModKeyPress} from "./util";
import {TermWrap} from "./term";
import {v4 as uuidv4} from "uuid";
import type {SessionDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize, RemoteInputPacketType, FeStateType, ContextMenuOpts, RendererContext, RendererModel, PtyDataType, BookmarkType, ClientDataType, HistoryViewDataType, AlertMessageType, HistorySearchParams, FocusTypeStrs, ScreenLinesType, HistoryTypeStrs} from "./types";
import type {SessionDataType, LineType, RemoteType, HistoryItem, RemoteInstanceType, RemotePtrType, CmdDataType, FeCmdPacketType, TermOptsType, RemoteStateType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, UIContextType, HistoryInfoType, HistoryQueryOpts, FeInputPacketType, TermWinSize, RemoteInputPacketType, FeStateType, ContextMenuOpts, RendererContext, RendererModel, PtyDataType, BookmarkType, ClientDataType, HistoryViewDataType, AlertMessageType, HistorySearchParams, FocusTypeStrs, ScreenLinesType, HistoryTypeStrs, RendererPluginType, WindowSize} from "./types";
import {WSControl} from "./ws";
import {ImageRendererModel} from "./imagerenderer";
import {measureText, getMonoFontSize} from "./textmeasure";
import dayjs from "dayjs";
import localizedFormat from 'dayjs/plugin/localizedFormat';
@ -39,23 +38,45 @@ const BUILD = __PROMPT_BUILD__;
type LineContainerModel = {
loadTerminalRenderer : (elem : Element, line : LineType, cmd : Cmd, width : number) => void,
loadImageRenderer : (imageDivElem : any, line : LineType, cmd : Cmd) => ImageRendererModel,
registerRenderer : (cmdId : string, renderer : RendererModel) => void,
unloadRenderer : (cmdId : string) => void,
getUsedRows : (line : LineType, cmd : Cmd, width : number) => number,
getIsFocused : (lineNum : number) => boolean,
getTermWrap : (cmdId : string) => TermWrap;
getRenderer : (cmdId : string) => RendererModel,
getFocusType : () => "input" | "cmd" | "cmd-fg",
getSelectedLine : () => number,
getCmd : (line : LineType) => Cmd,
setTermFocus : (lineNum : number, focus : boolean) => void,
getUsedRows : (context : RendererContext, line : LineType, cmd : Cmd, width : number) => number,
getContentHeight : (context : RendererContext) => number,
setContentHeight : (context : RendererContext, height : number) => void,
getMaxContentSize() : WindowSize,
getIdealContentSize() : WindowSize,
}
type SWLinePtr = {
line : LineType,
slines : ScreenLines,
screen : Screen,
};
function getRendererType(line : LineType) : "terminal" | "plugin" {
if (isBlank(line.renderer) || line.renderer == "terminal") {
return "terminal";
}
return "plugin";
}
function getRendererContext(line : LineType) : RendererContext {
return {
sessionId: line.sessionid,
screenId: line.screenid,
cmdId: line.cmdid,
lineId: line.lineid,
lineNum: line.linenum,
};
}
function windowWidthToCols(width : number, fontSize : number) : number {
let dr = getMonoFontSize(fontSize);
let cols = Math.trunc((width - 50) / dr.width) - 1;
@ -260,6 +281,17 @@ class Cmd {
}
}
handleDataFromRenderer(data : string, renderer : RendererModel) : void {
// console.log("handle data", {data: data});
if (!this.isRunning()) {
return;
}
for (let pos=0; pos<data.length; pos += InputChunkSize) {
let dataChunk = data.slice(pos, pos+InputChunkSize);
this.handleInputChunk(dataChunk);
}
}
handleInputChunk(data : string) : void {
let inputPacket : FeInputPacketType = {
type: "feinput",
@ -279,6 +311,7 @@ class Screen {
name : OV<string>;
archived : OV<boolean>;
curRemote : OV<RemotePtrType>;
lastScreenSize : WindowSize;
lastCols : number;
lastRows : number;
selectedLine : OV<number>;
@ -287,7 +320,8 @@ class Screen {
anchorOffset : number = 0;
termLineNumFocus : OV<number>;
setAnchor_debounced : (anchorLine : number, anchorOffset : number) => void;
renderers : Record<string, RendererModel> = {}; // cmdid => TermWrap
terminals : Record<string, TermWrap> = {}; // cmdid => TermWrap
renderers : Record<string, RendererModel> = {}; // cmdid => RendererModel
constructor(sdata : ScreenDataType) {
this.sessionId = sdata.sessionid;
@ -327,6 +361,14 @@ class Screen {
})();
}
getContentHeight(context : RendererContext) : number {
return GlobalModel.getContentHeight(context);
}
setContentHeight(context : RendererContext, height : number) : void {
GlobalModel.setContentHeight(context, height);
}
getCmd(line : LineType) : Cmd {
return GlobalModel.getCmd(line);
}
@ -483,11 +525,15 @@ class Screen {
updatePtyData(ptyMsg : PtyDataUpdateType) {
let cmdId = ptyMsg.cmdid;
let renderer = this.renderers[cmdId];
if (renderer == null) {
return;
if (renderer != null) {
let data = base64ToArray(ptyMsg.ptydata64);
renderer.receiveData(ptyMsg.ptypos, data, "from-sw");
}
let term = this.terminals[cmdId];
if (term != null) {
let data = base64ToArray(ptyMsg.ptydata64);
term.receiveData(ptyMsg.ptypos, data, "from-sw");
}
let data = base64ToArray(ptyMsg.ptydata64);
renderer.receiveData(ptyMsg.ptypos, data, "from-sw");
}
isActive() : boolean {
@ -498,7 +544,44 @@ class Screen {
return (this.sessionId == activeScreen.sessionId) && (this.screenId == activeScreen.screenId);
}
termSizeCallback(rows : number, cols : number) : void {
screenSizeCallback(winSize : WindowSize) : void {
if (winSize.height == 0 || winSize.width == 0) {
return;
}
if (this.lastScreenSize != null && this.lastScreenSize.height == winSize.height && this.lastScreenSize.width == winSize.width) {
return;
}
this.lastScreenSize = winSize;
let cols = windowWidthToCols(winSize.width, GlobalModel.termFontSize.get());
let rows = windowHeightToRows(winSize.height, GlobalModel.termFontSize.get());
this._termSizeCallback(rows, cols);
}
getMaxContentSize() : WindowSize {
if (this.lastScreenSize == null) {
let width = termWidthFromCols(80, GlobalModel.termFontSize.get());
let height = termHeightFromRows(25, GlobalModel.termFontSize.get());
return {width, height};
}
let winSize = this.lastScreenSize;
let width = boundInt(winSize.width-50, 100, 5000);
let height = boundInt(winSize.height-100, 100, 5000);
return {width, height};
}
getIdealContentSize() : WindowSize {
if (this.lastScreenSize == null) {
let width = termWidthFromCols(80, GlobalModel.termFontSize.get());
let height = termHeightFromRows(25, GlobalModel.termFontSize.get());
return {width, height};
}
let winSize = this.lastScreenSize;
let width = boundInt(Math.ceil((winSize.width-50)*0.7), 100, 5000);
let height = boundInt(Math.ceil((winSize.height-100)*0.5), 100, 5000);
return {width, height};
}
_termSizeCallback(rows : number, cols : number) : void {
if (cols == 0 || rows == 0) {
return;
}
@ -507,16 +590,24 @@ class Screen {
}
this.lastRows = rows;
this.lastCols = cols;
for (let cmdid in this.renderers) {
this.renderers[cmdid].resizeCols(cols);
for (let cmdid in this.terminals) {
this.terminals[cmdid].resizeCols(cols);
}
GlobalCommandRunner.resizeScreen(this.screenId, rows, cols);
}
getTermWrap(cmdId : string) : TermWrap {
return this.terminals[cmdId];
}
getRenderer(cmdId : string) : RendererModel {
return this.renderers[cmdId];
}
registerRenderer(cmdId : string, renderer : RendererModel) {
this.renderers[cmdId] = renderer;
}
setTermFocus(lineNum : number, focus : boolean) : void {
// console.log("SW setTermFocus", lineNum, focus);
mobx.action(() => this.termLineNumFocus.set(focus ? lineNum : 0))();
@ -572,23 +663,15 @@ class Screen {
return false;
}
loadImageRenderer(imageDivElem : any, line : LineType, cmd : Cmd) : ImageRendererModel {
let cmdId = cmd.cmdId;
let context = {sessionId: this.sessionId, screenId: this.screenId, cmdId: cmdId, lineId : line.lineid, lineNum: line.linenum};
let imageModel = new ImageRendererModel(imageDivElem, context, cmd.getTermOpts(), !cmd.isRunning(), GlobalModel.termFontSize.get());
this.renderers[cmdId] = imageModel;
return imageModel;
}
loadTerminalRenderer(elem : Element, line : LineType, cmd : Cmd, width : number) {
let cmdId = cmd.cmdId;
let termWrap = this.getRenderer(cmdId);
let termWrap = this.getTermWrap(cmdId);
if (termWrap != null) {
console.log("term-wrap already exists for", this.screenId, cmdId);
return;
}
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let usedRows = GlobalModel.getTUR(this.sessionId, cmdId, cols);
let usedRows = GlobalModel.getContentHeight(getRendererContext(line));
if (line.contentheight != null && line.contentheight != -1) {
usedRows = line.contentheight;
}
@ -604,7 +687,7 @@ class Screen {
customKeyHandler: this.termCustomKeyHandler.bind(this),
fontSize: GlobalModel.termFontSize.get(),
});
this.renderers[cmdId] = termWrap;
this.terminals[cmdId] = termWrap;
if ((this.focusType.get() == "cmd" || this.focusType.get() == "cmd-fg") && this.selectedLine.get() == line.linenum) {
termWrap.giveFocus();
}
@ -617,9 +700,14 @@ class Screen {
rmodel.dispose();
delete this.renderers[cmdId];
}
let term = this.terminals[cmdId];
if (term != null) {
term.dispose();
delete this.terminals[cmdId];
}
}
getUsedRows(line : LineType, cmd : Cmd, width : number) : number {
getUsedRows(context : RendererContext, line : LineType, cmd : Cmd, width : number) : number {
if (cmd == null) {
return 0;
}
@ -627,10 +715,10 @@ class Screen {
if (!termOpts.flexrows) {
return termOpts.rows;
}
let termWrap = this.getRenderer(cmd.cmdId);
let termWrap = this.getTermWrap(cmd.cmdId);
if (termWrap == null) {
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let usedRows = GlobalModel.getTUR(this.sessionId, cmd.cmdId, cols);
let usedRows = GlobalModel.getContentHeight(context);
if (usedRows != null) {
return usedRows;
}
@ -1601,6 +1689,7 @@ type LineFocusType = {
class SpecialHistoryViewLineContainer {
historyItem : HistoryItem;
terminal : TermWrap;
renderer : RendererModel;
cmd : Cmd;
@ -1614,17 +1703,37 @@ class SpecialHistoryViewLineContainer {
}
return this.cmd;
}
setTermFocus(lineNum : number, focus : boolean) : void {
return;
}
setContentHeight(context : RendererContext, height : number) : void {
return;
}
getMaxContentSize() : WindowSize {
let width = termWidthFromCols(80, GlobalModel.termFontSize.get());
let height = termHeightFromRows(25, GlobalModel.termFontSize.get());
return {width, height};
}
getIdealContentSize() : WindowSize {
let width = termWidthFromCols(80, GlobalModel.termFontSize.get());
let height = termHeightFromRows(25, GlobalModel.termFontSize.get());
return {width, height};
}
loadTerminalRenderer(elem : Element, line : LineType, cmd : Cmd, width : number) : void {
this.unloadRenderer(null);
let cmdId = cmd.cmdId;
let termWrap = this.getRenderer(cmdId);
let termWrap = this.getTermWrap(cmdId);
if (termWrap != null) {
console.log("term-wrap already exists for", line.screenid, cmdId);
return;
}
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let usedRows = GlobalModel.getTUR(line.sessionid, cmdId, cols);
let usedRows = GlobalModel.getContentHeight(getRendererContext(line));
if (line.contentheight != null && line.contentheight != -1) {
usedRows = line.contentheight;
}
@ -1641,23 +1750,12 @@ class SpecialHistoryViewLineContainer {
fontSize: GlobalModel.termFontSize.get(),
noSetTUR: true,
});
this.renderer = termWrap;
this.terminal = termWrap;
return;
}
loadImageRenderer(imageDivElem : any, line : LineType, cmd : Cmd) : ImageRendererModel {
this.unloadRenderer(null);
let cmdId = cmd.cmdId;
let context = {
sessionId: this.historyItem.sessionid,
screenId: this.historyItem.screenid,
cmdId: cmdId,
lineId : line.lineid,
lineNum: line.linenum
};
let imageModel = new ImageRendererModel(imageDivElem, context, cmd.getTermOpts(), !cmd.isRunning(), GlobalModel.termFontSize.get());
this.renderer = imageModel;
return imageModel;
registerRenderer(cmdId : string, renderer : RendererModel) : void {
this.renderer = renderer;
}
unloadRenderer(cmdId : string) : void {
@ -1665,9 +1763,17 @@ class SpecialHistoryViewLineContainer {
this.renderer.dispose();
this.renderer = null;
}
if (this.terminal != null) {
this.terminal.dispose();
this.terminal = null;
}
}
getContentHeight(context : RendererContext) : number {
return GlobalModel.getContentHeight(context);
}
getUsedRows(line : LineType, cmd : Cmd, width : number) : number {
getUsedRows(context : RendererContext, line : LineType, cmd : Cmd, width : number) : number {
if (cmd == null) {
return 0;
}
@ -1675,10 +1781,10 @@ class SpecialHistoryViewLineContainer {
if (!termOpts.flexrows) {
return termOpts.rows;
}
let termWrap = this.getRenderer(cmd.cmdId);
let termWrap = this.getTermWrap(cmd.cmdId);
if (termWrap == null) {
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let usedRows = GlobalModel.getTUR(this.historyItem.sessionid, cmd.cmdId, cols);
let usedRows = GlobalModel.getContentHeight(context);
if (usedRows != null) {
return usedRows;
}
@ -1697,6 +1803,10 @@ class SpecialHistoryViewLineContainer {
getRenderer(cmdId : string) : RendererModel {
return this.renderer;
}
getTermWrap(cmdId : string) : TermWrap {
return this.terminal;
}
getFocusType() : "input" | "cmd" | "cmd-fg" {
return "input";
@ -2246,6 +2356,7 @@ class Model {
welcomeModalOpen : OV<boolean> = mobx.observable.box(false, {name: "welcomeModalOpen"});
screenSettingsModal : OV<{sessionId : string, screenId : string}> = mobx.observable.box(null, {name: "screenSettingsModal"});
sessionSettingsModal : OV<string> = mobx.observable.box(null, {name: "sessionSettingsModal"});
rendererPlugins : RendererPluginType[] = [];
inputModel : InputModel;
bookmarksModel : BookmarksModel;
@ -2293,6 +2404,27 @@ class Model {
setTimeout(() => this.getClientData(), 10);
}
registerRendererPlugin(plugin : RendererPluginType) {
if (isBlank(plugin.name)) {
throw new Error("invalid plugin, no name");
}
let existingPlugin = this.getRendererPluginByName(plugin.name);
if (existingPlugin != null) {
throw new Error(sprintf("plugin with name %s already registered", plugin.name));
}
this.rendererPlugins.push(plugin);
}
getRendererPluginByName(name : string) : RendererPluginType {
for (let i=0; i<this.rendererPlugins.length; i++) {
let plugin = this.rendererPlugins[i];
if (plugin.name == name) {
return plugin;
}
}
return null;
}
showAlert(alertMessage : AlertMessageType) : Promise<boolean> {
mobx.action(() => {
this.alertMessage.set(alertMessage);
@ -2415,15 +2547,15 @@ class Model {
})();
}
getTUR(sessionId : string, cmdId : string, cols : number) : number {
let key = sessionId + "/" + cmdId + "/" + cols;
getContentHeight(context : RendererContext) : number {
let key = context.sessionId + "/" + context.cmdId;
return this.termUsedRowsCache[key];
}
setTUR(termContext : RendererContext, size : TermWinSize, usedRows : number) : void {
let key = termContext.sessionId + "/" + termContext.cmdId + "/" + size.cols;
this.termUsedRowsCache[key] = usedRows;
GlobalCommandRunner.setTermUsedRows(termContext, usedRows);
setContentHeight(context : RendererContext, height : number) : void {
let key = context.sessionId + "/" + context.cmdId;
this.termUsedRowsCache[key] = height;
GlobalCommandRunner.setTermUsedRows(context, height);
}
contextScreen(e : any, screenId : string) {
@ -2504,7 +2636,11 @@ class Model {
let screen = ptr.screen;
let renderer = screen.getRenderer(cmdId);
if (renderer != null) {
renderer.cmdDone();
renderer.setIsDone();
}
let term = screen.getTermWrap(cmdId);
if (term != null) {
term.cmdDone();
}
}
}
@ -3284,12 +3420,12 @@ function _getPtyDataFromUrl(url : string) : Promise<PtyDataType> {
});
}
function getPtyData(sessionId : string, cmdId : string) {
function getPtyData(sessionId : string, cmdId : string) : Promise<PtyDataType> {
let url = sprintf(GlobalModel.getBaseHostPort() + "/api/ptyout?sessionid=%s&cmdid=%s", sessionId, cmdId);
return _getPtyDataFromUrl(url);
}
function getRemotePtyData(remoteId : string) {
function getRemotePtyData(remoteId : string) : Promise<PtyDataType> {
let url = sprintf(GlobalModel.getBaseHostPort() + "/api/remote-pty?remoteid=%s", remoteId);
return _getPtyDataFromUrl(url);
}
@ -3304,7 +3440,7 @@ if ((window as any).GlobalModel == null) {
GlobalModel = (window as any).GlobalModel;
GlobalCommandRunner = (window as any).GlobalCommandRunner;
export {Model, Session, ScreenLines, GlobalModel, GlobalCommandRunner, Cmd, Screen, riToRPtr, windowWidthToCols, windowHeightToRows, termWidthFromCols, termHeightFromRows, getPtyData, getRemotePtyData, TabColors, RemoteColors};
export {Model, Session, ScreenLines, GlobalModel, GlobalCommandRunner, Cmd, Screen, riToRPtr, windowWidthToCols, windowHeightToRows, termWidthFromCols, termHeightFromRows, getPtyData, getRemotePtyData, TabColors, RemoteColors, getRendererType, getRendererContext};
export type {LineContainerModel};

View File

@ -764,54 +764,6 @@ body::-webkit-scrollbar {
visibility: visible;
}
}
.markdown {
color: @term-white;
margin-bottom: 10px;
code {
background-color: black;
color: white;
.mono-font();
padding: 5px;
}
code.inline {
padding-top: 0;
padding-bottom: 0;
}
.title {
color: white;
margin-bottom: 0;
}
strong {
color: white;
}
a {
color: #32afff;
}
ul {
list-style-type: disc;
list-style-position: outside;
margin-left: 16px;
}
ol {
list-style-position: outside;
margin-left: 19px;
}
blockquote {
margin: 4px 10px 4px 10px;
border-radius: 3px;
background-color: #444;
padding: 2px 4px 2px 6px;
}
}
}
.screen-tabs-container {
@ -1609,13 +1561,26 @@ body::-webkit-scrollbar {
margin-right: 10px;
}
.ts, .termopts {
.ts, .termopts, .renderer {
display: flex;
color: #aaa;
margin-top: 5px;
.mono-font(11px);
}
.renderer {
color: #aaa;
margin-left: 3px;
margin-top: 5px;
.mono-font(11px);
i {
display: inline-block;
font-size: 9px;
margin-right: 2px;
margin-top: 2px;
}
}
.termopts {
display: flex;
color: #aaa;
@ -2978,3 +2943,80 @@ input[type=checkbox] {
color: #fff;
}
}
.simple-image-renderer {
padding: 10px;
}
.markdown {
color: @term-white;
margin-bottom: 10px;
code {
background-color: black;
color: white;
.mono-font();
padding: 5px;
}
code.inline {
padding-top: 0;
padding-bottom: 0;
}
.title {
color: white;
margin-bottom: 0;
}
strong {
color: white;
}
a {
color: #32afff;
}
ul {
list-style-type: disc;
list-style-position: outside;
margin-left: 16px;
}
ol {
list-style-position: outside;
margin-left: 19px;
}
blockquote {
margin: 4px 10px 4px 10px;
border-radius: 3px;
background-color: #444;
padding: 2px 4px 2px 6px;
}
pre {
background-color: inherit;
margin: 4px 10px 4px 10px;
padding: 2px 4px 2px 6px;
}
}
.markdown-renderer.markdown {
padding: 5px;
line-height: 1.5;
blockquote {
background-color: #222;
}
code {
background-color: #222;
font-size: 14px;
}
pre {
background-color: #222;
}
}

View File

@ -6,6 +6,9 @@ import {Terminal} from 'xterm';
import {Main} from "./main";
import {GlobalModel} from "./model";
import {v4 as uuidv4} from "uuid";
import {RendererPluginType} from "./types";
import {SimpleImageRenderer} from "./imagerenderer";
import {SimpleMarkdownRenderer} from "./markdownrenderer";
// @ts-ignore
let VERSION = __PROMPT_VERSION__;
@ -26,6 +29,31 @@ jbmFont200.load();
jbmFont700.load();
faFont.load();
const ImagePlugin : RendererPluginType = {
name: "image",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["image/*"],
component: SimpleImageRenderer,
};
const MarkdownPlugin : RendererPluginType = {
name: "markdown",
rendererType: "simple",
heightType: "pixels",
dataType: "blob",
collapseType: "hide",
globalCss: null,
mimeTypes: ["text/markdown"],
component: SimpleMarkdownRenderer,
};
GlobalModel.registerRendererPlugin(ImagePlugin);
GlobalModel.registerRendererPlugin(MarkdownPlugin);
document.addEventListener("DOMContentLoaded", () => {
let reactElem = React.createElement(Main, null, null);
let elem = document.getElementById("app");

194
src/simplerenderer.tsx Normal file
View File

@ -0,0 +1,194 @@
import * as React from "react";
import * as mobxReact from "mobx-react";
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, LineType} from "./types";
import {GlobalModel, LineContainerModel, getPtyData, Cmd} from "./model";
import {PtyDataBuffer} from "./ptydata";
type OV<V> = mobx.IObservableValue<V>;
type CV<V> = mobx.IComputedValue<V>;
class SimpleBlobRendererModel {
context : RendererContext;
opts : RendererOpts;
isDone : OV<boolean>;
api : RendererModelContainerApi;
savedHeight : number;
loading : OV<boolean>;
loadError : OV<string> = mobx.observable.box(null, {name: "renderer-loadError"});
ptyData : PtyDataType;
constructor() {
}
initialize(params : RendererModelInitializeParams) : void {
this.loading = mobx.observable.box(true, {name: "renderer-loading"});
this.isDone = mobx.observable.box(params.isDone, {name: "renderer-isDone"});
this.context = params.context;
this.opts = params.opts;
this.api = params.api;
this.savedHeight = params.savedHeight;
if (this.isDone.get()) {
this.reload(0);
}
}
dispose() : void {
return;
}
giveFocus() : void {
return;
}
updateOpts(update : RendererOptsUpdate) : void {
Object.assign(this.opts, update);
}
updateHeight(newHeight : number) : void {
if (this.savedHeight != newHeight) {
this.savedHeight = newHeight;
this.api.saveHeight(newHeight);
}
}
setIsDone() : void {
if (this.isDone.get()) {
return;
}
mobx.action(() => {
this.isDone.set(true);
})();
this.reload(0);
}
reload(delayMs : number) : void {
mobx.action(() => {
this.loading.set(true);
})();
let rtnp = getPtyData(this.context.sessionId, this.context.cmdId);
rtnp.then((ptydata) => {
setTimeout(() => {
this.ptyData = ptydata;
mobx.action(() => {
this.loading.set(false);
this.loadError.set(null);
})();
}, delayMs);
}).catch((e) => {
console.log("error loading data", e);
mobx.action(() => {
this.loadError.set("error loading data: " + e);
})();
});
}
receiveData(pos : number, data : Uint8Array, reason? : string) : void {
// this.dataBuf.receiveData(pos, data, reason);
}
}
function contextFromLine(line : LineType) : RendererContext {
return {
sessionId: line.sessionid,
screenId: line.screenid,
cmdId: line.cmdid,
lineId: line.lineid,
lineNum: line.linenum,
};
}
function apiAdapter(lcm : LineContainerModel, line : LineType, cmd : Cmd) : RendererModelContainerApi {
return {
saveHeight: (height : number) => {
lcm.setContentHeight(contextFromLine(line), height);
},
onFocusChanged: (focus : boolean) => {
lcm.setTermFocus(line.linenum, focus);
},
dataHandler: (data : string, model : RendererModel) => {
cmd.handleDataFromRenderer(data, model);
},
};
}
@mobxReact.observer
class SimpleBlobRenderer extends React.Component<{lcm : LineContainerModel, line : LineType, cmd : Cmd, plugin : RendererPluginType}, {}> {
model : SimpleBlobRendererModel;
wrapperDivRef : React.RefObject<any> = React.createRef();
constructor(props : any) {
super(props);
let {lcm, line, cmd} = this.props;
let context = contextFromLine(line);
let savedHeight = lcm.getContentHeight(context);
if (savedHeight == null) {
if (line.contentheight != null && line.contentheight != -1) {
savedHeight = line.contentheight;
}
else {
savedHeight = 0;
}
}
let initOpts = {
context: context,
isDone: !cmd.isRunning(),
savedHeight: savedHeight,
opts: {
maxSize: lcm.getMaxContentSize(),
idealSize: lcm.getIdealContentSize(),
termOpts: cmd.getTermOpts(),
termFontSize: GlobalModel.termFontSize.get(),
},
api: apiAdapter(lcm, line, cmd),
};
this.model = new SimpleBlobRendererModel();
this.model.initialize(initOpts);
lcm.registerRenderer(line.cmdid, this.model);
}
componentDidMount() {
this.checkHeight();
}
componentWillUnmount() {
let {lcm, line} = this.props;
lcm.unloadRenderer(line.cmdid);
}
componentDidUpdate() {
this.checkHeight();
}
checkHeight() : void {
// TODO: use resizeobserver instead of ref.
if (this.wrapperDivRef.current == null) {
return;
}
let height = this.wrapperDivRef.current.offsetHeight;
this.model.updateHeight(height);
}
render() {
let {plugin} = this.props;
let model = this.model;
if (model.loading.get()) {
return (<div style={{height: this.model.savedHeight}}>...</div>);
}
let Comp = plugin.component;
let dataBlob = new Blob([model.ptyData.data]);
return (
<div ref={this.wrapperDivRef}>
<Comp data={dataBlob} context={model.context} opts={model.opts}/>
</div>
);
}
}
export {SimpleBlobRendererModel, SimpleBlobRenderer};

View File

@ -222,7 +222,7 @@ class TermWrap {
}
this.usedRows.set(tur);
if (!this.noSetTUR) {
GlobalModel.setTUR(termContext, this.termSize, tur);
GlobalModel.setContentHeight(termContext, tur);
}
})();
}

View File

@ -1,9 +1,12 @@
import * as React from "react";
import * as mobx from "mobx";
type ShareModeType = "local" | "private" | "view" | "shared";
type FocusTypeStrs = "input"|"cmd"|"cmd-fg";
type HistoryTypeStrs = "global" | "session" | "screen";
type OV<V> = mobx.IObservableValue<V>;
type SessionDataType = {
sessionid : string,
name : string,
@ -348,7 +351,61 @@ type RendererContext = {
lineNum : number,
};
type RendererOpts = {
maxSize : WindowSize,
idealSize : WindowSize,
termOpts : TermOptsType,
termFontSize : number,
};
type RendererOptsUpdate = {
maxSize? : WindowSize,
idealSize? : WindowSize,
termOpts? : TermOptsType,
termFontSize? : number,
};
type RendererPluginType = {
name : string,
rendererType : "simple" | "full",
heightType : "rows" | "pixels",
dataType : "json" | "blob",
collapseType : "hide" | "remove",
globalCss? : string,
mimeTypes? : string[],
modelCtor? : RendererModel,
component : SimpleBlobRendererComponent,
}
type RendererModelContainerApi = {
onFocusChanged : (focus : boolean) => void,
saveHeight : (height : number) => void,
dataHandler : (data : string, model : RendererModel) => void,
};
type RendererModelInitializeParams = {
context : RendererContext,
isDone : boolean,
savedHeight : number,
opts : RendererOpts,
api : RendererModelContainerApi,
};
type RendererModel = {
initialize : (params : RendererModelInitializeParams) => void,
dispose : () => void,
reload : (delayMs : number) => void,
giveFocus : () => void,
updateOpts : (opts : RendererOptsUpdate) => void,
setIsDone : () => void,
receiveData : (pos : number, data : Uint8Array, reason? : string) => void,
};
type SimpleBlobRendererComponent = React.ComponentType<{data : Blob, context : RendererContext, opts : RendererOpts}>;
type SimpleJsonRendererComponent = React.ComponentType<{data : any, context : RendererContext, opts : RendererOpts}>;
type FullRendererComponent = React.ComponentType<{model : any}>;
type OldRendererModel = {
dispose : () => void,
reload : (delayMs : number) => void,
receiveData : (pos : number, data : Uint8Array, reason? : string) => void,
@ -417,4 +474,4 @@ type HistorySearchParams = {
type RenderModeType = "normal" | "collapsed";
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, FeStateType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs};
export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, FeStateType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate};