From 0a19aa31d4965c7c6d71b1029874cf6dd9085d9d Mon Sep 17 00:00:00 2001
From: Mike Sawka <mike@commandline.dev>
Date: Thu, 20 Jun 2024 00:00:00 -0700
Subject: [PATCH] working on wsh createblock (wsh view).  bug fix for emain
 closed windows (#64)

---
 cmd/wsh/cmd/{getmeta.go => wshcmd-getmeta.go} |  0
 cmd/wsh/cmd/{html.go => wshcmd-html.go}       |  0
 cmd/wsh/cmd/{root.go => wshcmd-root.go}       |  5 +-
 cmd/wsh/cmd/{setmeta.go => wshcmd-setmeta.go} |  0
 cmd/wsh/cmd/{version.go => wshcmd-version.go} |  0
 cmd/wsh/cmd/wshcmd-view.go                    | 61 +++++++++++++++++++
 emain/emain.ts                                |  6 ++
 pkg/blockcontroller/blockcontroller.go        |  2 +
 pkg/service/windowservice/windowservice.go    | 10 +++
 pkg/util/utilfn/utilfn.go                     | 10 +++
 pkg/wshutil/wshcommands.go                    | 13 ++++
 11 files changed, 106 insertions(+), 1 deletion(-)
 rename cmd/wsh/cmd/{getmeta.go => wshcmd-getmeta.go} (100%)
 rename cmd/wsh/cmd/{html.go => wshcmd-html.go} (100%)
 rename cmd/wsh/cmd/{root.go => wshcmd-root.go} (97%)
 rename cmd/wsh/cmd/{setmeta.go => wshcmd-setmeta.go} (100%)
 rename cmd/wsh/cmd/{version.go => wshcmd-version.go} (100%)
 create mode 100644 cmd/wsh/cmd/wshcmd-view.go

diff --git a/cmd/wsh/cmd/getmeta.go b/cmd/wsh/cmd/wshcmd-getmeta.go
similarity index 100%
rename from cmd/wsh/cmd/getmeta.go
rename to cmd/wsh/cmd/wshcmd-getmeta.go
diff --git a/cmd/wsh/cmd/html.go b/cmd/wsh/cmd/wshcmd-html.go
similarity index 100%
rename from cmd/wsh/cmd/html.go
rename to cmd/wsh/cmd/wshcmd-html.go
diff --git a/cmd/wsh/cmd/root.go b/cmd/wsh/cmd/wshcmd-root.go
similarity index 97%
rename from cmd/wsh/cmd/root.go
rename to cmd/wsh/cmd/wshcmd-root.go
index 730b97c14..0cb679194 100644
--- a/cmd/wsh/cmd/root.go
+++ b/cmd/wsh/cmd/wshcmd-root.go
@@ -41,7 +41,9 @@ var RpcClient *wshutil.WshRpc
 func doShutdown(reason string, exitCode int) {
 	shutdownOnce.Do(func() {
 		defer os.Exit(exitCode)
-		log.Printf("shutting down: %s\r\n", reason)
+		if reason != "" {
+			log.Printf("shutting down: %s\r\n", reason)
+		}
 		if usingHtmlMode {
 			cmd := &wshutil.BlockSetMetaCommand{
 				Command: wshutil.BlockCommand_SetMeta,
@@ -157,6 +159,7 @@ func resolveSimpleId(id string) (string, error) {
 
 // Execute executes the root command.
 func Execute() error {
+	defer doShutdown("", 0)
 	setupRpcClient(nil)
 	return rootCmd.Execute()
 }
diff --git a/cmd/wsh/cmd/setmeta.go b/cmd/wsh/cmd/wshcmd-setmeta.go
similarity index 100%
rename from cmd/wsh/cmd/setmeta.go
rename to cmd/wsh/cmd/wshcmd-setmeta.go
diff --git a/cmd/wsh/cmd/version.go b/cmd/wsh/cmd/wshcmd-version.go
similarity index 100%
rename from cmd/wsh/cmd/version.go
rename to cmd/wsh/cmd/wshcmd-version.go
diff --git a/cmd/wsh/cmd/wshcmd-view.go b/cmd/wsh/cmd/wshcmd-view.go
new file mode 100644
index 000000000..cdb8f3e59
--- /dev/null
+++ b/cmd/wsh/cmd/wshcmd-view.go
@@ -0,0 +1,61 @@
+// Copyright 2024, Command Line Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+package cmd
+
+import (
+	"io/fs"
+	"log"
+	"os"
+	"path/filepath"
+
+	"github.com/spf13/cobra"
+	"github.com/wavetermdev/thenextwave/pkg/wshutil"
+	"github.com/wavetermdev/thenextwave/pkg/wstore"
+)
+
+var viewNewBlock bool
+
+var viewCmd = &cobra.Command{
+	Use:   "view",
+	Short: "preview a file or directory",
+	Args:  cobra.ExactArgs(1),
+	Run:   viewRun,
+}
+
+func init() {
+	viewCmd.Flags().BoolVarP(&viewNewBlock, "newblock", "n", false, "open view in a new block")
+	rootCmd.AddCommand(viewCmd)
+}
+
+func viewRun(cmd *cobra.Command, args []string) {
+	fileArg := args[0]
+	absFile, err := filepath.Abs(fileArg)
+	if err != nil {
+		log.Printf("error getting absolute path: %v\n", err)
+		return
+	}
+	_, err = os.Stat(absFile)
+	if err == fs.ErrNotExist {
+		log.Printf("file does not exist: %q\n", absFile)
+		return
+	}
+	if err != nil {
+		log.Printf("error getting file info: %v\n", err)
+	}
+	setTermRawMode()
+	viewWshCmd := &wshutil.CreateBlockCommand{
+		Command: wshutil.Command_CreateBlock,
+		BlockDef: &wstore.BlockDef{
+			View: "preview",
+			Meta: map[string]interface{}{
+				"file": absFile,
+			},
+		},
+	}
+	_, err = RpcClient.SendRpcRequest(viewWshCmd, 2000)
+	if err != nil {
+		log.Printf("error running view command: %v\r\n", err)
+		return
+	}
+}
diff --git a/emain/emain.ts b/emain/emain.ts
index e1e3473a1..a43c55f15 100644
--- a/emain/emain.ts
+++ b/emain/emain.ts
@@ -519,6 +519,12 @@ async function appMain() {
     let wins: WaveBrowserWindow[] = [];
     for (let windowId of clientData.windowids.slice().reverse()) {
         let windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
+        if (windowData == null) {
+            services.WindowService.CloseWindow(windowId).catch((e) => {
+                /* ignore */
+            });
+            continue;
+        }
         const win = createBrowserWindow(clientData, windowData);
         wins.push(win);
     }
diff --git a/pkg/blockcontroller/blockcontroller.go b/pkg/blockcontroller/blockcontroller.go
index 450452d46..29a0c0fa2 100644
--- a/pkg/blockcontroller/blockcontroller.go
+++ b/pkg/blockcontroller/blockcontroller.go
@@ -267,6 +267,8 @@ func (bc *BlockController) waveOSCMessageHandler(ctx context.Context, cmd wshuti
 		return staticHandleGetMeta(ctx, cmd.(*wshutil.BlockGetMetaCommand))
 	case wshutil.Command_ResolveIds:
 		return staticHandleResolveIds(ctx, cmd.(*wshutil.ResolveIdsCommand))
+	case wshutil.Command_CreateBlock:
+		return nil, nil
 	}
 
 	ProcessStaticCommand(bc.BlockId, cmd)
diff --git a/pkg/service/windowservice/windowservice.go b/pkg/service/windowservice/windowservice.go
index 1c81afe84..d6e0a6512 100644
--- a/pkg/service/windowservice/windowservice.go
+++ b/pkg/service/windowservice/windowservice.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
+	"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
 	"github.com/wavetermdev/thenextwave/pkg/wstore"
 )
 
@@ -96,5 +97,14 @@ func (svc *WindowService) CloseWindow(ctx context.Context, windowId string) erro
 	if err != nil {
 		return fmt.Errorf("error deleting window: %w", err)
 	}
+	client, err := wstore.DBGetSingleton[*wstore.Client](ctx)
+	if err != nil {
+		return fmt.Errorf("error getting client: %w", err)
+	}
+	utilfn.RemoveElemFromSlice(client.WindowIds, windowId)
+	err = wstore.DBUpdate(ctx, client)
+	if err != nil {
+		return fmt.Errorf("error updating client: %w", err)
+	}
 	return nil
 }
diff --git a/pkg/util/utilfn/utilfn.go b/pkg/util/utilfn/utilfn.go
index b866980e3..e95f6a025 100644
--- a/pkg/util/utilfn/utilfn.go
+++ b/pkg/util/utilfn/utilfn.go
@@ -750,6 +750,16 @@ func SliceIdx[T comparable](arr []T, elem T) int {
 	return -1
 }
 
+func RemoveElemFromSlice[T comparable](arr []T, elem T) []T {
+	rtn := make([]T, 0, len(arr))
+	for _, e := range arr {
+		if e != elem {
+			rtn = append(rtn, e)
+		}
+	}
+	return rtn
+}
+
 func MoveSliceIdxToFront[T any](arr []T, idx int) []T {
 	// create and return a new slice with idx moved to the front
 	if idx == 0 || idx >= len(arr) {
diff --git a/pkg/wshutil/wshcommands.go b/pkg/wshutil/wshcommands.go
index dfea9fa0b..05935e1f9 100644
--- a/pkg/wshutil/wshcommands.go
+++ b/pkg/wshutil/wshcommands.go
@@ -11,6 +11,7 @@ import (
 	"github.com/wavetermdev/thenextwave/pkg/ijson"
 	"github.com/wavetermdev/thenextwave/pkg/shellexec"
 	"github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
+	"github.com/wavetermdev/thenextwave/pkg/wstore"
 )
 
 const CommandKey = "command"
@@ -24,6 +25,7 @@ const (
 	BlockCommand_AppendBlockFile = "blockfile:append"
 	BlockCommand_AppendIJson     = "blockfile:appendijson"
 	Command_ResolveIds           = "resolveids"
+	Command_CreateBlock          = "createblock"
 )
 
 var CommandToTypeMap = map[string]reflect.Type{
@@ -35,6 +37,7 @@ var CommandToTypeMap = map[string]reflect.Type{
 	BlockCommand_AppendBlockFile: reflect.TypeOf(BlockAppendFileCommand{}),
 	BlockCommand_AppendIJson:     reflect.TypeOf(BlockAppendIJsonCommand{}),
 	Command_ResolveIds:           reflect.TypeOf(ResolveIdsCommand{}),
+	Command_CreateBlock:          reflect.TypeOf(CreateBlockCommand{}),
 }
 
 func CommandTypeUnionMeta() tsgenmeta.TypeUnionMeta {
@@ -158,3 +161,13 @@ type BlockAppendIJsonCommand struct {
 func (bwc *BlockAppendIJsonCommand) GetCommand() string {
 	return BlockCommand_AppendIJson
 }
+
+type CreateBlockCommand struct {
+	Command  string              `json:"command" tstype:"\"createblock\""`
+	BlockDef *wstore.BlockDef    `json:"blockdef"`
+	RtOpts   *wstore.RuntimeOpts `json:"rtopts,omitempty"`
+}
+
+func (cbc *CreateBlockCommand) GetCommand() string {
+	return Command_CreateBlock
+}