package wcore import ( "context" "fmt" "log" "time" "github.com/google/uuid" "github.com/wavetermdev/waveterm/pkg/telemetry" "github.com/wavetermdev/waveterm/pkg/util/utilfn" "github.com/wavetermdev/waveterm/pkg/waveobj" "github.com/wavetermdev/waveterm/pkg/wshrpc" "github.com/wavetermdev/waveterm/pkg/wstore" ) func CreateWorkspace(ctx context.Context, name string, icon string, color string) (*waveobj.Workspace, error) { log.Println("CreateWorkspace") ws := &waveobj.Workspace{ OID: uuid.NewString(), TabIds: []string{}, Name: name, Icon: icon, Color: color, } wstore.DBInsert(ctx, ws) return ws, nil } func DeleteWorkspace(ctx context.Context, workspaceId string, force bool) (bool, error) { log.Printf("DeleteWorkspace %s\n", workspaceId) workspace, err := wstore.DBMustGet[*waveobj.Workspace](ctx, workspaceId) if err != nil { return false, fmt.Errorf("error getting workspace: %w", err) } if workspace.Name != "" && workspace.Icon != "" && !force { log.Printf("Ignoring DeleteWorkspace for workspace %s as it is named\n", workspaceId) return false, nil } for _, tabId := range workspace.TabIds { log.Printf("deleting tab %s\n", tabId) err := DeleteTab(ctx, workspaceId, tabId) if err != nil { return false, fmt.Errorf("error closing tab: %w", err) } } err = wstore.DBDelete(ctx, waveobj.OType_Workspace, workspaceId) if err != nil { return false, fmt.Errorf("error deleting workspace: %w", err) } log.Printf("deleted workspace %s\n", workspaceId) return true, nil } func GetWorkspace(ctx context.Context, wsID string) (*waveobj.Workspace, error) { return wstore.DBMustGet[*waveobj.Workspace](ctx, wsID) } func createTabObj(ctx context.Context, workspaceId string, name string) (*waveobj.Tab, error) { ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if ws == nil { return nil, fmt.Errorf("workspace not found: %q", workspaceId) } layoutStateId := uuid.NewString() tab := &waveobj.Tab{ OID: uuid.NewString(), Name: name, BlockIds: []string{}, LayoutState: layoutStateId, } layoutState := &waveobj.LayoutState{ OID: layoutStateId, } ws.TabIds = append(ws.TabIds, tab.OID) wstore.DBInsert(ctx, tab) wstore.DBInsert(ctx, layoutState) wstore.DBUpdate(ctx, ws) return tab, nil } // returns tabid func CreateTab(ctx context.Context, workspaceId string, tabName string, activateTab bool) (string, error) { if tabName == "" { client, err := wstore.DBGetSingleton[*waveobj.Client](ctx) if err != nil { return "", fmt.Errorf("error getting client: %w", err) } ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if ws == nil { return "", fmt.Errorf("workspace not found: %q", workspaceId) } tabName = "T" + fmt.Sprint(len(ws.TabIds)+1) err = wstore.DBUpdate(ctx, client) if err != nil { return "", fmt.Errorf("error updating client: %w", err) } } tab, err := createTabObj(ctx, workspaceId, tabName) if err != nil { return "", fmt.Errorf("error creating tab: %w", err) } if activateTab { err = SetActiveTab(ctx, workspaceId, tab.OID) if err != nil { return "", fmt.Errorf("error setting active tab: %w", err) } } telemetry.GoUpdateActivityWrap(wshrpc.ActivityUpdate{NewTab: 1}, "createtab") return tab.OID, nil } // must delete all blocks individually first // also deletes LayoutState func DeleteTab(ctx context.Context, workspaceId string, tabId string) error { ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if ws == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId) if tab == nil { return fmt.Errorf("tab not found: %q", tabId) } // close blocks (sends events + stops block controllers) for _, blockId := range tab.BlockIds { err := DeleteBlock(ctx, blockId) if err != nil { return fmt.Errorf("error deleting block %s: %w", blockId, err) } } tabIdx := utilfn.FindStringInSlice(ws.TabIds, tabId) if tabIdx == -1 { return nil } ws.TabIds = append(ws.TabIds[:tabIdx], ws.TabIds[tabIdx+1:]...) wstore.DBUpdate(ctx, ws) wstore.DBDelete(ctx, waveobj.OType_Tab, tabId) wstore.DBDelete(ctx, waveobj.OType_LayoutState, tab.LayoutState) return nil } func SetActiveTab(ctx context.Context, workspaceId string, tabId string) error { workspace, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if workspace == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } if tabId != "" { tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId) if tab == nil { return fmt.Errorf("tab not found: %q", tabId) } } workspace.ActiveTabId = tabId wstore.DBUpdate(ctx, workspace) return nil } func UpdateWorkspaceTabIds(ctx context.Context, workspaceId string, tabIds []string) error { ws, _ := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if ws == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } ws.TabIds = tabIds wstore.DBUpdate(ctx, ws) return nil } func ListWorkspaces(ctx context.Context) (waveobj.WorkspaceList, error) { workspaces, err := wstore.DBGetAllObjsByType[*waveobj.Workspace](ctx, waveobj.OType_Workspace) if err != nil { return nil, err } log.Println("got workspaces") windows, err := wstore.DBGetAllObjsByType[*waveobj.Window](ctx, waveobj.OType_Window) if err != nil { return nil, err } workspaceToWindow := make(map[string]string) for _, window := range windows { workspaceToWindow[window.WorkspaceId] = window.OID } var wl waveobj.WorkspaceList for _, workspace := range workspaces { if workspace.Name == "" || workspace.Icon == "" || workspace.Color == "" { continue } windowId, ok := workspaceToWindow[workspace.OID] if !ok { windowId = "" } wl = append(wl, &waveobj.WorkspaceListEntry{ WorkspaceId: workspace.OID, WindowId: windowId, }) } return wl, nil } func SetIcon(workspaceId string, icon string) error { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() ws, e := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if e != nil { return e } if ws == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } ws.Icon = icon wstore.DBUpdate(ctx, ws) return nil } func SetColor(workspaceId string, color string) error { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() ws, e := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if e != nil { return e } if ws == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } ws.Color = color wstore.DBUpdate(ctx, ws) return nil } func SetName(workspaceId string, name string) error { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() ws, e := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId) if e != nil { return e } if ws == nil { return fmt.Errorf("workspace not found: %q", workspaceId) } ws.Name = name wstore.DBUpdate(ctx, ws) return nil }