mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
restore terminal state when loading term view
This commit is contained in:
parent
30ef011338
commit
02cda396e8
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user