diff --git a/src/history.tsx b/src/history.tsx index 99807369..7aa891d2 100644 --- a/src/history.tsx +++ b/src/history.tsx @@ -547,7 +547,7 @@ class LineContainer extends React.Component<{historyId : string, width : number}
- +
); } diff --git a/src/imagerenderer.tsx b/src/imagerenderer.tsx index ec6063ee..a01d8aea 100644 --- a/src/imagerenderer.tsx +++ b/src/imagerenderer.tsx @@ -4,7 +4,8 @@ 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 {LineContainerModel} from "./model"; +import {termWidthFromCols, termHeightFromRows} from "./textmeasure"; import {incObs} from "./util"; import {PtyDataBuffer} from "./ptydata"; diff --git a/src/linecomps.tsx b/src/linecomps.tsx index 8e682064..087ab4ff 100644 --- a/src/linecomps.tsx +++ b/src/linecomps.tsx @@ -6,13 +6,17 @@ import {boundMethod} from "autobind-decorator"; import dayjs from "dayjs"; import localizedFormat from 'dayjs/plugin/localizedFormat'; import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components"; -import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols, getRendererContext, getRendererType} from "./model"; +import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, getRendererContext} from "./model"; +import {windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols} from "./textmeasure"; 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"; +import {isBlank} from "./util"; +import {PluginModel} from "./plugins"; +import * as lineutil from "./lineutil"; dayjs.extend(localizedFormat) @@ -24,10 +28,6 @@ type HeightChangeCallbackType = (lineNum : number, newHeight : number, oldHeight type RendererComponentProps = {screen : LineContainerModel, line : LineType, width : number, staticRender : boolean, visible : OV, onHeightChange : HeightChangeCallbackType, collapsed : boolean}; type RendererComponentType = { new(props : RendererComponentProps) : React.Component }; -function isBlank(s : string) : boolean { - return (s == null || s == ""); -} - function makeFullRemoteRef(ownerName : string, remoteRef : string, name : string) : string { if (isBlank(ownerName) && isBlank(name)) { return remoteRef; @@ -75,40 +75,8 @@ function getCwdStr(remote : RemoteType, state : FeStateType) : string { return cwd; } -function getLineDateTimeStr(ts : number) : string { - let lineDate = new Date(ts); - let nowDate = new Date(); - - if (nowDate.getFullYear() != lineDate.getFullYear()) { - return dayjs(lineDate).format("ddd L LTS"); - } - else if (nowDate.getMonth() != lineDate.getMonth() || nowDate.getDate() != lineDate.getDate()) { - let yesterdayDate = (new Date()); - yesterdayDate.setDate(yesterdayDate.getDate()-1); - if (yesterdayDate.getMonth() == lineDate.getMonth() && yesterdayDate.getDate() == lineDate.getDate()) { - return "Yesterday " + dayjs(lineDate).format("LTS");; - } - return dayjs(lineDate).format("ddd L LTS"); - } - else { - return dayjs(lineDate).format("LTS"); - } -} - @mobxReact.observer -class LineAvatar extends React.Component<{line : LineType, cmd : Cmd}, {}> { - @boundMethod - handleRightClick(e : any) { - e.preventDefault(); - e.stopPropagation(); - let {line} = this.props; - if (line != null) { - mobx.action(() => { - GlobalModel.lineSettingsModal.set(line); - })(); - } - } - +class LineAvatar extends React.Component<{line : LineType, cmd : Cmd, onRightClick? : (e : any) => void}, {}> { render() { let {line, cmd} = this.props; let lineNumStr = (line.linenumtemp ? "~" : "") + String(line.linenum); @@ -116,7 +84,7 @@ class LineAvatar extends React.Component<{line : LineType, cmd : Cmd}, {}> { let rtnstate = (cmd != null ? cmd.getRtnState() : false); let isComment = (line.linetype == "text"); return ( -
this.handleRightClick(e)} className={cn("avatar", "num-"+lineNumStr.length, "status-" + status, {"ephemeral": line.ephemeral}, {"rtnstate": rtnstate})}> +
{lineNumStr} @@ -132,7 +100,6 @@ class LineAvatar extends React.Component<{line : LineType, cmd : Cmd}, {}> { } } - @mobxReact.observer class LineCmd extends React.Component<{screen : LineContainerModel, line : LineType, width : number, staticRender : boolean, visible : OV, onHeightChange : HeightChangeCallbackType, topBorder : boolean, renderMode : RenderModeType, overrideCollapsed : OV, noSelect? : boolean, showHints? : boolean}, {}> { lineRef : React.RefObject = React.createRef(); @@ -231,18 +198,18 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
-
{cmd.getFullCmdText()}
+
{lineutil.getFullCmdText(cmd.getCmdStr())}
); } - let isMultiLine = cmd.isMultiLineCmdText(); + let isMultiLine = lineutil.isMultiLineCmdText(cmd.getCmdStr()); return (
- {cmd.getSingleLineCmdText()} + {lineutil.getSingleLineCmdText(cmd.getCmdStr())}
...▼
@@ -389,6 +356,21 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT return height; } + @boundMethod + onAvatarRightClick(e : any) : void { + let {line, noSelect} = this.props; + if (noSelect) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if (line != null) { + mobx.action(() => { + GlobalModel.lineSettingsModal.set(line); + })(); + } + } + renderSimple() { let {screen, line, topBorder} = this.props; let cmd = screen.getCmd(line); @@ -436,7 +418,7 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT renderMetaWrap(cmd : Cmd) { let {line} = this.props; let model = GlobalModel; - let formattedTime = getLineDateTimeStr(line.ts); + let formattedTime = lineutil.getLineDateTimeStr(line.ts); let termOpts = cmd.getTermOpts(); let remote = model.getRemote(cmd.remoteId); let renderer = line.renderer; @@ -459,6 +441,16 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT
); } + + getRendererOpts(cmd : Cmd) : RendererOpts { + let {screen} = this.props; + return { + maxSize: screen.getMaxContentSize(), + idealSize: screen.getIdealContentSize(), + termOpts: cmd.getTermOpts(), + termFontSize: GlobalModel.termFontSize.get(), + }; + } render() { let {screen, line, width, staticRender, visible, topBorder, renderMode} = this.props; @@ -468,7 +460,7 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT if (staticRender || !isVisible) { return this.renderSimple(); } - let formattedTime = getLineDateTimeStr(line.ts); + let formattedTime = lineutil.getLineDateTimeStr(line.ts); let cmd = screen.getCmd(line); if (cmd == null) { return ( @@ -507,16 +499,16 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT let rendererPlugin : RendererPluginType = null; let isNoneRenderer = (line.renderer == "none"); if (!isBlank(line.renderer) && line.renderer != "terminal" && !isNoneRenderer) { - rendererPlugin = GlobalModel.getRendererPluginByName(line.renderer); + rendererPlugin = PluginModel.getRendererPluginByName(line.renderer); } - let rendererType = getRendererType(line); + let rendererType = lineutil.getRendererType(line); return (
- +
@@ -535,7 +527,7 @@ class LineCmd extends React.Component<{screen : LineContainerModel, line : LineT - +
@@ -622,9 +614,24 @@ class LineText extends React.Component<{screen : LineContainerModel, line : Line GlobalCommandRunner.screenSelectLine(String(line.linenum)); } + @boundMethod + onAvatarRightClick(e : any) : void { + let {line, noSelect} = this.props; + if (noSelect) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if (line != null) { + mobx.action(() => { + GlobalModel.lineSettingsModal.set(line); + })(); + } + } + render() { let {screen, line, topBorder, renderMode} = this.props; - let formattedTime = getLineDateTimeStr(line.ts); + let formattedTime = lineutil.getLineDateTimeStr(line.ts); let isSelected = mobx.computed(() => (screen.getSelectedLine() == line.linenum), {name: "computed-isSelected"}).get(); let isFocused = mobx.computed(() => (screen.getFocusType() == "cmd"), {name: "computed-isFocused"}).get(); let isCollapsed = (renderMode == "collapsed"); @@ -638,7 +645,7 @@ class LineText extends React.Component<{screen : LineContainerModel, line : Line return (
- +
{formattedTime}
diff --git a/src/lineutil.ts b/src/lineutil.ts new file mode 100644 index 00000000..8f25210b --- /dev/null +++ b/src/lineutil.ts @@ -0,0 +1,76 @@ +import dayjs from "dayjs"; +import localizedFormat from 'dayjs/plugin/localizedFormat'; +import {isBlank, getDateStr} from "./util"; +import {LineType, WebLine} from "./types"; + +dayjs.extend(localizedFormat) + +function getRendererType(line : LineType|WebLine) : "terminal" | "plugin" { + if (isBlank(line.renderer) || line.renderer == "terminal") { + return "terminal"; + } + return "plugin"; +} + +function getLineDateStr(todayDate : string, yesterdayDate : string, ts : number) : string { + let lineDate = new Date(ts); + let dateStr = getDateStr(lineDate); + if (dateStr == todayDate) { + return "today"; + } + if (dateStr == yesterdayDate) { + return "yesterday"; + } + return dateStr; +} + +function getLineDateTimeStr(ts : number) : string { + let lineDate = new Date(ts); + let nowDate = new Date(); + + if (nowDate.getFullYear() != lineDate.getFullYear()) { + return dayjs(lineDate).format("ddd L LTS"); + } + else if (nowDate.getMonth() != lineDate.getMonth() || nowDate.getDate() != lineDate.getDate()) { + let yesterdayDate = (new Date()); + yesterdayDate.setDate(yesterdayDate.getDate()-1); + if (yesterdayDate.getMonth() == lineDate.getMonth() && yesterdayDate.getDate() == lineDate.getDate()) { + return "Yesterday " + dayjs(lineDate).format("LTS");; + } + return dayjs(lineDate).format("ddd L LTS"); + } + else { + return dayjs(lineDate).format("LTS"); + } +} + +function isMultiLineCmdText(cmdText : string) : boolean { + if (cmdText == null) { + return false; + } + cmdText = cmdText.trim(); + let nlIdx = cmdText.indexOf("\n"); + return (nlIdx != -1); +} + +function getFullCmdText(cmdText : string) { + if (cmdText == null) { + return "(none)"; + } + cmdText = cmdText.trim(); + return cmdText; +} + +function getSingleLineCmdText(cmdText : string) { + if (cmdText == null) { + return "(none)"; + } + cmdText = cmdText.trim(); + let nlIdx = cmdText.indexOf("\n"); + if (nlIdx != -1) { + cmdText = cmdText.substr(0, nlIdx); + } + return cmdText; +} + +export {getRendererType, getLineDateStr, getLineDateTimeStr, isMultiLineCmdText, getFullCmdText, getSingleLineCmdText}; diff --git a/src/main.tsx b/src/main.tsx index 6c1b692d..dc09315e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -11,7 +11,8 @@ import dayjs from "dayjs"; import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem, HistoryQueryOpts, RemoteEditType, FeStateType, ContextMenuOpts, BookmarkType, RenderModeType, ClientMigrationInfo} from "./types"; import type * as T from "./types"; import localizedFormat from 'dayjs/plugin/localizedFormat'; -import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, riToRPtr, windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols, TabColors, RemoteColors} from "./model"; +import {GlobalModel, GlobalCommandRunner, Session, Cmd, ScreenLines, Screen, riToRPtr, TabColors, RemoteColors} from "./model"; +import {windowWidthToCols, windowHeightToRows, termHeightFromRows, termWidthFromCols} from "./textmeasure"; import {isModKeyPress, boundInt} from "./util"; import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' diff --git a/src/model.ts b/src/model.ts index 5a79adb1..e045baab 100644 --- a/src/model.ts +++ b/src/model.ts @@ -5,9 +5,9 @@ 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, RendererPluginType, WindowSize, ClientMigrationInfo, WebShareOpts} 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, ClientMigrationInfo, WebShareOpts, TermContextUnion} from "./types"; import {WSControl} from "./ws"; -import {measureText, getMonoFontSize} from "./textmeasure"; +import {measureText, getMonoFontSize, windowWidthToCols, windowHeightToRows, termWidthFromCols, termHeightFromRows} from "./textmeasure"; import dayjs from "dayjs"; import localizedFormat from 'dayjs/plugin/localizedFormat'; import customParseFormat from 'dayjs/plugin/customParseFormat'; @@ -18,8 +18,6 @@ dayjs.extend(localizedFormat) var GlobalUser = "sawka"; const RemotePtyRows = 8; // also in main.tsx const RemotePtyCols = 80; -const MinTermCols = 10; -const MaxTermCols = 1024; const ProdServerEndpoint = "http://localhost:1619"; const ProdServerWsEndpoint = "ws://localhost:1623"; const DevServerEndpoint = "http://localhost:8090"; @@ -60,13 +58,6 @@ type SWLinePtr = { 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 { screenId: line.screenid, @@ -76,32 +67,6 @@ function getRendererContext(line : LineType) : RendererContext { }; } -function windowWidthToCols(width : number, fontSize : number) : number { - let dr = getMonoFontSize(fontSize); - let cols = Math.trunc((width - 50) / dr.width) - 1; - cols = boundInt(cols, MinTermCols, MaxTermCols); - return cols; -} - -function windowHeightToRows(height : number, fontSize : number) : number { - let dr = getMonoFontSize(fontSize); - let rows = Math.floor((height - 80) / dr.height) - 1; - if (rows <= 0) { - rows = 1; - } - return rows; -} - -function termWidthFromCols(cols : number, fontSize : number) : number { - let dr = getMonoFontSize(fontSize); - return Math.ceil(dr.width*cols) + 15; -} - -function termHeightFromRows(rows : number, fontSize : number) : number { - let dr = getMonoFontSize(fontSize); - return Math.ceil(dr.height*rows); -} - function cmdStatusIsRunning(status : string) : boolean { return status == "running" || status == "detached"; } @@ -227,38 +192,6 @@ class Cmd { return this.data.get().festate; } - isMultiLineCmdText() : boolean { - let cmdText = this.data.get().cmdstr; - if (cmdText == null) { - return false; - } - cmdText = cmdText.trim(); - let nlIdx = cmdText.indexOf("\n"); - return (nlIdx != -1); - } - - getSingleLineCmdText() { - let cmdText = this.data.get().cmdstr; - if (cmdText == null) { - return "(none)"; - } - cmdText = cmdText.trim(); - let nlIdx = cmdText.indexOf("\n"); - if (nlIdx != -1) { - cmdText = cmdText.substr(0, nlIdx); - } - return cmdText; - } - - getFullCmdText() { - let cmdText = this.data.get().cmdstr; - if (cmdText == null) { - return "(none)"; - } - cmdText = cmdText.trim(); - return cmdText; - } - isRunning() : boolean { let data = this.data.get(); return cmdStatusIsRunning(data.status); @@ -691,6 +624,8 @@ class Screen { isRunning: cmd.isRunning(), customKeyHandler: this.termCustomKeyHandler.bind(this), fontSize: GlobalModel.termFontSize.get(), + ptyDataSource: getTermPtyData, + onUpdateContentHeight: (termContext : RendererContext, height : number) => { GlobalModel.setContentHeight(termContext, height); }, }); this.terminals[cmdId] = termWrap; if ((this.focusType.get() == "cmd" || this.focusType.get() == "cmd-fg") && this.selectedLine.get() == line.linenum) { @@ -1639,6 +1574,8 @@ class InputModel { focusHandler: this.setRemoteTermWrapFocus.bind(this), isRunning: true, fontSize: GlobalModel.termFontSize.get(), + ptyDataSource: getTermPtyData, + onUpdateContentHeight: null, }); } } @@ -1757,7 +1694,8 @@ class SpecialHistoryViewLineContainer { isRunning: cmd.isRunning(), customKeyHandler: null, fontSize: GlobalModel.termFontSize.get(), - noSetTUR: true, + ptyDataSource: getTermPtyData, + onUpdateContentHeight: null, }); this.terminal = termWrap; return; @@ -2369,7 +2307,6 @@ class Model { sessionSettingsModal : OV = mobx.observable.box(null, {name: "sessionSettingsModal"}); clientSettingsModal : OV = mobx.observable.box(false, {name: "clientSettingsModal"}); lineSettingsModal : OV = mobx.observable.box(null, {name: "lineSettingsModal"}); - rendererPlugins : RendererPluginType[] = []; inputModel : InputModel; bookmarksModel : BookmarksModel; @@ -2423,20 +2360,6 @@ class Model { getApi().reloadWindow(); } - registerRendererPlugin(plugin : RendererPluginType) { - if (isBlank(plugin.name)) { - throw new Error("invalid plugin, no name"); - } - if (plugin.name == "terminal" || plugin.name == "none") { - throw new Error(sprintf("invalid plugin, name '%s' is reserved", plugin.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); - } - getHasClientStop() : boolean { if (this.clientData.get() == null) { return true; @@ -2448,16 +2371,6 @@ class Model { return false; } - getRendererPluginByName(name : string) : RendererPluginType { - for (let i=0; i { mobx.action(() => { this.alertMessage.set(alertMessage); @@ -3494,6 +3407,13 @@ function _getPtyDataFromUrl(url : string) : Promise { }); } +function getTermPtyData(termContext : TermContextUnion) : Promise { + if ("remoteId" in termContext) { + return getRemotePtyData(termContext.remoteId); + } + return getPtyData(termContext.screenId, termContext.cmdId, termContext.lineNum); +} + function getPtyData(screenId : string, cmdId : string, lineNum : number) : Promise { let url = sprintf(GlobalModel.getBaseHostPort() + "/api/ptyout?linenum=%d&screenid=%s&cmdid=%s", lineNum, screenId, cmdId); return _getPtyDataFromUrl(url); @@ -3514,7 +3434,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, getRendererType, getRendererContext}; +export {Model, Session, ScreenLines, GlobalModel, GlobalCommandRunner, Cmd, Screen, riToRPtr, TabColors, RemoteColors, getRendererContext, getTermPtyData}; export type {LineContainerModel}; diff --git a/src/plugins.ts b/src/plugins.ts new file mode 100644 index 00000000..c32f3ba6 --- /dev/null +++ b/src/plugins.ts @@ -0,0 +1,67 @@ +import {RendererPluginType} from "./types"; +import {SimpleImageRenderer} from "./imagerenderer"; +import {SimpleMarkdownRenderer} from "./markdownrenderer"; +import {isBlank} from "./util"; +import {sprintf} from "sprintf-js"; + +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, +}; + +let AllPlugins = [ImagePlugin, MarkdownPlugin]; + +class PluginModelClass { + rendererPlugins : RendererPluginType[] = []; + + registerRendererPlugin(plugin : RendererPluginType) { + if (isBlank(plugin.name)) { + throw new Error("invalid plugin, no name"); + } + if (plugin.name == "terminal" || plugin.name == "none") { + throw new Error(sprintf("invalid plugin, name '%s' is reserved", plugin.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 = mobx.IObservableValue; type OArr = mobx.IObservableArray; @@ -307,7 +308,7 @@ class LineSettingsModal extends React.Component<{line : LineType}, {}> { renderRendererDropdown() : any { let {line} = this.props; let renderer = this.tempRenderer.get() ?? "terminal"; - let plugins = GlobalModel.rendererPlugins; + let plugins = PluginModel.rendererPlugins; let plugin : RendererPluginType = null; return (
diff --git a/src/sh2.less b/src/sh2.less index a268ebbb..3bc80609 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -3185,7 +3185,15 @@ body.prompt-webshare #main { .logo-text { .mono-font(32px); - color: @prompt-green; + a { + color: @prompt-green; + } + } + + .screen-name { + color: white; + .mono-font(24px); + margin-left: 20px; } .download-button { @@ -3195,7 +3203,7 @@ body.prompt-webshare #main { .prompt-content { flex-grow: 1; - padding: 10px; + padding: 10px; } .prompt-footer { @@ -3218,4 +3226,9 @@ body.prompt-webshare #main { #app { color: white; } + + .lines .line-sep { + margin-left: 0px; + margin-right: 10px; + } } diff --git a/src/sh2.ts b/src/sh2.ts index 9c58f4e8..4c942c6e 100644 --- a/src/sh2.ts +++ b/src/sh2.ts @@ -6,53 +6,14 @@ 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"; +import {loadFonts} from "./util"; // @ts-ignore let VERSION = __PROMPT_VERSION__; // @ts-ignore let BUILD = __PROMPT_BUILD__; -let jbmFontNormal = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-regular.woff2')", {style: "normal", weight: "400"}); -let jbmFont200 = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-200.woff2')", {style: "normal", weight: "200"}); -let jbmFont700 = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-700.woff2')", {style: "normal", weight: "700"}); -let faFont = new FontFace("FontAwesome", "url(static/fonts/fontawesome-webfont-4.7.woff2)", {style: "normal", weight: "normal"}); -let docFonts : any = document.fonts; // work around ts typing issue -docFonts.add(jbmFontNormal); -docFonts.add(jbmFont200); -docFonts.add(jbmFont700); -docFonts.add(faFont); -jbmFontNormal.load(); -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); +loadFonts(); document.addEventListener("DOMContentLoaded", () => { let reactElem = React.createElement(Main, null, null); diff --git a/src/simplerenderer.tsx b/src/simplerenderer.tsx index ae4dba9c..1c008db9 100644 --- a/src/simplerenderer.tsx +++ b/src/simplerenderer.tsx @@ -5,7 +5,7 @@ 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 {LineContainerModel, getTermPtyData, Cmd} from "./model"; import {PtyDataBuffer} from "./ptydata"; import {debounce, throttle} from "throttle-debounce"; @@ -72,7 +72,7 @@ class SimpleBlobRendererModel { mobx.action(() => { this.loading.set(true); })(); - let rtnp = getPtyData(this.context.screenId, this.context.cmdId, this.context.lineNum); + let rtnp = getTermPtyData(this.context); rtnp.then((ptydata) => { setTimeout(() => { this.ptyData = ptydata; @@ -120,14 +120,14 @@ function apiAdapter(lcm : LineContainerModel, line : LineType, cmd : Cmd) : Rend } @mobxReact.observer -class SimpleBlobRenderer extends React.Component<{lcm : LineContainerModel, line : LineType, cmd : Cmd, plugin : RendererPluginType, onHeightChange : () => void}, {}> { +class SimpleBlobRenderer extends React.Component<{lcm : LineContainerModel, line : LineType, cmd : Cmd, rendererOpts : RendererOpts, plugin : RendererPluginType, onHeightChange : () => void}, {}> { model : SimpleBlobRendererModel; wrapperDivRef : React.RefObject = React.createRef(); rszObs : ResizeObserver; constructor(props : any) { super(props); - let {lcm, line, cmd} = this.props; + let {lcm, line, cmd, rendererOpts} = this.props; let context = contextFromLine(line); let savedHeight = lcm.getContentHeight(context); if (savedHeight == null) { @@ -142,12 +142,7 @@ class SimpleBlobRenderer extends React.Component<{lcm : LineContainerModel, line context: context, isDone: !cmd.isRunning(), savedHeight: savedHeight, - opts: { - maxSize: lcm.getMaxContentSize(), - idealSize: lcm.getIdealContentSize(), - termOpts: cmd.getTermOpts(), - termFontSize: GlobalModel.termFontSize.get(), - }, + opts: rendererOpts, api: apiAdapter(lcm, line, cmd), }; this.model = new SimpleBlobRendererModel(); diff --git a/src/term.ts b/src/term.ts index c166a320..a3f3ade5 100644 --- a/src/term.ts +++ b/src/term.ts @@ -3,9 +3,9 @@ import {Terminal} from 'xterm'; import {sprintf} from "sprintf-js"; import {boundMethod} from "autobind-decorator"; import {v4 as uuidv4} from "uuid"; -import {GlobalModel, GlobalCommandRunner, termHeightFromRows, windowWidthToCols, windowHeightToRows, getPtyData, getRemotePtyData} from "./model"; +import {termHeightFromRows, windowWidthToCols, windowHeightToRows} from "./textmeasure"; import {boundInt} from "./util"; -import type {TermOptsType, TermWinSize, RendererContext, WindowSize, PtyDataType} from "./types"; +import type {TermContextUnion, TermOptsType, TermWinSize, RendererContext, WindowSize, PtyDataType} from "./types"; type DataUpdate = { data : Uint8Array, @@ -15,12 +15,8 @@ type DataUpdate = { const MinTermCols = 10; const MaxTermCols = 1024; -type RemoteTermContext = {remoteId : string}; - -type TermContext = RendererContext | RemoteTermContext; - type TermWrapOpts = { - termContext : TermContext, + termContext : TermContextUnion, usedRows? : number, termOpts : TermOptsType, winSize : WindowSize, @@ -30,13 +26,14 @@ type TermWrapOpts = { isRunning : boolean, customKeyHandler? : (event : any, termWrap : TermWrap) => boolean, fontSize : number, - noSetTUR? : boolean, + ptyDataSource : (termContext : TermContextUnion) => Promise, + onUpdateContentHeight : (termContext : RendererContext, height : number) => void, }; // cmd-instance class TermWrap { terminal : any; - termContext : TermContext; + termContext : TermContextUnion; atRowMax : boolean; usedRows : mobx.IObservableValue; flexRows : boolean; @@ -51,7 +48,8 @@ class TermWrap { focusHandler : (focus : boolean) => void; isRunning : boolean; fontSize : number; - noSetTUR : boolean; + onUpdateContentHeight : (termContext : RendererContext, height : number) => void; + ptyDataSource : (termContext : TermContextUnion) => Promise; constructor(elem : Element, opts : TermWrapOpts) { opts = opts ?? ({} as any); @@ -62,7 +60,8 @@ class TermWrap { this.focusHandler = opts.focusHandler; this.isRunning = opts.isRunning; this.fontSize = opts.fontSize; - this.noSetTUR = !!opts.noSetTUR; + this.ptyDataSource = opts.ptyDataSource; + this.onUpdateContentHeight = opts.onUpdateContentHeight; if (this.flexRows) { this.atRowMax = false; this.usedRows = mobx.observable.box(opts.usedRows ?? (opts.isRunning ? 1 : 0), {name: "term-usedrows"}); @@ -221,8 +220,8 @@ class TermWrap { return; } this.usedRows.set(tur); - if (!this.noSetTUR) { - GlobalModel.setContentHeight(termContext, tur); + if (this.onUpdateContentHeight != null) { + this.onUpdateContentHeight(termContext, tur); } })(); } @@ -272,14 +271,7 @@ class TermWrap { } this.reloading = true; this.terminal.reset(); - let rtnp : Promise = null; - if (this.getContextRemoteId() != null) { - rtnp = getRemotePtyData(this.getContextRemoteId()); - } - else { - let termContext = this.getRendererContext(); - rtnp = getPtyData(termContext.screenId, termContext.cmdId, termContext.lineNum); - } + let rtnp = this.ptyDataSource(this.termContext); rtnp.then((ptydata) => { setTimeout(() => { this._reloadThenHandler(ptydata); diff --git a/src/textmeasure.ts b/src/textmeasure.ts index 7e6eb405..c4397d14 100644 --- a/src/textmeasure.ts +++ b/src/textmeasure.ts @@ -1,3 +1,8 @@ +import {boundInt} from "./util"; + +const MinTermCols = 10; +const MaxTermCols = 1024; + let MonoFontSizes : {height : number, width : number}[] = []; MonoFontSizes[8] = {height: 11, width: 4.797}; @@ -41,4 +46,30 @@ function measureText(text : string, textOpts? : {pre? : boolean, mono? : boolean return measureDiv.getBoundingClientRect() } -export {measureText, getMonoFontSize}; +function windowWidthToCols(width : number, fontSize : number) : number { + let dr = getMonoFontSize(fontSize); + let cols = Math.trunc((width - 50) / dr.width) - 1; + cols = boundInt(cols, MinTermCols, MaxTermCols); + return cols; +} + +function windowHeightToRows(height : number, fontSize : number) : number { + let dr = getMonoFontSize(fontSize); + let rows = Math.floor((height - 80) / dr.height) - 1; + if (rows <= 0) { + rows = 1; + } + return rows; +} + +function termWidthFromCols(cols : number, fontSize : number) : number { + let dr = getMonoFontSize(fontSize); + return Math.ceil(dr.width*cols) + 15; +} + +function termHeightFromRows(rows : number, fontSize : number) : number { + let dr = getMonoFontSize(fontSize); + return Math.ceil(dr.height*rows); +} + +export {measureText, getMonoFontSize, windowWidthToCols, windowHeightToRows, termWidthFromCols, termHeightFromRows}; diff --git a/src/types.ts b/src/types.ts index f2712c98..d104fd3f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -355,6 +355,10 @@ type RendererContext = { lineNum : number, }; +type RemoteTermContext = {remoteId : string}; + +type TermContextUnion = RendererContext | RemoteTermContext; + type RendererOpts = { maxSize : WindowSize, idealSize : WindowSize, @@ -482,4 +486,52 @@ 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, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate, ClientMigrationInfo, WebShareOpts, RemoteStatusTypeStrs}; +type WebScreen = { + screenid : string, + sharename : string, + vts : number, +}; + +type WebLine = { + screenid : string, + lineid : string, + ts : number, + linenum : number, + linetype : string, + text : string, + contentheight : number, + renderer : string, + archived : boolean, + vts : number, +}; + +type WebRemote = { + alias : string, + canonicalname : string, + name : string, +}; + +type WebCmd = { + screeid : string, + lineid : string, + remote : WebRemote, + cmdstr : string, + rawcmdstr : string, + festate : FeStateType, + termopts : TermOptsType, + status : string, + startpk : CmdStartPacketType, + doneinfo : CmdDoneInfoType, + rtnstate : boolean, + rtnstatestr : string, + vts : number, +}; + +type WebFullScreen = { + screen : WebScreen, + lines : WebLine[], + cmds : WebCmd[], + vts : number, +} + +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, ClientMigrationInfo, WebShareOpts, RemoteStatusTypeStrs, WebFullScreen, WebScreen, WebLine, WebCmd, RemoteTermContext, TermContextUnion}; diff --git a/src/util.ts b/src/util.ts index 628618ef..b1435e45 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,13 @@ import * as mobx from "mobx"; import {sprintf} from "sprintf-js"; +import dayjs from "dayjs"; +import localizedFormat from 'dayjs/plugin/localizedFormat'; + +dayjs.extend(localizedFormat) + +function isBlank(s : string) : boolean { + return (s == null || s == ""); +} function handleNotOkResp(resp : any, url : URL) : Promise { let errMsg = sprintf("Bad status code response from fetch '%s': code=%d %s", url.toString(), resp.status, resp.statusText); @@ -249,4 +257,46 @@ function incObs(inum : mobx.IObservableValue) { })(); } -export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeDataMap, genMergeSimpleData, parseEnv0, boundInt, isModKeyPress, incObs}; +function loadFonts() { + let jbmFontNormal = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-regular.woff2')", {style: "normal", weight: "400"}); + let jbmFont200 = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-200.woff2')", {style: "normal", weight: "200"}); + let jbmFont700 = new FontFace("JetBrains Mono", "url('static/fonts/jetbrains-mono-v13-latin-700.woff2')", {style: "normal", weight: "700"}); + let faFont = new FontFace("FontAwesome", "url(static/fonts/fontawesome-webfont-4.7.woff2)", {style: "normal", weight: "normal"}); + let docFonts : any = document.fonts; // work around ts typing issue + docFonts.add(jbmFontNormal); + docFonts.add(jbmFont200); + docFonts.add(jbmFont700); + docFonts.add(faFont); + jbmFontNormal.load(); + jbmFont200.load(); + jbmFont700.load(); + faFont.load(); +} + +const DOW_STRS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + +function getTodayStr() : string { + return getDateStr(new Date()); +} + +function getYesterdayStr() : string { + let d = new Date(); + d.setDate(d.getDate()-1); + return getDateStr(d); +} + +function getDateStr(d : Date) : string { + let yearStr = String(d.getFullYear()); + let monthStr = String(d.getMonth()+1); + if (monthStr.length == 1) { + monthStr = "0" + monthStr; + } + let dayStr = String(d.getDate()); + if (dayStr.length == 1) { + dayStr = "0" + dayStr; + } + let dowStr = DOW_STRS[d.getDay()]; + return dowStr + " " + yearStr + "-" + monthStr + "-" + dayStr; +} + +export {handleJsonFetchResponse, base64ToArray, genMergeData, genMergeDataMap, genMergeSimpleData, parseEnv0, boundInt, isModKeyPress, incObs, isBlank, loadFonts, getTodayStr, getYesterdayStr, getDateStr}; diff --git a/src/webshare-elems.tsx b/src/webshare-elems.tsx index 8f7018dd..0305125a 100644 --- a/src/webshare-elems.tsx +++ b/src/webshare-elems.tsx @@ -6,6 +6,282 @@ import {boundMethod} from "autobind-decorator"; import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components"; import cn from "classnames"; import {WebShareModel} from "./webshare-model"; +import * as T from "./types"; +import {isBlank} from "./util"; +import {PluginModel} from "./plugins"; +import * as lineutil from "./lineutil"; +import * as util from "./util"; + +// TODO selection +// TODO remotevars + +function makeFullRemoteRef(ownerName : string, remoteRef : string, name : string) : string { + if (isBlank(ownerName) && isBlank(name)) { + return remoteRef; + } + if (!isBlank(ownerName) && isBlank(name)) { + return ownerName + ":" + remoteRef; + } + if (isBlank(ownerName) && !isBlank(name)) { + return remoteRef + ":" + name; + } + return ownerName + ":" + remoteRef + ":" + name; +} + +function replaceHomePath(path : string, homeDir : string) : string { + if (path == homeDir) { + return "~"; + } + if (path.startsWith(homeDir + "/")) { + return "~" + path.substr(homeDir.length); + } + return path; +} + +function getCwdStr(state : FeStateType) : string { + if ((state == null || state.cwd == null) && remote != null) { + return "~"; + } + let cwd = "?"; + if (state && state.cwd) { + cwd = state.cwd; + } + // if (remote && remote.remotevars.home) { + // cwd = replaceHomePath(cwd, remote.remotevars.cwd) + // } + return cwd; +} + +function getRemoteStr(remote : WebRemote) : string { + if (remote == null) { + return "(invalid remote)"; + } + let remoteRef = (!isBlank(remote.alias) ? remote.alias : remote.canonicalname); + let fullRef = makeFullRemoteRef(null, remoteRef, remote.name); + return fullRef; +} + +@mobxReact.observer +class Prompt extends React.Component<{remote : T.WebRemote, festate : T.FeStateType}, {}> { + render() { + let {remote, festate} = this.props; + let remoteStr = getRemoteStr(remote); + let cwd = getCwdStr(festate); + let isRoot = false; + // if (remote && remote.remotevars) { + // if (remote.remotevars["sudo"] || remote.remotevars["bestuser"] == "root") { + // isRoot = true; + // } + // } + let remoteColorClass = (isRoot ? "color-red" : "color-green"); + if (remote && remote.remoteopts && remote.remoteopts.color) { + remoteColorClass = "color-" + remote.remoteopts.color; + } + let remoteTitle : string = null; + if (remote && remote.canonicalname) { + remoteTitle = remote.canonicalname; + } + return ( + [{remoteStr}] {cwd} {isRoot ? "#" : "$"} + ); + } +} + +@mobxReact.observer +class LineAvatar extends React.Component<{line : T.WebLine, cmd : T.WebCmd}, {}> { + render() { + let {line, cmd} = this.props; + let lineNumStr = String(line.linenum); + let status = (cmd != null ? cmd.status : "done"); + let rtnstate = (cmd != null ? cmd.rtnstate : false); + let isComment = (line.linetype == "text"); + return ( +
+ {lineNumStr} + + + + + + + + + +
+ ); + } +} + +@mobxReact.observer +class WebLineCmdView extends React.Component<{line : T.WebLine, cmd : T.WebCmd}, {}> { + isCmdExpanded : OV = mobx.observable.box(false, {name: "cmd-expanded"}); + isOverflow : OV = mobx.observable.box(false, {name: "line-overflow"}); + + renderSimple() { +
+ +
+ } + + renderCmdText(cmd : Cmd, remote : WebRemote) : any { + if (cmd == null) { + return ( +
+ (cmd not found) +
+ ); + } + if (this.isCmdExpanded.get()) { + return ( + +
+
+ +
+
+
+
{getFullCmdText(cmd.cmdstr)}
+
+
+ ); + } + let isMultiLine = lineutil.isMultiLineCmdText(cmd.cmdstr); + return ( +
+
+ + + {lineutil.getSingleLineCmdText(cmd.cmdstr)} +
+ +
...▼
+
+
+ ); + } + + renderMetaWrap() { + let {line, cmd} = this.props; + let formattedTime = lineutil.getLineDateTimeStr(line.ts); + let termOpts = cmd.termopts; + let remote = cmd.remote; + let renderer = line.renderer; + return ( +
+
+
{formattedTime}
+
 
+ +
{renderer} 
+
+
+ ({termOpts.rows}x{termOpts.cols}) +
+
+ {this.renderCmdText(cmd, remote)} +
+ ); + } + + render() { + let {line, cmd} = this.props; + let model = WebShareModel; + let isSelected = mobx.computed(() => (model.getSelectedLine() == line.linenum), {name: "computed-isSelected"}).get(); + let rendererPlugin : RendererPluginType = null; + let isNoneRenderer = (line.renderer == "none"); + if (!isBlank(line.renderer) && line.renderer != "terminal" && !isNoneRenderer) { + rendererPlugin = PluginModel.getRendererPluginByName(line.renderer); + } + let rendererType = lineutil.getRendererType(line); + return ( +
+
+
+ + {this.renderMetaWrap()} +
+
+ ); + } +} + +@mobxReact.observer +class WebLineTextView extends React.Component<{line : T.WebLine, cmd : T.WebCmd}, {}> { + render() { + let {line} = this.props; + let isSelected = mobx.computed(() => (model.getSelectedLine() == line.linenum), {name: "computed-isSelected"}).get(); + return ( +
+
+
+ +
+
+
{line.text}
+
+
+ ); + } +} + +@mobxReact.observer +class WebLineView extends React.Component<{line : T.WebLine, cmd : T.WebCmd}, {}> { + render() { + let {line} = this.props; + if (line.linetype == "text") { + return + } + if (line.linetype == "cmd") { + return + } + return ( +
invalid linetype "{line.linetype}"
+ ); + } +} + +@mobxReact.observer +class WebScreenView extends React.Component<{screen : T.WebFullScreen}, {}> { + render() { + let {screen} = this.props; + let lines = screen.lines ?? []; + let cmds = screen.cmds ?? []; + let cmdMap : Record = {}; + for (let i=0; i{dateSepStr}
+ lineElements.push(sepElem); + } + let topBorder = (dateSepStr == null) && (idx != 0); + let lineElem = ; + lineElements.push(lineElem); + } + return ( +
+
+
+ {lineElements} +
+
+ ); + } +} @mobxReact.observer class WebShareMain extends React.Component<{}, {}> { @@ -14,10 +290,17 @@ class WebShareMain extends React.Component<{}, {}> { } render() { + let screen = WebShareModel.screen.get(); + let errMessage = WebShareModel.errMessage.get(); return (
-
[prompt]
+
+ [prompt] +
+ +
{screen.screen.sharename}
+
-
screenid={WebShareModel.screenId}, viewkey={WebShareModel.viewKey}
-
{WebShareModel.errMessage.get()}
+ + + + +
{WebShareModel.errMessage.get()}
+
{this.renderCopy()} diff --git a/src/webshare-model.ts b/src/webshare-model.ts index ce9a2f27..02d3c72d 100644 --- a/src/webshare-model.ts +++ b/src/webshare-model.ts @@ -1,6 +1,7 @@ import * as mobx from "mobx"; import {boundMethod} from "autobind-decorator"; import {handleJsonFetchResponse} from "./util"; +import * as T from "./types"; type OV = mobx.IObservableValue; type OArr = mobx.IObservableArray; @@ -19,6 +20,7 @@ class WebShareModelClass { viewKey : string; screenId : string; errMessage : OV = mobx.observable.box(null, {name: "errMessage"}); + screen : OV = mobx.observable.box(null, {name: "webScreen"}); constructor() { let urlParams = new URLSearchParams(window.location.search); @@ -34,6 +36,10 @@ class WebShareModelClass { })(); } + getSelectedLine() : number { + return 10; + } + loadFullScreenData() : void { if (isBlank(this.screenId)) { this.setErrMessage("No ScreenId Specified, Cannot Load."); @@ -46,7 +52,7 @@ class WebShareModelClass { let usp = new URLSearchParams({screenid: this.screenId, viewkey: this.viewKey}); 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) => { - console.log("got data", data); + mobx.action(() => this.screen.set(data))(); }).catch((err) => { this.errMessage.set("Cannot get screen: " + err.message); }); diff --git a/src/webshare.ts b/src/webshare.ts index 2cd45ce7..42dbf3e7 100644 --- a/src/webshare.ts +++ b/src/webshare.ts @@ -3,12 +3,24 @@ import * as React from "react"; import {createRoot} from 'react-dom/client'; import {sprintf} from "sprintf-js"; import {WebShareMain} from "./webshare-elems"; +import {loadFonts} from "./util"; +import {WebShareModel} from "./webshare-model"; + +loadFonts(); document.addEventListener("DOMContentLoaded", () => { let elem = document.getElementById("app"); let root = createRoot(elem); let reactElem = React.createElement(WebShareMain, null, null); - root.render(reactElem); + let isFontLoaded = document.fonts.check("12px 'JetBrains Mono'"); + if (isFontLoaded) { + root.render(reactElem); + } + else { + document.fonts.ready.then(() => { + root.render(reactElem); + }); + } }); (window as any).mobx = mobx; diff --git a/webshare/static/fonts/fontawesome-webfont-4.7.woff2 b/webshare/static/fonts/fontawesome-webfont-4.7.woff2 new file mode 100644 index 00000000..4d13fc60 Binary files /dev/null and b/webshare/static/fonts/fontawesome-webfont-4.7.woff2 differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff b/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff new file mode 100644 index 00000000..2b8eeac6 Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff2 b/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff2 new file mode 100644 index 00000000..d23f9b05 Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-200.woff2 differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff b/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff new file mode 100644 index 00000000..78af4538 Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff2 b/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff2 new file mode 100644 index 00000000..5737b146 Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-700.woff2 differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff b/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff new file mode 100644 index 00000000..f80359f7 Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff differ diff --git a/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff2 b/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff2 new file mode 100644 index 00000000..de8b746b Binary files /dev/null and b/webshare/static/fonts/jetbrains-mono-v13-latin-regular.woff2 differ