mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
implement wscommand using type union interface, send resize events there
This commit is contained in:
parent
083e00227e
commit
8a71180f20
@ -157,4 +157,19 @@ function initWS() {
|
||||
globalWS.connectNow("initWS");
|
||||
}
|
||||
|
||||
export { WOS, atoms, getBackendHostPort, getORefSubject, globalStore, globalWS, initWS, useBlockAtom, useBlockCache };
|
||||
function sendWSCommand(command: WSCommandType) {
|
||||
globalWS.pushMessage(command);
|
||||
}
|
||||
|
||||
export {
|
||||
WOS,
|
||||
atoms,
|
||||
getBackendHostPort,
|
||||
getORefSubject,
|
||||
globalStore,
|
||||
globalWS,
|
||||
initWS,
|
||||
sendWSCommand,
|
||||
useBlockAtom,
|
||||
useBlockCache,
|
||||
};
|
||||
|
@ -283,21 +283,6 @@ function cleanWaveObjectCache() {
|
||||
}
|
||||
}
|
||||
|
||||
// Events.On("waveobj:update", (event: any) => {
|
||||
// const data: WaveObjUpdate[] = event?.data;
|
||||
// if (data == null) {
|
||||
// return;
|
||||
// }
|
||||
// if (!Array.isArray(data)) {
|
||||
// console.log("invalid waveobj:update, not an array", data);
|
||||
// return;
|
||||
// }
|
||||
// if (data.length == 0) {
|
||||
// return;
|
||||
// }
|
||||
// updateWaveObjects(data);
|
||||
// });
|
||||
|
||||
// gets the value of a WaveObject from the cache.
|
||||
// should provide getFn if it is available (e.g. inside of a jotai atom)
|
||||
// otherwise it will use the globalStore.get function
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { getBackendHostPort, getORefSubject, WOS } from "@/store/global";
|
||||
import { WOS, getBackendHostPort, getORefSubject, sendWSCommand } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import { base64ToArray } from "@/util/util";
|
||||
import { FitAddon } from "@xterm/addon-fit";
|
||||
@ -49,11 +49,12 @@ function handleResize(fitAddon: FitAddon, blockId: string, term: Terminal) {
|
||||
const oldCols = term.cols;
|
||||
fitAddon.fit();
|
||||
if (oldRows !== term.rows || oldCols !== term.cols) {
|
||||
const resizeCommand: BlockInputCommand = {
|
||||
command: "controller:input",
|
||||
const wsCommand: SetBlockTermSizeWSCommand = {
|
||||
wscommand: "setblocktermsize",
|
||||
blockid: blockId,
|
||||
termsize: { rows: term.rows, cols: term.cols },
|
||||
};
|
||||
services.BlockService.SendCommand(blockId, resizeCommand);
|
||||
sendWSCommand(wsCommand);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,8 +82,13 @@ const TerminalView = ({ blockId }: { blockId: string }) => {
|
||||
newTerm.loadAddon(newFitAddon);
|
||||
newTerm.open(connectElemRef.current);
|
||||
newFitAddon.fit();
|
||||
services.BlockService.SendCommand(blockId, {
|
||||
command: "controller:input",
|
||||
// services.BlockService.SendCommand(blockId, {
|
||||
// command: "controller:input",
|
||||
// termsize: { rows: newTerm.rows, cols: newTerm.cols },
|
||||
// });
|
||||
sendWSCommand({
|
||||
wscommand: "setblocktermsize",
|
||||
blockid: blockId,
|
||||
termsize: { rows: newTerm.rows, cols: newTerm.cols },
|
||||
});
|
||||
newTerm.onData((data) => {
|
||||
|
19
frontend/types/gotypes.d.ts
vendored
19
frontend/types/gotypes.d.ts
vendored
@ -16,7 +16,7 @@ declare global {
|
||||
|
||||
type BlockCommand = {
|
||||
command: string;
|
||||
} & ( BlockMessageCommand | BlockInputCommand | BlockSetViewCommand | BlockSetMetaCommand );
|
||||
} & ( BlockInputCommand | BlockSetViewCommand | BlockSetMetaCommand );
|
||||
|
||||
// wstore.BlockDef
|
||||
type BlockDef = {
|
||||
@ -34,12 +34,6 @@ declare global {
|
||||
termsize?: TermSize;
|
||||
};
|
||||
|
||||
// blockcontroller.BlockMessageCommand
|
||||
type BlockMessageCommand = {
|
||||
command: "message";
|
||||
message: string;
|
||||
};
|
||||
|
||||
// blockcontroller.BlockSetMetaCommand
|
||||
type BlockSetMetaCommand = {
|
||||
command: "setmeta";
|
||||
@ -117,6 +111,13 @@ declare global {
|
||||
winsize?: WinSize;
|
||||
};
|
||||
|
||||
// webcmd.SetBlockTermSizeWSCommand
|
||||
type SetBlockTermSizeWSCommand = {
|
||||
wscommand: "setblocktermsize";
|
||||
blockid: string;
|
||||
termsize: TermSize;
|
||||
};
|
||||
|
||||
// wstore.Tab
|
||||
type Tab = WaveObj & {
|
||||
name: string;
|
||||
@ -137,6 +138,10 @@ declare global {
|
||||
activetabid: string;
|
||||
};
|
||||
|
||||
type WSCommandType = {
|
||||
wscommand: string;
|
||||
} & ( SetBlockTermSizeWSCommand );
|
||||
|
||||
// eventbus.WSEventType
|
||||
type WSEventType = {
|
||||
eventtype: string;
|
||||
|
@ -22,7 +22,6 @@ const (
|
||||
)
|
||||
|
||||
var CommandToTypeMap = map[string]reflect.Type{
|
||||
BlockCommand_Message: reflect.TypeOf(BlockMessageCommand{}),
|
||||
BlockCommand_Input: reflect.TypeOf(BlockInputCommand{}),
|
||||
BlockCommand_SetView: reflect.TypeOf(BlockSetViewCommand{}),
|
||||
BlockCommand_SetMeta: reflect.TypeOf(BlockSetMetaCommand{}),
|
||||
@ -33,7 +32,6 @@ func CommandTypeUnionMeta() tsgenmeta.TypeUnionMeta {
|
||||
BaseType: reflect.TypeOf((*BlockCommand)(nil)).Elem(),
|
||||
TypeFieldName: "command",
|
||||
Types: []reflect.Type{
|
||||
reflect.TypeOf(BlockMessageCommand{}),
|
||||
reflect.TypeOf(BlockInputCommand{}),
|
||||
reflect.TypeOf(BlockSetViewCommand{}),
|
||||
reflect.TypeOf(BlockSetMetaCommand{}),
|
||||
@ -70,15 +68,6 @@ func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) {
|
||||
return cmd.(BlockCommand), nil
|
||||
}
|
||||
|
||||
type BlockMessageCommand struct {
|
||||
Command string `json:"command" tstype:"\"message\""`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (mc *BlockMessageCommand) GetCommand() string {
|
||||
return BlockCommand_Message
|
||||
}
|
||||
|
||||
type BlockInputCommand struct {
|
||||
Command string `json:"command" tstype:"\"controller:input\""`
|
||||
InputData64 string `json:"inputdata64,omitempty"`
|
||||
|
@ -293,9 +293,6 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) error {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
||||
defer cancelFn()
|
||||
switch cmd := cmdGen.(type) {
|
||||
case *BlockMessageCommand:
|
||||
log.Printf("MESSAGE: %s | %q\n", blockId, cmd.Message)
|
||||
return nil
|
||||
case *BlockSetViewCommand:
|
||||
log.Printf("SETVIEW: %s | %q\n", blockId, cmd.View)
|
||||
block, err := wstore.DBGet[*wstore.Block](ctx, blockId)
|
||||
|
@ -16,6 +16,8 @@ type BlockService struct{}
|
||||
|
||||
const DefaultTimeout = 2 * time.Second
|
||||
|
||||
var BlockServiceInstance = &BlockService{}
|
||||
|
||||
func (bs *BlockService) SendCommand_Meta() tsgenmeta.MethodMeta {
|
||||
return tsgenmeta.MethodMeta{
|
||||
Desc: "send command to block",
|
||||
|
@ -16,11 +16,12 @@ import (
|
||||
"github.com/wavetermdev/thenextwave/pkg/service/objectservice"
|
||||
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
|
||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||
)
|
||||
|
||||
var ServiceMap = map[string]any{
|
||||
"block": &blockservice.BlockService{},
|
||||
"block": blockservice.BlockServiceInstance,
|
||||
"object": &objectservice.ObjectService{},
|
||||
"file": &fileservice.FileService{},
|
||||
"client": &clientservice.ClientService{},
|
||||
@ -36,6 +37,7 @@ var methodMetaRType = reflect.TypeOf(tsgenmeta.MethodMeta{})
|
||||
var waveObjUpdateRType = reflect.TypeOf(wstore.WaveObjUpdate{})
|
||||
var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
|
||||
var blockCommandRType = reflect.TypeOf((*blockcontroller.BlockCommand)(nil)).Elem()
|
||||
var wsCommandRType = reflect.TypeOf((*webcmd.WSCommandType)(nil)).Elem()
|
||||
|
||||
type WebCallType struct {
|
||||
Service string `json:"service"`
|
||||
@ -91,7 +93,7 @@ func convertComplex(argType reflect.Type, jsonArg any) (any, error) {
|
||||
}
|
||||
|
||||
func isSpecialWaveArgType(argType reflect.Type) bool {
|
||||
return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType || argType == blockCommandRType
|
||||
return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType || argType == blockCommandRType || argType == wsCommandRType
|
||||
}
|
||||
|
||||
func convertBlockCommand(argType reflect.Type, jsonArg any) (any, error) {
|
||||
@ -105,10 +107,23 @@ func convertBlockCommand(argType reflect.Type, jsonArg any) (any, error) {
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func convertWSCommand(argType reflect.Type, jsonArg any) (any, error) {
|
||||
if _, ok := jsonArg.(map[string]any); !ok {
|
||||
return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
|
||||
}
|
||||
cmd, err := webcmd.ParseWSCommandMap(jsonArg.(map[string]any))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing command map: %w", err)
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func convertSpecial(argType reflect.Type, jsonArg any) (any, error) {
|
||||
jsonType := reflect.TypeOf(jsonArg)
|
||||
if argType == blockCommandRType {
|
||||
return convertBlockCommand(argType, jsonArg)
|
||||
} else if argType == wsCommandRType {
|
||||
return convertWSCommand(argType, jsonArg)
|
||||
} else if argType == waveObjRType {
|
||||
if jsonType.Kind() != reflect.Map {
|
||||
return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/wavetermdev/thenextwave/pkg/service"
|
||||
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
|
||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||
)
|
||||
|
||||
@ -354,6 +355,7 @@ func GenerateServiceClass(serviceName string, serviceObj any, tsTypesMap map[ref
|
||||
|
||||
func GenerateWaveObjTypes(tsTypesMap map[reflect.Type]string) {
|
||||
GenerateTSTypeUnion(blockcontroller.CommandTypeUnionMeta(), tsTypesMap)
|
||||
GenerateTSTypeUnion(webcmd.WSCommandTypeUnionMeta(), tsTypesMap)
|
||||
GenerateTSType(reflect.TypeOf(waveobj.ORef{}), tsTypesMap)
|
||||
GenerateTSType(reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem(), tsTypesMap)
|
||||
GenerateTSType(reflect.TypeOf(map[string]any{}), tsTypesMap)
|
||||
|
60
pkg/web/webcmd/webcmd.go
Normal file
60
pkg/web/webcmd/webcmd.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package webcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/wavetermdev/thenextwave/pkg/shellexec"
|
||||
"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
|
||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||
)
|
||||
|
||||
const (
|
||||
WSCommand_SetBlockTermSize = "setblocktermsize"
|
||||
)
|
||||
|
||||
type WSCommandType interface {
|
||||
GetWSCommand() string
|
||||
}
|
||||
|
||||
func WSCommandTypeUnionMeta() tsgenmeta.TypeUnionMeta {
|
||||
return tsgenmeta.TypeUnionMeta{
|
||||
BaseType: reflect.TypeOf((*WSCommandType)(nil)).Elem(),
|
||||
TypeFieldName: "wscommand",
|
||||
Types: []reflect.Type{
|
||||
reflect.TypeOf(SetBlockTermSizeWSCommand{}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type SetBlockTermSizeWSCommand struct {
|
||||
WSCommand string `json:"wscommand" tstype:"\"setblocktermsize\""`
|
||||
BlockId string `json:"blockid"`
|
||||
TermSize shellexec.TermSize `json:"termsize"`
|
||||
}
|
||||
|
||||
func (cmd *SetBlockTermSizeWSCommand) GetWSCommand() string {
|
||||
return cmd.WSCommand
|
||||
}
|
||||
|
||||
func ParseWSCommandMap(cmdMap map[string]any) (WSCommandType, error) {
|
||||
cmdType, ok := cmdMap["wscommand"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no wscommand field in command map")
|
||||
}
|
||||
switch cmdType {
|
||||
case WSCommand_SetBlockTermSize:
|
||||
var cmd SetBlockTermSizeWSCommand
|
||||
err := waveobj.DoMapStucture(&cmd, cmdMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding SetBlockTermSizeWSCommand: %w", err)
|
||||
}
|
||||
return &cmd, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown wscommand type %q", cmdType)
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,10 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
|
||||
"github.com/wavetermdev/thenextwave/pkg/eventbus"
|
||||
"github.com/wavetermdev/thenextwave/pkg/service/blockservice"
|
||||
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
||||
)
|
||||
|
||||
const wsReadWaitTimeout = 15 * time.Second
|
||||
@ -70,7 +73,42 @@ func getStringFromMap(jmsg map[string]any, key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func processWSCommand(jmsg map[string]any, outputCh chan any) {
|
||||
var rtnErr error
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
rtnErr = fmt.Errorf("panic: %v", r)
|
||||
log.Printf("panic in processMessage: %v\n", r)
|
||||
debug.PrintStack()
|
||||
}
|
||||
if rtnErr == nil {
|
||||
return
|
||||
}
|
||||
rtn := map[string]any{"type": "error", "error": rtnErr.Error()}
|
||||
outputCh <- rtn
|
||||
}()
|
||||
wsCommand, err := webcmd.ParseWSCommandMap(jmsg)
|
||||
if err != nil {
|
||||
rtnErr = fmt.Errorf("cannot parse wscommand: %v", err)
|
||||
return
|
||||
}
|
||||
switch cmd := wsCommand.(type) {
|
||||
case *webcmd.SetBlockTermSizeWSCommand:
|
||||
blockCmd := &blockcontroller.BlockInputCommand{
|
||||
Command: blockcontroller.BlockCommand_Input,
|
||||
TermSize: &cmd.TermSize,
|
||||
}
|
||||
blockservice.BlockServiceInstance.SendCommand(cmd.BlockId, blockCmd)
|
||||
}
|
||||
}
|
||||
|
||||
func processMessage(jmsg map[string]any, outputCh chan any) {
|
||||
wsCommand := getStringFromMap(jmsg, "wscommand")
|
||||
if wsCommand != "" {
|
||||
processWSCommand(jmsg, outputCh)
|
||||
return
|
||||
}
|
||||
msgType := getMessageType(jmsg)
|
||||
if msgType != "rpc" {
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user