can preview lines inside of history view. also implement view in context

This commit is contained in:
sawka
2023-03-02 23:25:45 -08:00
parent 075e98c4cf
commit 58a8d02f9d
5 changed files with 272 additions and 33 deletions
+124 -15
View File
@@ -5,15 +5,18 @@ import {If, For, When, Otherwise, Choose} from "tsx-control-statements/component
import {sprintf} from "sprintf-js";
import {boundMethod} from "autobind-decorator";
import cn from "classnames";
import {GlobalModel, GlobalCommandRunner} from "./model";
import {HistoryItem, RemotePtrType} from "./types";
import {GlobalModel, GlobalCommandRunner, Cmd} from "./model";
import {HistoryItem, RemotePtrType, LineType, CmdDataType} from "./types";
import dayjs from "dayjs";
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {Line} from "./linecomps";
dayjs.extend(localizedFormat)
const PageSize = 50;
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>;
function isBlank(s : string) {
return (s == null || s == "");
@@ -61,6 +64,10 @@ function formatSSName(snames : Record<string, string>, scrnames : Record<string,
@mobxReact.observer
class HistoryView extends React.Component<{}, {}> {
tableRef : React.RefObject<any> = React.createRef();
tableWidth : OV<number> = mobx.observable.box(0, {name: "tableWidth"});
tableRszObs : ResizeObserver;
@boundMethod
clickCloseHandler() : void {
GlobalModel.historyViewModel.closeView();
@@ -115,7 +122,7 @@ class HistoryView extends React.Component<{}, {}> {
return;
}
else {
for (let i=0; i<hvm.items.length && i<PageSize; i++) {
for (let i=0; i<hvm.items.length; i++) {
hvm.selectedItems.set(hvm.items[i].historyid, true);
}
}
@@ -136,6 +143,37 @@ class HistoryView extends React.Component<{}, {}> {
GlobalModel.historyViewModel.setActiveItem(historyId);
}
}
checkWidth() {
if (this.tableRef.current != null) {
mobx.action(() => {
this.tableWidth.set(this.tableRef.current.offsetWidth);
})();
}
}
@boundMethod
handleTableResize() {
this.checkWidth();
}
componentDidMount() {
if (this.tableRef.current != null) {
this.tableRszObs = new ResizeObserver(this.handleTableResize.bind(this));
this.tableRszObs.observe(this.tableRef.current);
}
this.checkWidth();
}
componentWillUnmount() {
if (this.tableRszObs != null) {
this.tableRszObs.disconnect();
}
}
componentDidUpdate() {
this.checkWidth();
}
render() {
let isHidden = (GlobalModel.activeMainView.get() != "history");
@@ -150,11 +188,7 @@ class HistoryView extends React.Component<{}, {}> {
let snames = GlobalModel.getSessionNames();
let rnames = GlobalModel.getRemoteNames();
let scrnames = GlobalModel.getScreenNames();
let hasMore = false;
if (items.length > PageSize) {
items = items.slice(0, PageSize);
hasMore = true;
}
let hasMore = hvm.hasMore.get();
let offset = hvm.offset.get();
let numSelected = hvm.selectedItems.size;
let controlCheckboxIcon = "fa-sharp fa-regular fa-square";
@@ -164,7 +198,12 @@ class HistoryView extends React.Component<{}, {}> {
if (numSelected > 0 && numSelected == items.length) {
controlCheckboxIcon = "fa-sharp fa-regular fa-square-check";
}
let activeItem = hvm.activeItem.get();
let activeItemId = hvm.activeItem.get();
let activeItem = hvm.getHistoryItemById(activeItemId);
let activeLine : LineType = null;
if (activeItem != null) {
activeLine = hvm.getLineById(activeItem.lineid);
}
return (
<div className={cn("history-view", "alt-view", {"is-hidden": isHidden})}>
<div className="close-button" onClick={this.clickCloseHandler}><i className="fa-sharp fa-solid fa-xmark"></i></div>
@@ -196,7 +235,7 @@ class HistoryView extends React.Component<{}, {}> {
<div className="btn-spacer"/>
<div className={cn("showing-btn", {"is-disabled": !hasMore})} onClick={hasMore ? this.handleNext : null}><i className="fa-sharp fa-solid fa-chevron-right"/></div>
</div>
<table className="history-table" cellSpacing="0" cellPadding="0" border={0}>
<table className="history-table" cellSpacing="0" cellPadding="0" border={0} ref={this.tableRef}>
<tbody>
<For index="idx" each="item" of={items}>
<tr key={item.historyid} className={cn("history-item", {"is-selected": hvm.selectedItems.get(item.historyid)})}>
@@ -221,13 +260,13 @@ class HistoryView extends React.Component<{}, {}> {
{formatRemoteName(rnames, item.remote)}
</td>
<td className="cmdstr" onClick={() => this.activateItem(item.historyid)}>
{item.cmdstr}
<div className="cmdstr-content">{item.cmdstr}</div>
</td>
</tr>
<If condition={activeItem == item.historyid}>
<If condition={activeItemId == item.historyid}>
<tr className="active-history-item">
<td colSpan={10}>
<line sw={hvm.specialLineContainer} line={null} width={600} staticRender={true} visible={null} onHeightChange={null} overrideCollapsed={null} topBorder={false} renderMode="normal"/>
<td colSpan={6}>
<LineContainer key={activeItemId} historyId={activeItemId} width={this.tableWidth.get()}/>
</td>
</tr>
</If>
@@ -244,5 +283,75 @@ class HistoryView extends React.Component<{}, {}> {
}
}
class LineContainer extends React.Component<{historyId : string, width : number}, {}> {
line : LineType;
cmd : Cmd;
historyItem : HistoryItem;
visible : OV<boolean> = mobx.observable.box(true);
overrideCollapsed : OV<boolean> = mobx.observable.box(false);
constructor(props : any) {
super(props);
let hvm = GlobalModel.historyViewModel;
this.historyItem = hvm.getHistoryItemById(props.historyId);
if (this.historyItem == null) {
return;
}
this.line = hvm.getLineById(this.historyItem.lineid);
this.cmd = hvm.getCmdById(this.historyItem.cmdid);
}
@boundMethod
handleHeightChange(lineNum : number, newHeight : number, oldHeight : number) : void {
return;
}
@boundMethod
viewInContext() {
let screen = GlobalModel.getScreenById(this.historyItem.sessionid, this.historyItem.screenid);
if (screen == null) {
return null;
}
GlobalModel.historyViewModel.closeView();
GlobalCommandRunner.lineView(screen.sessionId, screen.screenId, this.line.linenum);
}
render() {
let hvm = GlobalModel.historyViewModel;
if (this.historyItem == null || this.props.width == 0) {
return null;
}
if (this.line == null) {
return <div className="line-container no-line"><div>[no line data]</div></div>;
}
let width = this.props.width;
width = width - 50;
if (width < 400) {
width = 400;
}
let session = GlobalModel.getSessionById(this.historyItem.sessionid);
let screen = GlobalModel.getScreenById(this.historyItem.sessionid, this.historyItem.screenid);
let ssStr = "";
let canViewInContext = false;
if (session != null && screen != null) {
ssStr = sprintf("#%s[%s]", session.name.get(), screen.name.get());
canViewInContext = true;
}
return (
<div className="line-container">
<If condition={canViewInContext}>
<div className="line-context">
<div title="View in Context" className="vic-btn" onClick={this.viewInContext}><i className="fa-sharp fa-solid fa-right"/> {ssStr}</div>
</div>
</If>
<If condition={session == null}>
<div className="no-line-context"/>
</If>
<Line sw={hvm.specialLineContainer} line={this.line} width={width} staticRender={false} visible={this.visible} onHeightChange={this.handleHeightChange} overrideCollapsed={this.overrideCollapsed} topBorder={false} renderMode="normal"/>
</div>
);
}
}
export {HistoryView};
+93 -11
View File
@@ -163,7 +163,6 @@ class Cmd {
remoteId : string;
cmdId : string;
data : OV<CmdDataType>;
watching : boolean = false;
constructor(cmd : CmdDataType) {
this.sessionId = cmd.sessionid;
@@ -1639,17 +1638,47 @@ type LineFocusType = {
class SpecialHistoryViewLineContainer {
historyItem : HistoryItem;
renderer : RendererModel;
cmd : Cmd;
constructor(hitem : HistoryItem) {
this.historyItem = hitem;
}
getCmd(line : LineType) : Cmd {
return null;
if (this.cmd == null) {
this.cmd = GlobalModel.historyViewModel.getCmdById(line.cmdid);
}
return this.cmd;
}
loadTerminalRenderer(elem : Element, line : LineType, cmd : Cmd, width : number) : void {
this.unloadRenderer(null);
let cmdId = cmd.cmdId;
let termWrap = this.getRenderer(cmdId);
if (termWrap != null) {
console.log("term-wrap already exists for", line.windowid, cmdId);
return;
}
let cols = windowWidthToCols(width, GlobalModel.termFontSize.get());
let usedRows = GlobalModel.getTUR(line.sessionid, cmdId, cols);
if (line.contentheight != null && line.contentheight != -1) {
usedRows = line.contentheight;
}
let termContext = {sessionId: line.sessionid, screenId: "(historyview)", windowId: line.windowid, cmdId: cmdId, lineId : line.lineid, lineNum: line.linenum};
termWrap = new TermWrap(elem, {
termContext: termContext,
usedRows: usedRows,
termOpts: cmd.getTermOpts(),
winSize: {height: 0, width: width},
dataHandler: null,
focusHandler: null,
isRunning: cmd.isRunning(),
customKeyHandler: null,
fontSize: GlobalModel.termFontSize.get(),
noSetTUR: true,
});
this.renderer = termWrap;
return;
}
loadImageRenderer(imageDivElem : any, line : LineType, cmd : Cmd) : ImageRendererModel {
@@ -1719,12 +1748,17 @@ const HistoryPageSize = 50;
class HistoryViewModel {
items : OArr<HistoryItem> = mobx.observable.array([], {name: "HistoryItems"});
hasMore : OV<boolean> = mobx.observable.box(false, {name: "historyview-hasmore"});
offset : OV<number> = mobx.observable.box(0, {name: "historyview-offset"});
searchText : OV<string> = mobx.observable.box("", {name: "historyview-searchtext"});
activeSearchText : string = null;
selectedItems : OMap<string, boolean> = mobx.observable.map({}, {name: "historyview-selectedItems"});
deleteActive : OV<boolean> = mobx.observable.box(false, {name: "historyview-deleteActive"});
activeItem : OV<string> = mobx.observable.box(null, {name: "historyview-activeItem"});
historyItemLines : LineType[] = [];
historyItemCmds : CmdDataType[] = [];
specialLineContainer : SpecialHistoryViewLineContainer;
constructor() {
@@ -1736,19 +1770,50 @@ class HistoryViewModel {
})();
}
getLineById(lineId : string) : LineType {
if (isBlank(lineId)) {
return null;
}
for (let i=0; i<this.historyItemLines.length; i++) {
let line = this.historyItemLines[i];
if (line.lineid == lineId) {
return line;
}
}
return null;
}
getCmdById(cmdId : string) : Cmd {
if (isBlank(cmdId)) {
return null;
}
for (let i=0; i<this.historyItemCmds.length; i++) {
let cmd = this.historyItemCmds[i];
if (cmd.cmdid == cmdId) {
return new Cmd(cmd);
}
}
return null;
}
getHistoryItemById(historyId : string) : HistoryItem {
if (isBlank(historyId)) {
return null;
}
for (let i=0; i<this.items.length; i++) {
let hitem = this.items[i];
if (hitem.historyid == historyId) {
return hitem;
}
}
return null;
}
setActiveItem(historyId : string) {
if (this.activeItem.get() == historyId) {
return;
}
let hitem : HistoryItem = null;
if (historyId != null) {
for (let i=0; i<this.items.length; i++) {
if (this.items[i].historyid == historyId) {
hitem = this.items[i];
break;
}
}
}
let hitem = this.getHistoryItemById(historyId);
mobx.action(() => {
if (hitem == null) {
this.activeItem.set(null);
@@ -1795,9 +1860,12 @@ class HistoryViewModel {
submitSearch() : void {
mobx.action(() => {
this.hasMore.set(false);
this.items.replace([]);
this.offset.set(0);
this.activeSearchText = this.searchText.get();
this.historyItemLines = [];
this.historyItemCmds = [];
})();
GlobalCommandRunner.historyView({offset: 0, searchText: this.activeSearchText});
}
@@ -1813,8 +1881,11 @@ class HistoryViewModel {
showHistoryView(data : HistoryViewDataType) : void {
mobx.action(() => {
GlobalModel.activeMainView.set("history");
this.hasMore.set(data.hasmore);
this.items.replace(data.items || []);
this.offset.set(data.offset);
this.historyItemLines = (data.lines ?? []);
this.historyItemCmds = (data.cmds ?? []);
this.selectedItems.clear();
})();
}
@@ -2890,6 +2961,17 @@ class CommandRunner {
GlobalModel.submitCommand("screen", null, [screen], {"nohist": "1"}, false);
}
lineView(sessionId : string, screenId : string, lineNum : number) {
let screen = GlobalModel.getScreenById(sessionId, screenId);
if (screen != null) {
let sw = screen.getActiveSW();
if (sw != null) {
sw.setAnchorFields(lineNum, 0, "line:view");
}
}
GlobalModel.submitCommand("line", "view", [sessionId, screenId, String(lineNum)], {"nohist": "1"}, false);
}
createNewSession() {
GlobalModel.submitCommand("session", "open", null, {"nohist": "1"}, false);
}
+45 -4
View File
@@ -307,8 +307,44 @@ body::-webkit-scrollbar {
tr.active-history-item {
td {
padding: 10px;
background-color: blue;
padding-right: 10px;
.line-container {
padding: 0px 10px 10px 10px;
overflow-x: auto;
background-color: black;
}
.line-context {
.mono-font(12px);
margin-left: 20px;
margin-bottom: 10px;
margin-top: 10px;
display: flex;
flex-direction: row;
.vic-btn {
cursor: pointer;
color: #ccc;
&:hover {
color: white;
}
}
}
.no-line-context {
height: 10px;
}
.line-container.no-line {
.mono-font(12px);
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 20px;
}
}
}
@@ -400,13 +436,18 @@ body::-webkit-scrollbar {
.mono-font(12px);
color: white;
background-color: black;
flex: 1 0 auto;
flex: 1 0 0;
padding-left: 20px;
border-radius: 3px;
white-space: pre;
max-height: 64px;
overflow-y: auto;
cursor: pointer;
min-width: 300px;
overflow-y: auto;
.cmdstr-content {
overflow-x: auto;
}
}
}
}
+7 -2
View File
@@ -29,7 +29,8 @@ type TermWrapOpts = {
dataHandler? : (data : string, termWrap : TermWrap) => void,
isRunning : boolean,
customKeyHandler? : (event : any, termWrap : TermWrap) => boolean,
fontSize: number,
fontSize : number,
noSetTUR? : boolean,
};
// cmd-instance
@@ -50,6 +51,7 @@ class TermWrap {
focusHandler : (focus : boolean) => void;
isRunning : boolean;
fontSize : number;
noSetTUR : boolean;
constructor(elem : Element, opts : TermWrapOpts) {
opts = opts ?? ({} as any);
@@ -60,6 +62,7 @@ class TermWrap {
this.focusHandler = opts.focusHandler;
this.isRunning = opts.isRunning;
this.fontSize = opts.fontSize;
this.noSetTUR = !!opts.noSetTUR;
if (this.flexRows) {
this.atRowMax = false;
this.usedRows = mobx.observable.box(opts.usedRows ?? (opts.isRunning ? 1 : 0), {name: "term-usedrows"});
@@ -218,7 +221,9 @@ class TermWrap {
return;
}
this.usedRows.set(tur);
GlobalModel.setTUR(termContext, this.termSize, tur);
if (!this.noSetTUR) {
GlobalModel.setTUR(termContext, this.termSize, tur);
}
})();
}
+3 -1
View File
@@ -290,9 +290,11 @@ type ModelUpdateType = {
};
type HistoryViewDataType = {
totalcount : number,
offset : number,
items : HistoryItem[],
lines : LineType[],
cmds : CmdDataType[],
hasmore : boolean,
};
type BookmarkType = {