diff --git a/src/prompt.less b/src/prompt.less index 0d150046..16576e1a 100644 --- a/src/prompt.less +++ b/src/prompt.less @@ -276,13 +276,12 @@ input[type="checkbox"] { background-color: #aaaea7; cursor: default !important; } - .error { - background-color: red; + .message { color: white; border-radius: 6px; margin-bottom: 1rem; padding: 4px 1rem; - max-width: 16rem; + max-width: 80vw; } } diff --git a/src/simplerenderer.tsx b/src/simplerenderer.tsx index 8e64e796..fa1cba20 100644 --- a/src/simplerenderer.tsx +++ b/src/simplerenderer.tsx @@ -269,6 +269,7 @@ class SimpleBlobRenderer extends React.Component< cmdstr={cmdstr} exitcode={exitcode} data={simpleModel.dataBlob} + readOnly={simpleModel.readOnly} lineState={simpleModel.lineState} context={simpleModel.context} opts={simpleModel.opts} diff --git a/src/view/code.tsx b/src/view/code.tsx index 42eb01bb..b387c235 100644 --- a/src/view/code.tsx +++ b/src/view/code.tsx @@ -6,9 +6,10 @@ import { GlobalModel } from "../model"; class SourceCodeRenderer extends React.Component< { data: Blob; - cmdstr: String; - cwd: String; - exitcode: Number; + cmdstr: string; + cwd: string; + readOnly: boolean; + exitcode: number; context: RendererContext; opts: RendererOpts; savedHeight: number; @@ -18,16 +19,14 @@ class SourceCodeRenderer extends React.Component< {} > { /** - * codeCache is a Hashmap with key=filepath and value=code + * codeCache is a Hashmap with key=screenId:lineId:filepath and value=code * Editor should never read the code directly from the filesystem. it should read from the cache. - * Upon loading a file (props.data contains the file-contents) FOR THE FIRST TIME, - * we will put it in the cache, and will update the contents of the cache upon every onChange(). - * ALl this is to ensure that the file contents doesnt get reloaded when the line scrolls out of the viewport - * (and hence the react component gets destroyed) */ static codeCache = new Map(); filePath; + cacheKey; + originalData; constructor(props) { super(props); this.editorRef = React.createRef(); @@ -39,26 +38,28 @@ class SourceCodeRenderer extends React.Component< isFullWindow: false, isSave: false, editorHeight: props.savedHeight, - errorMessage: null, + message: null, }; } componentDidMount(): void { - // DANGEROUS ... I AM ASSUMING THE COMMAND IS IN FORMAT cat prompt_samples/sample.java - // filePath should be saved in the new lineOpts field that Mike is working on :) - this.filePath = `${this.props.cwd}/${this.props.cmdstr.split(" ")[1]}`; - const code = SourceCodeRenderer.codeCache.get(this.filePath); + this.filePath = this.props.lineState["prompt:file"]; + const { screenId, lineId } = this.props.context; + this.cacheKey = `${screenId}-${lineId}-${this.filePath}`; + const code = SourceCodeRenderer.codeCache.get(this.cacheKey); if (code) { this.setState({ code }); } else this.props.data.text().then((code) => { + this.originalData = code; this.setState({ code }); - SourceCodeRenderer.codeCache.set(this.filePath, code); + SourceCodeRenderer.codeCache.set(this.cacheKey, code); }); } handleEditorDidMount = (editor, monaco) => { - const extension = this.props.cmdstr.match(/(?:[^\\\/:*?"<>|\r\n]+\.)([a-zA-Z0-9]+)\b/)?.[1] || ""; + const extension = + (this.filePath && this.filePath.match(/(?:[^\\\/:*?"<>|\r\n]+\.)([a-zA-Z0-9]+)\b/)?.[1]) || ""; const detectedLanguage = monaco.languages .getLanguages() .find((lang) => lang.extensions?.includes("." + extension)); @@ -73,6 +74,12 @@ class SourceCodeRenderer extends React.Component< } } this.setEditorHeight(); + editor.onKeyDown((e) => { + if (e.code === "KeyS" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.doSave(); + } + }); }; handleLanguageChange = (event) => { @@ -95,17 +102,27 @@ class SourceCodeRenderer extends React.Component< }; doSave = () => { - // call the function that would save the file to filesystem. would likely be async - // as a result of the save operation, the entire component should get reloaded (** HOW **) - // once its reloaded, this.props.data.text() should contain the latest code - // in which case, the cache will get refilled and we can consider the transaction "committed" - this.setState({ errorMessage: "File could not be saved" }); - setTimeout(() => this.setState({ errorMessage: null }), 3000); + const { screenId, lineId } = this.props.context; + const encodedCode = new TextEncoder().encode(this.state.code); + debugger; + GlobalModel.writeRemoteFile(screenId, lineId, this.filePath, encodedCode, { useTemp: true }) + .then(() => { + this.originalData = this.state.code; + this.setState({ + isSave: false, + message: { status: "success", text: `Saved to ${this.props.cwd}/${this.filePath}` }, + }); + setTimeout(() => this.setState({ message: null }), 3000); + }) + .catch((e) => { + this.setState({ message: { status: "error", text: e.message } }); + setTimeout(() => this.setState({ message: null }), 3000); + }); }; handleEditorChange = (code) => { this.setState({ isFullWindow: true, code }); - SourceCodeRenderer.codeCache.set(this.filePath, code); + SourceCodeRenderer.codeCache.set(this.cacheKey, code); this.setEditorHeight(); setTimeout(() => this.props.scrollToBringIntoViewport(), 350); this.props.data.text().then((originalCode) => this.setState({ isSave: code !== originalCode })); @@ -155,7 +172,17 @@ class SourceCodeRenderer extends React.Component< scrollBeyondLastLine: false, fontSize: GlobalModel.termFontSize.get(), fontFamily: "JetBrains Mono", - readOnly: this.props.opts.readOnly, + readOnly: this.props.readOnly, + keybindings: [ + { + key: "ctrl+s", + command: "-editor.action.filesave", + }, + { + key: "cmd+s", + command: "-editor.action.filesave", + }, + ], }} onChange={this.handleEditorChange} /> @@ -189,13 +216,17 @@ class SourceCodeRenderer extends React.Component< )} - {this.state.errorMessage && ( + {this.state.message && (