restore terminal state when loading term view

This commit is contained in:
sawka 2024-05-29 00:28:25 -07:00
parent 30ef011338
commit 02cda396e8
3 changed files with 66 additions and 2 deletions

View File

@ -40,14 +40,21 @@ function getThemeFromCSSVars(el: Element): ITheme {
return theme;
}
type InitialLoadDataType = {
loaded: boolean;
heldData: Uint8Array[];
};
const TerminalView = ({ blockId }: { blockId: string }) => {
const connectElemRef = React.useRef<HTMLDivElement>(null);
const termRef = React.useRef<Terminal>(null);
const initialLoadRef = React.useRef<InitialLoadDataType>({ loaded: false, heldData: [] });
React.useEffect(() => {
if (!connectElemRef.current) {
return;
}
console.log("terminal created");
const term = new Terminal({
theme: getThemeFromCSSVars(connectElemRef.current),
fontSize: 12,
@ -89,7 +96,11 @@ const TerminalView = ({ blockId }: { blockId: string }) => {
blockSubject.subscribe((data) => {
// base64 decode
const decodedData = base64ToArray(data.ptydata);
if (initialLoadRef.current.loaded) {
term.write(decodedData);
} else {
initialLoadRef.current.heldData.push(decodedData);
}
});
return () => {
@ -99,6 +110,39 @@ const TerminalView = ({ blockId }: { blockId: string }) => {
};
}, [connectElemRef.current]);
React.useEffect(() => {
if (!termRef.current) {
return;
}
// load data from blockfile
const startTs = Date.now();
let loadedBytes = 0;
const localTerm = termRef.current; // avoids devmode double effect running issue (terminal gets created twice)
const usp = new URLSearchParams();
usp.set("blockid", blockId);
usp.set("name", "main");
fetch("/wave/blockfile?" + usp.toString())
.then((resp) => {
if (resp.ok) {
return resp.arrayBuffer();
}
console.log("error loading blockfile", resp.status, resp.statusText);
})
.then((data: ArrayBuffer) => {
const uint8View = new Uint8Array(data);
localTerm.write(uint8View);
loadedBytes = uint8View.byteLength;
})
.finally(() => {
initialLoadRef.current.heldData.forEach((data) => {
localTerm.write(data);
});
initialLoadRef.current.loaded = true;
initialLoadRef.current.heldData = [];
console.log(`terminal loaded blockfile ${loadedBytes} bytes, ${Date.now() - startTs}ms`);
});
}, [termRef.current]);
return (
<div className="view-term">
<div key="conntectElem" className="term-connectelem" ref={connectElemRef}></div>

View File

@ -4,6 +4,7 @@
package blockcontroller
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
@ -108,6 +109,21 @@ func (bc *BlockController) handleShellProcData(data []byte, seqNum int) error {
return nil
}
func (bc *BlockController) resetTerminalState() {
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
var buf bytes.Buffer
// buf.WriteString("\x1b[?1049l") // disable alternative buffer
buf.WriteString("\x1b[0m") // reset attributes
buf.WriteString("\x1b[?25h") // show cursor
buf.WriteString("\x1b[?1000l") // disable mouse tracking
buf.WriteString("\r\n\r\n(restored terminal state)\r\n\r\n")
err := blockstore.GBS.AppendData(ctx, bc.BlockId, "main", buf.Bytes())
if err != nil {
log.Printf("error appending to blockfile (terminal reset): %v\n", err)
}
}
func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts) error {
// create a circular blockfile for the output
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
@ -116,6 +132,10 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts) error {
if err != nil && err != blockstore.ErrAlreadyExists {
return fmt.Errorf("error creating blockfile: %w", err)
}
if err == blockstore.ErrAlreadyExists {
// reset the terminal state
bc.resetTerminalState()
}
if bc.getShellProc() != nil {
return nil
}

View File

@ -118,7 +118,7 @@ func (svc *ObjectService) SetActiveTab(uiContext wstore.UIContext, tabId string)
blockErr := blockcontroller.StartBlockController(ctx, blockId)
if blockErr != nil {
// we don't want to fail the set active tab operation if a block controller fails to start
log.Printf("error starting block controller (blockid:%s): %w", blockId, blockErr)
log.Printf("error starting block controller (blockid:%s): %v", blockId, blockErr)
continue
}
}