diff --git a/src/app/line/renderer/basicrenderer.tsx b/src/app/line/renderer/basicrenderer.tsx deleted file mode 100644 index 2862b0a12..000000000 --- a/src/app/line/renderer/basicrenderer.tsx +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2023, Command Line Inc. -// SPDX-License-Identifier: Apache-2.0 - -import * as React from "react"; -import * as mobxReact from "mobx-react"; -import * as mobx from "mobx"; - -import { debounce } from "throttle-debounce"; -import * as util from "@/util/util"; -import { GlobalModel } from "@/models"; - -class SimpleBlobRendererModel { - context: RendererContext; - opts: RendererOpts; - isDone: OV; - api: RendererModelContainerApi; - savedHeight: number; - loading: OV; - loadError: OV = mobx.observable.box(null, { - name: "renderer-loadError", - }); - lineState: LineStateType; - ptyData: PtyDataType; - ptyDataSource: (termContext: TermContextUnion) => Promise; - dataBlob: Blob; - readOnly: boolean; - notFound: boolean; - - 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.lineState = params.lineState; - this.savedHeight = params.savedHeight; - this.ptyDataSource = params.ptyDataSource; - if (this.isDone.get()) { - setTimeout(() => this.reload(0), 10); - } - } - - 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); - })(); - if (delayMs == 0) { - this.reload_noDelay(); - } else { - setTimeout(() => { - this.reload_noDelay(); - }, delayMs); - } - } - - reload_noDelay(): void { - let source = this.lineState["prompt:source"] || "pty"; - if (source == "pty") { - this.reloadPtyData(); - } else if (source == "file") { - this.reloadFileData(); - } else { - mobx.action(() => { - this.loadError.set("error: invalid load source: " + source); - })(); - } - } - - reloadFileData(): void { - // todo add file methods to API, so we don't have a GlobalModel dependency here! - let path = this.lineState["prompt:file"]; - if (util.isBlank(path)) { - mobx.action(() => { - this.loadError.set("renderer has file source, but no prompt:file specified"); - })(); - return; - } - let rtnp = GlobalModel.readRemoteFile(this.context.screenId, this.context.lineId, path); - rtnp.then((file) => { - this.notFound = (file as any).notFound; - this.readOnly = (file as any).readOnly; - this.dataBlob = file; - mobx.action(() => { - this.loading.set(false); - this.loadError.set(null); - })(); - }).catch((e) => { - mobx.action(() => { - this.loadError.set("error loading file data: " + e); - })(); - }); - } - - reloadPtyData(): void { - this.readOnly = true; - let rtnp = this.ptyDataSource(this.context); - if (rtnp == null) { - console.log("no promise returned from ptyDataSource (simplerenderer)", this.context); - return; - } - rtnp.then((ptydata) => { - this.ptyData = ptydata; - this.dataBlob = new Blob([this.ptyData.data]); - mobx.action(() => { - this.loading.set(false); - this.loadError.set(null); - })(); - }).catch((e) => { - mobx.action(() => { - this.loadError.set("error loading data: " + e); - })(); - }); - } - - receiveData(pos: number, data: Uint8Array, reason?: string): void { - // this.dataBuf.receiveData(pos, data, reason); - } -} - -@mobxReact.observer -class SimpleBlobRenderer extends React.Component< - { - rendererContainer: RendererContainerType; - lineId: string; - plugin: RendererPluginType; - onHeightChange: () => void; - initParams: RendererModelInitializeParams; - scrollToBringIntoViewport: () => void; - isSelected: boolean; - shouldFocus: boolean; - }, - {} -> { - model: SimpleBlobRendererModel; - wrapperDivRef: React.RefObject = React.createRef(); - rszObs: ResizeObserver; - updateHeight_debounced: (newHeight: number) => void; - - constructor(props: any) { - super(props); - let { rendererContainer, lineId, plugin, initParams } = this.props; - this.model = new SimpleBlobRendererModel(); - this.model.initialize(initParams); - rendererContainer.registerRenderer(lineId, this.model); - this.updateHeight_debounced = debounce(1000, this.updateHeight.bind(this)); - } - - updateHeight(newHeight: number): void { - this.model.updateHeight(newHeight); - } - - handleResize(entries: ResizeObserverEntry[]): void { - if (this.model.loading.get()) { - return; - } - if (this.props.onHeightChange) { - this.props.onHeightChange(); - } - if (!this.model.loading.get() && this.wrapperDivRef.current != null) { - let height = this.wrapperDivRef.current.offsetHeight; - this.updateHeight_debounced(height); - } - } - - checkRszObs() { - if (this.rszObs != null) { - return; - } - if (this.wrapperDivRef.current == null) { - return; - } - this.rszObs = new ResizeObserver(this.handleResize.bind(this)); - this.rszObs.observe(this.wrapperDivRef.current); - } - - componentDidMount() { - this.checkRszObs(); - } - - componentWillUnmount() { - let { rendererContainer, lineId } = this.props; - rendererContainer.unloadRenderer(lineId); - if (this.rszObs != null) { - this.rszObs.disconnect(); - this.rszObs = null; - } - } - - componentDidUpdate() { - this.checkRszObs(); - } - - render() { - let { plugin } = this.props; - let model = this.model; - if (model.loadError.get() != null) { - let errorText = model.loadError.get(); - let height = this.model.savedHeight; - return ( -
-
ERROR: {errorText}
-
- ); - } - if (model.loading.get()) { - let height = this.model.savedHeight; - return ( -
- loading content -
- ); - } - let Comp = plugin.simpleComponent; - if (Comp == null) { -
(no component found in plugin)
; - } - let { festate, cmdstr, exitcode } = this.props.initParams.rawCmd; - return ( -
- -
- ); - } -} - -export { SimpleBlobRendererModel, SimpleBlobRenderer }; diff --git a/src/electron/emain.ts b/src/electron/emain.ts index be4f429f6..2b6114775 100644 --- a/src/electron/emain.ts +++ b/src/electron/emain.ts @@ -308,16 +308,21 @@ function shFrameNavHandler(event: Electron.Event { - const urlParams = { + readRemoteFile(screenId: string, lineId: string, path: string, mimetype?: string): Promise { + const urlParams: Record = { screenid: screenId, lineid: lineId, path: path, }; + if (mimetype != null) { + urlParams["mimetype"] = mimetype; + } const usp = new URLSearchParams(urlParams); const url = new URL(this.getBaseHostPort() + "/api/read-file?" + usp.toString()); const fetchHeaders = this.getFetchHeaders(); diff --git a/src/plugins/pdf/pdf.less b/src/plugins/pdf/pdf.less new file mode 100644 index 000000000..4f58b90f7 --- /dev/null +++ b/src/plugins/pdf/pdf.less @@ -0,0 +1,7 @@ +.pdf-renderer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding-top: var(--termpad); +} diff --git a/src/plugins/pdf/pdf.tsx b/src/plugins/pdf/pdf.tsx new file mode 100644 index 000000000..dda6208e9 --- /dev/null +++ b/src/plugins/pdf/pdf.tsx @@ -0,0 +1,49 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import * as React from "react"; +import * as mobx from "mobx"; +import * as mobxReact from "mobx-react"; + +import "./pdf.less"; + +@mobxReact.observer +class SimplePdfRenderer extends React.Component< + { data: ExtBlob; context: RendererContext; opts: RendererOpts; savedHeight: number }, + {} +> { + objUrl: string = null; + + componentWillUnmount() { + if (this.objUrl != null) { + URL.revokeObjectURL(this.objUrl); + } + } + + render() { + let dataBlob = this.props.data; + if (dataBlob == null || dataBlob.notFound) { + return ( +
+
+ ERROR: file {dataBlob && dataBlob.name ? JSON.stringify(dataBlob.name) : ""} not found +
+
+ ); + } + if (this.objUrl == null) { + const pdfBlob = new File([dataBlob], "test.pdf", { type: "application/pdf" }); + this.objUrl = URL.createObjectURL(pdfBlob); + } + const opts = this.props.opts; + const maxHeight = opts.maxSize.height - 10; + const maxWidth = opts.maxSize.width - 10; + return ( +
+