mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
save work
This commit is contained in:
parent
f975ecce48
commit
669762a9c9
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||||
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/workspaces/line"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HistoryItemType struct {
|
type HistoryItemType struct {
|
||||||
@ -114,7 +115,7 @@ type HistoryViewData struct {
|
|||||||
RawOffset int `json:"rawoffset"`
|
RawOffset int `json:"rawoffset"`
|
||||||
NextRawOffset int `json:"nextrawoffset"`
|
NextRawOffset int `json:"nextrawoffset"`
|
||||||
HasMore bool `json:"hasmore"`
|
HasMore bool `json:"hasmore"`
|
||||||
Lines []*sstore.LineType `json:"lines"`
|
Lines []*line.LineType `json:"lines"`
|
||||||
Cmds []*sstore.CmdType `json:"cmds"`
|
Cmds []*sstore.CmdType `json:"cmds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,14 +304,14 @@ func getLineIdsFromHistoryItems(historyItems []*HistoryItemType) []string {
|
|||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryItemType) ([]*sstore.LineType, []*sstore.CmdType, error) {
|
func GetLineCmdsFromHistoryItems(ctx context.Context, historyItems []*HistoryItemType) ([]*line.LineType, []*sstore.CmdType, error) {
|
||||||
if len(historyItems) == 0 {
|
if len(historyItems) == 0 {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) ([]*sstore.LineType, []*sstore.CmdType, error) {
|
return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) ([]*line.LineType, []*sstore.CmdType, error) {
|
||||||
lineIdsJsonArr := dbutil.QuickJsonArr(getLineIdsFromHistoryItems(historyItems))
|
lineIdsJsonArr := dbutil.QuickJsonArr(getLineIdsFromHistoryItems(historyItems))
|
||||||
query := `SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?))`
|
query := `SELECT * FROM line WHERE lineid IN (SELECT value FROM json_each(?))`
|
||||||
lineArr := dbutil.SelectMappable[*sstore.LineType](tx, query, lineIdsJsonArr)
|
lineArr := dbutil.SelectMappable[*line.LineType](tx, query, lineIdsJsonArr)
|
||||||
query = `SELECT * FROM cmd WHERE lineid IN (SELECT value FROM json_each(?))`
|
query = `SELECT * FROM cmd WHERE lineid IN (SELECT value FROM json_each(?))`
|
||||||
cmdArr := dbutil.SelectMapsGen[*sstore.CmdType](tx, query, lineIdsJsonArr)
|
cmdArr := dbutil.SelectMapsGen[*sstore.CmdType](tx, query, lineIdsJsonArr)
|
||||||
return lineArr, cmdArr, nil
|
return lineArr, cmdArr, nil
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -291,99 +290,6 @@ func FmtUniqueName(name string, defaultFmtStr string, startIdx int, strs []strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// special "E" returns last unarchived line, "EA" returns last line (even if archived)
|
|
||||||
func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (string, error) {
|
|
||||||
return WithTxRtn(ctx, func(tx *TxWrap) (string, error) {
|
|
||||||
if lineArg == "E" {
|
|
||||||
query := `SELECT lineid FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC LIMIT 1`
|
|
||||||
lineId := tx.GetString(query, screenId)
|
|
||||||
return lineId, nil
|
|
||||||
}
|
|
||||||
if lineArg == "EA" {
|
|
||||||
query := `SELECT lineid FROM line WHERE screenid = ? ORDER BY linenum DESC LIMIT 1`
|
|
||||||
lineId := tx.GetString(query, screenId)
|
|
||||||
return lineId, nil
|
|
||||||
}
|
|
||||||
lineNum, err := strconv.Atoi(lineArg)
|
|
||||||
if err == nil {
|
|
||||||
// valid linenum
|
|
||||||
query := `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?`
|
|
||||||
lineId := tx.GetString(query, screenId, lineNum)
|
|
||||||
return lineId, nil
|
|
||||||
} else if len(lineArg) == 8 {
|
|
||||||
// prefix id string match
|
|
||||||
query := `SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ?`
|
|
||||||
lineId := tx.GetString(query, screenId, lineArg)
|
|
||||||
return lineId, nil
|
|
||||||
} else {
|
|
||||||
// id match
|
|
||||||
query := `SELECT lineid FROM line WHERE screenid = ? AND lineid = ?`
|
|
||||||
lineId := tx.GetString(query, screenId, lineArg)
|
|
||||||
return lineId, nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *CmdType, error) {
|
|
||||||
return WithTxRtn3(ctx, func(tx *TxWrap) (*LineType, *CmdType, error) {
|
|
||||||
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
|
|
||||||
lineVal := dbutil.GetMappable[*LineType](tx, query, screenId, lineId)
|
|
||||||
if lineVal == nil {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
var cmdRtn *CmdType
|
|
||||||
query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
|
|
||||||
cmdRtn = dbutil.GetMapGen[*CmdType](tx, query, screenId, lineId)
|
|
||||||
return lineVal, cmdRtn, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func InsertLine(ctx context.Context, line *LineType, cmd *CmdType) error {
|
|
||||||
if line == nil {
|
|
||||||
return fmt.Errorf("line cannot be nil")
|
|
||||||
}
|
|
||||||
if line.LineId == "" {
|
|
||||||
return fmt.Errorf("line must have lineid set")
|
|
||||||
}
|
|
||||||
if line.LineNum != 0 {
|
|
||||||
return fmt.Errorf("line should not hage linenum set")
|
|
||||||
}
|
|
||||||
if cmd != nil && cmd.ScreenId == "" {
|
|
||||||
return fmt.Errorf("cmd should have screenid set")
|
|
||||||
}
|
|
||||||
qjs := dbutil.QuickJson(line.LineState)
|
|
||||||
if len(qjs) > MaxLineStateSize {
|
|
||||||
return fmt.Errorf("linestate exceeds maxsize, size[%d] max[%d]", len(qjs), MaxLineStateSize)
|
|
||||||
}
|
|
||||||
return WithTx(ctx, func(tx *TxWrap) error {
|
|
||||||
query := `SELECT screenid FROM screen WHERE screenid = ?`
|
|
||||||
if !tx.Exists(query, line.ScreenId) {
|
|
||||||
return fmt.Errorf("screen not found, cannot insert line[%s]", line.ScreenId)
|
|
||||||
}
|
|
||||||
query = `SELECT nextlinenum FROM screen WHERE screenid = ?`
|
|
||||||
nextLineNum := tx.GetInt(query, line.ScreenId)
|
|
||||||
line.LineNum = int64(nextLineNum)
|
|
||||||
query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived)
|
|
||||||
VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)`
|
|
||||||
tx.NamedExec(query, dbutil.ToDBMap(line, false))
|
|
||||||
query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?`
|
|
||||||
tx.Exec(query, nextLineNum+1, line.ScreenId)
|
|
||||||
if cmd != nil {
|
|
||||||
cmd.OrigTermOpts = cmd.TermOpts
|
|
||||||
cmdMap := cmd.ToMap()
|
|
||||||
query = `
|
|
||||||
INSERT INTO cmd ( screenid, lineid, remoteownerid, remoteid, remotename, cmdstr, rawcmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, cmdpid, remotepid, donets, restartts, exitcode, durationms, rtnstate, runout, rtnbasehash, rtndiffhasharr)
|
|
||||||
VALUES (:screenid,:lineid,:remoteownerid,:remoteid,:remotename,:cmdstr,:rawcmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:cmdpid,:remotepid,:donets,:restartts,:exitcode,:durationms,:rtnstate,:runout,:rtnbasehash,:rtndiffhasharr)
|
|
||||||
`
|
|
||||||
tx.NamedExec(query, cmdMap)
|
|
||||||
}
|
|
||||||
if IsWebShare(tx, line.ScreenId) {
|
|
||||||
InsertScreenLineUpdate(tx, line.ScreenId, line.LineId, UpdateType_LineNew)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCmdByScreenId(ctx context.Context, screenId string, lineId string) (*CmdType, error) {
|
func GetCmdByScreenId(ctx context.Context, screenId string, lineId string) (*CmdType, error) {
|
||||||
return WithTxRtn(ctx, func(tx *TxWrap) (*CmdType, error) {
|
return WithTxRtn(ctx, func(tx *TxWrap) (*CmdType, error) {
|
||||||
query := `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
|
query := `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
|
||||||
@ -574,7 +480,7 @@ func HangupCmd(ctx context.Context, ck base.CommandKey) (*ScreenType, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNextId(ids []string, delId string) string {
|
func GetNextId(ids []string, delId string) string {
|
||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
@ -35,14 +34,12 @@ import (
|
|||||||
|
|
||||||
type RemotePtrType = scpacket.RemotePtrType
|
type RemotePtrType = scpacket.RemotePtrType
|
||||||
|
|
||||||
const LineNoHeight = -1
|
|
||||||
const DBFileName = "waveterm.db"
|
const DBFileName = "waveterm.db"
|
||||||
const DBWALFileName = "waveterm.db-wal"
|
const DBWALFileName = "waveterm.db-wal"
|
||||||
const DBFileNameBackup = "backup.waveterm.db"
|
const DBFileNameBackup = "backup.waveterm.db"
|
||||||
const DBWALFileNameBackup = "backup.waveterm.db-wal"
|
const DBWALFileNameBackup = "backup.waveterm.db-wal"
|
||||||
const MaxWebShareLineCount = 50
|
const MaxWebShareLineCount = 50
|
||||||
const MaxWebShareScreenCount = 3
|
const MaxWebShareScreenCount = 3
|
||||||
const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed
|
|
||||||
|
|
||||||
const DefaultSessionName = "default"
|
const DefaultSessionName = "default"
|
||||||
const LocalRemoteAlias = "local"
|
const LocalRemoteAlias = "local"
|
||||||
@ -54,23 +51,6 @@ const APITokenSentinel = "--apitoken--"
|
|||||||
// be passed to waveshell (it should always get resolved prior to sending a run packet)
|
// be passed to waveshell (it should always get resolved prior to sending a run packet)
|
||||||
const ShellTypePref_Detect = "detect"
|
const ShellTypePref_Detect = "detect"
|
||||||
|
|
||||||
const (
|
|
||||||
LineTypeCmd = "cmd"
|
|
||||||
LineTypeText = "text"
|
|
||||||
LineTypeOpenAI = "openai"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
LineState_Source = "prompt:source"
|
|
||||||
LineState_File = "prompt:file"
|
|
||||||
LineState_FileUrl = "wave:fileurl"
|
|
||||||
LineState_Min = "wave:min"
|
|
||||||
LineState_Template = "template"
|
|
||||||
LineState_Mode = "mode"
|
|
||||||
LineState_Lang = "lang"
|
|
||||||
LineState_Minimap = "minimap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MainViewSession = "session"
|
MainViewSession = "session"
|
||||||
MainViewBookmarks = "bookmarks"
|
MainViewBookmarks = "bookmarks"
|
||||||
@ -452,27 +432,6 @@ func (ri *RemoteInstance) ToMap() map[string]interface{} {
|
|||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
type LineType struct {
|
|
||||||
ScreenId string `json:"screenid"`
|
|
||||||
UserId string `json:"userid"`
|
|
||||||
LineId string `json:"lineid"`
|
|
||||||
Ts int64 `json:"ts"`
|
|
||||||
LineNum int64 `json:"linenum"`
|
|
||||||
LineNumTemp bool `json:"linenumtemp,omitempty"`
|
|
||||||
LineLocal bool `json:"linelocal"`
|
|
||||||
LineType string `json:"linetype"`
|
|
||||||
LineState map[string]any `json:"linestate"`
|
|
||||||
Renderer string `json:"renderer,omitempty"`
|
|
||||||
Text string `json:"text,omitempty"`
|
|
||||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
|
||||||
ContentHeight int64 `json:"contentheight,omitempty"`
|
|
||||||
Star bool `json:"star,omitempty"`
|
|
||||||
Archived bool `json:"archived,omitempty"`
|
|
||||||
Remove bool `json:"remove,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (LineType) UseDBMap() {}
|
|
||||||
|
|
||||||
type OpenAIUsage struct {
|
type OpenAIUsage struct {
|
||||||
PromptTokens int `json:"prompt_tokens"`
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
CompletionTokens int `json:"completion_tokens"`
|
CompletionTokens int `json:"completion_tokens"`
|
||||||
@ -783,79 +742,6 @@ func (cmd *CmdType) IsRunning() bool {
|
|||||||
return cmd.Status == CmdStatusRunning || cmd.Status == CmdStatusDetached
|
return cmd.Status == CmdStatusRunning || cmd.Status == CmdStatusDetached
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeNewLineCmd(screenId string, userId string, lineId string, renderer string, lineState map[string]any) *LineType {
|
|
||||||
rtn := &LineType{}
|
|
||||||
rtn.ScreenId = screenId
|
|
||||||
rtn.UserId = userId
|
|
||||||
rtn.LineId = lineId
|
|
||||||
rtn.Ts = time.Now().UnixMilli()
|
|
||||||
rtn.LineLocal = true
|
|
||||||
rtn.LineType = LineTypeCmd
|
|
||||||
rtn.LineId = lineId
|
|
||||||
rtn.ContentHeight = LineNoHeight
|
|
||||||
rtn.Renderer = renderer
|
|
||||||
if lineState == nil {
|
|
||||||
lineState = make(map[string]any)
|
|
||||||
}
|
|
||||||
rtn.LineState = lineState
|
|
||||||
return rtn
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeNewLineText(screenId string, userId string, text string) *LineType {
|
|
||||||
rtn := &LineType{}
|
|
||||||
rtn.ScreenId = screenId
|
|
||||||
rtn.UserId = userId
|
|
||||||
rtn.LineId = scbase.GenWaveUUID()
|
|
||||||
rtn.Ts = time.Now().UnixMilli()
|
|
||||||
rtn.LineLocal = true
|
|
||||||
rtn.LineType = LineTypeText
|
|
||||||
rtn.Text = text
|
|
||||||
rtn.ContentHeight = LineNoHeight
|
|
||||||
rtn.LineState = make(map[string]any)
|
|
||||||
return rtn
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeNewLineOpenAI(screenId string, userId string, lineId string) *LineType {
|
|
||||||
rtn := &LineType{}
|
|
||||||
rtn.ScreenId = screenId
|
|
||||||
rtn.UserId = userId
|
|
||||||
rtn.LineId = lineId
|
|
||||||
rtn.Ts = time.Now().UnixMilli()
|
|
||||||
rtn.LineLocal = true
|
|
||||||
rtn.LineType = LineTypeOpenAI
|
|
||||||
rtn.ContentHeight = LineNoHeight
|
|
||||||
rtn.Renderer = CmdRendererOpenAI
|
|
||||||
rtn.LineState = make(map[string]any)
|
|
||||||
return rtn
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddCommentLine(ctx context.Context, screenId string, userId string, commentText string) (*LineType, error) {
|
|
||||||
rtnLine := makeNewLineText(screenId, userId, commentText)
|
|
||||||
err := InsertLine(ctx, rtnLine, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rtnLine, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddOpenAILine(ctx context.Context, screenId string, userId string, cmd *CmdType) (*LineType, error) {
|
|
||||||
rtnLine := makeNewLineOpenAI(screenId, userId, cmd.LineId)
|
|
||||||
err := InsertLine(ctx, rtnLine, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rtnLine, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddCmdLine(ctx context.Context, screenId string, userId string, cmd *CmdType, renderer string, lineState map[string]any) (*LineType, error) {
|
|
||||||
rtnLine := makeNewLineCmd(screenId, userId, cmd.LineId, renderer, lineState)
|
|
||||||
err := InsertLine(ctx, rtnLine, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rtnLine, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnsureLocalRemote(ctx context.Context) error {
|
func EnsureLocalRemote(ctx context.Context) error {
|
||||||
remote, err := GetLocalRemote(ctx)
|
remote, err := GetLocalRemote(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
118
wavesrv/pkg/workspaces/line/dbops.go
Normal file
118
wavesrv/pkg/workspaces/line/dbops.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package line
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
||||||
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// special "E" returns last unarchived line, "EA" returns last line (even if archived)
|
||||||
|
func FindLineIdByArg(ctx context.Context, screenId string, lineArg string) (string, error) {
|
||||||
|
return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (string, error) {
|
||||||
|
if lineArg == "E" {
|
||||||
|
query := `SELECT lineid FROM line WHERE screenid = ? AND NOT archived ORDER BY linenum DESC LIMIT 1`
|
||||||
|
lineId := tx.GetString(query, screenId)
|
||||||
|
return lineId, nil
|
||||||
|
}
|
||||||
|
if lineArg == "EA" {
|
||||||
|
query := `SELECT lineid FROM line WHERE screenid = ? ORDER BY linenum DESC LIMIT 1`
|
||||||
|
lineId := tx.GetString(query, screenId)
|
||||||
|
return lineId, nil
|
||||||
|
}
|
||||||
|
lineNum, err := strconv.Atoi(lineArg)
|
||||||
|
if err == nil {
|
||||||
|
// valid linenum
|
||||||
|
query := `SELECT lineid FROM line WHERE screenid = ? AND linenum = ?`
|
||||||
|
lineId := tx.GetString(query, screenId, lineNum)
|
||||||
|
return lineId, nil
|
||||||
|
} else if len(lineArg) == 8 {
|
||||||
|
// prefix id string match
|
||||||
|
query := `SELECT lineid FROM line WHERE screenid = ? AND substr(lineid, 1, 8) = ?`
|
||||||
|
lineId := tx.GetString(query, screenId, lineArg)
|
||||||
|
return lineId, nil
|
||||||
|
} else {
|
||||||
|
// id match
|
||||||
|
query := `SELECT lineid FROM line WHERE screenid = ? AND lineid = ?`
|
||||||
|
lineId := tx.GetString(query, screenId, lineArg)
|
||||||
|
return lineId, nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLineCmdByLineId(ctx context.Context, screenId string, lineId string) (*LineType, *sstore.CmdType, error) {
|
||||||
|
return sstore.WithTxRtn3(ctx, func(tx *sstore.TxWrap) (*LineType, *sstore.CmdType, error) {
|
||||||
|
query := `SELECT * FROM line WHERE screenid = ? AND lineid = ?`
|
||||||
|
lineVal := dbutil.GetMappable[*LineType](tx, query, screenId, lineId)
|
||||||
|
if lineVal == nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
var cmdRtn *sstore.CmdType
|
||||||
|
query = `SELECT * FROM cmd WHERE screenid = ? AND lineid = ?`
|
||||||
|
cmdRtn = dbutil.GetMapGen[*sstore.CmdType](tx, query, screenId, lineId)
|
||||||
|
return lineVal, cmdRtn, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func InsertLine(ctx context.Context, line *LineType, cmd *sstore.CmdType) error {
|
||||||
|
if line == nil {
|
||||||
|
return fmt.Errorf("line cannot be nil")
|
||||||
|
}
|
||||||
|
if line.LineId == "" {
|
||||||
|
return fmt.Errorf("line must have lineid set")
|
||||||
|
}
|
||||||
|
if line.LineNum != 0 {
|
||||||
|
return fmt.Errorf("line should not hage linenum set")
|
||||||
|
}
|
||||||
|
if cmd != nil && cmd.ScreenId == "" {
|
||||||
|
return fmt.Errorf("cmd should have screenid set")
|
||||||
|
}
|
||||||
|
qjs := dbutil.QuickJson(line.LineState)
|
||||||
|
if len(qjs) > MaxLineStateSize {
|
||||||
|
return fmt.Errorf("linestate exceeds maxsize, size[%d] max[%d]", len(qjs), MaxLineStateSize)
|
||||||
|
}
|
||||||
|
return sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
||||||
|
query := `SELECT screenid FROM screen WHERE screenid = ?`
|
||||||
|
if !tx.Exists(query, line.ScreenId) {
|
||||||
|
return fmt.Errorf("screen not found, cannot insert line[%s]", line.ScreenId)
|
||||||
|
}
|
||||||
|
query = `SELECT nextlinenum FROM screen WHERE screenid = ?`
|
||||||
|
nextLineNum := tx.GetInt(query, line.ScreenId)
|
||||||
|
line.LineNum = int64(nextLineNum)
|
||||||
|
query = `INSERT INTO line ( screenid, userid, lineid, ts, linenum, linenumtemp, linelocal, linetype, linestate, text, renderer, ephemeral, contentheight, star, archived)
|
||||||
|
VALUES (:screenid,:userid,:lineid,:ts,:linenum,:linenumtemp,:linelocal,:linetype,:linestate,:text,:renderer,:ephemeral,:contentheight,:star,:archived)`
|
||||||
|
tx.NamedExec(query, dbutil.ToDBMap(line, false))
|
||||||
|
query = `UPDATE screen SET nextlinenum = ? WHERE screenid = ?`
|
||||||
|
tx.Exec(query, nextLineNum+1, line.ScreenId)
|
||||||
|
if cmd != nil {
|
||||||
|
cmd.OrigTermOpts = cmd.TermOpts
|
||||||
|
cmdMap := cmd.ToMap()
|
||||||
|
query = `
|
||||||
|
INSERT INTO cmd ( screenid, lineid, remoteownerid, remoteid, remotename, cmdstr, rawcmdstr, festate, statebasehash, statediffhasharr, termopts, origtermopts, status, cmdpid, remotepid, donets, restartts, exitcode, durationms, rtnstate, runout, rtnbasehash, rtndiffhasharr)
|
||||||
|
VALUES (:screenid,:lineid,:remoteownerid,:remoteid,:remotename,:cmdstr,:rawcmdstr,:festate,:statebasehash,:statediffhasharr,:termopts,:origtermopts,:status,:cmdpid,:remotepid,:donets,:restartts,:exitcode,:durationms,:rtnstate,:runout,:rtnbasehash,:rtndiffhasharr)
|
||||||
|
`
|
||||||
|
tx.NamedExec(query, cmdMap)
|
||||||
|
}
|
||||||
|
if sstore.IsWebShare(tx, line.ScreenId) {
|
||||||
|
sstore.InsertScreenLineUpdate(tx, line.ScreenId, line.LineId, sstore.UpdateType_LineNew)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType, error) {
|
||||||
|
return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (*ScreenLinesType, error) {
|
||||||
|
query := `SELECT screenid FROM screen WHERE screenid = ?`
|
||||||
|
screen := dbutil.GetMappable[*ScreenLinesType](tx, query, screenId)
|
||||||
|
if screen == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum`
|
||||||
|
screen.Lines = dbutil.SelectMappable[*LineType](tx, query, screen.ScreenId)
|
||||||
|
query = `SELECT * FROM cmd WHERE screenid = ?`
|
||||||
|
screen.Cmds = dbutil.SelectMapsGen[*sstore.CmdType](tx, query, screen.ScreenId)
|
||||||
|
return screen, nil
|
||||||
|
})
|
||||||
|
}
|
135
wavesrv/pkg/workspaces/line/line.go
Normal file
135
wavesrv/pkg/workspaces/line/line.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package line
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
|
||||||
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MaxLineStateSize = 4 * 1024 // 4k for now, can raise if needed
|
||||||
|
const LineNoHeight = -1
|
||||||
|
|
||||||
|
const (
|
||||||
|
LineTypeCmd = "cmd"
|
||||||
|
LineTypeText = "text"
|
||||||
|
LineTypeOpenAI = "openai"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LineState_Source = "prompt:source"
|
||||||
|
LineState_File = "prompt:file"
|
||||||
|
LineState_FileUrl = "wave:fileurl"
|
||||||
|
LineState_Min = "wave:min"
|
||||||
|
LineState_Template = "template"
|
||||||
|
LineState_Mode = "mode"
|
||||||
|
LineState_Lang = "lang"
|
||||||
|
LineState_Minimap = "minimap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LineType struct {
|
||||||
|
ScreenId string `json:"screenid"`
|
||||||
|
UserId string `json:"userid"`
|
||||||
|
LineId string `json:"lineid"`
|
||||||
|
Ts int64 `json:"ts"`
|
||||||
|
LineNum int64 `json:"linenum"`
|
||||||
|
LineNumTemp bool `json:"linenumtemp,omitempty"`
|
||||||
|
LineLocal bool `json:"linelocal"`
|
||||||
|
LineType string `json:"linetype"`
|
||||||
|
LineState map[string]any `json:"linestate"`
|
||||||
|
Renderer string `json:"renderer,omitempty"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||||
|
ContentHeight int64 `json:"contentheight,omitempty"`
|
||||||
|
Star bool `json:"star,omitempty"`
|
||||||
|
Archived bool `json:"archived,omitempty"`
|
||||||
|
Remove bool `json:"remove,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (LineType) UseDBMap() {}
|
||||||
|
|
||||||
|
type ScreenLinesType struct {
|
||||||
|
ScreenId string `json:"screenid"`
|
||||||
|
Lines []*LineType `json:"lines" dbmap:"-"`
|
||||||
|
Cmds []*sstore.CmdType `json:"cmds" dbmap:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ScreenLinesType) UseDBMap() {}
|
||||||
|
|
||||||
|
func (ScreenLinesType) GetType() string {
|
||||||
|
return "screenlines"
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNewLineCmd(screenId string, userId string, lineId string, renderer string, lineState map[string]any) *LineType {
|
||||||
|
rtn := &LineType{}
|
||||||
|
rtn.ScreenId = screenId
|
||||||
|
rtn.UserId = userId
|
||||||
|
rtn.LineId = lineId
|
||||||
|
rtn.Ts = time.Now().UnixMilli()
|
||||||
|
rtn.LineLocal = true
|
||||||
|
rtn.LineType = LineTypeCmd
|
||||||
|
rtn.LineId = lineId
|
||||||
|
rtn.ContentHeight = LineNoHeight
|
||||||
|
rtn.Renderer = renderer
|
||||||
|
if lineState == nil {
|
||||||
|
lineState = make(map[string]any)
|
||||||
|
}
|
||||||
|
rtn.LineState = lineState
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNewLineText(screenId string, userId string, text string) *LineType {
|
||||||
|
rtn := &LineType{}
|
||||||
|
rtn.ScreenId = screenId
|
||||||
|
rtn.UserId = userId
|
||||||
|
rtn.LineId = scbase.GenWaveUUID()
|
||||||
|
rtn.Ts = time.Now().UnixMilli()
|
||||||
|
rtn.LineLocal = true
|
||||||
|
rtn.LineType = LineTypeText
|
||||||
|
rtn.Text = text
|
||||||
|
rtn.ContentHeight = LineNoHeight
|
||||||
|
rtn.LineState = make(map[string]any)
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNewLineOpenAI(screenId string, userId string, lineId string) *LineType {
|
||||||
|
rtn := &LineType{}
|
||||||
|
rtn.ScreenId = screenId
|
||||||
|
rtn.UserId = userId
|
||||||
|
rtn.LineId = lineId
|
||||||
|
rtn.Ts = time.Now().UnixMilli()
|
||||||
|
rtn.LineLocal = true
|
||||||
|
rtn.LineType = LineTypeOpenAI
|
||||||
|
rtn.ContentHeight = LineNoHeight
|
||||||
|
rtn.Renderer = sstore.CmdRendererOpenAI
|
||||||
|
rtn.LineState = make(map[string]any)
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddCommentLine(ctx context.Context, screenId string, userId string, commentText string) (*LineType, error) {
|
||||||
|
rtnLine := makeNewLineText(screenId, userId, commentText)
|
||||||
|
err := InsertLine(ctx, rtnLine, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rtnLine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddOpenAILine(ctx context.Context, screenId string, userId string, cmd *sstore.CmdType) (*LineType, error) {
|
||||||
|
rtnLine := makeNewLineOpenAI(screenId, userId, cmd.LineId)
|
||||||
|
err := InsertLine(ctx, rtnLine, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rtnLine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddCmdLine(ctx context.Context, screenId string, userId string, cmd *sstore.CmdType, renderer string, lineState map[string]any) (*LineType, error) {
|
||||||
|
rtnLine := makeNewLineCmd(screenId, userId, cmd.LineId, renderer, lineState)
|
||||||
|
err := InsertLine(ctx, rtnLine, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rtnLine, nil
|
||||||
|
}
|
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/dbutil"
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbus"
|
|
||||||
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,21 +18,6 @@ func GetScreenById(ctx context.Context, screenId string) (*ScreenType, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetScreenLinesById(ctx context.Context, screenId string) (*ScreenLinesType, error) {
|
|
||||||
return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) (*ScreenLinesType, error) {
|
|
||||||
query := `SELECT screenid FROM screen WHERE screenid = ?`
|
|
||||||
screen := dbutil.GetMappable[*ScreenLinesType](tx, query, screenId)
|
|
||||||
if screen == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
query = `SELECT * FROM line WHERE screenid = ? ORDER BY linenum`
|
|
||||||
screen.Lines = dbutil.SelectMappable[*sstore.LineType](tx, query, screen.ScreenId)
|
|
||||||
query = `SELECT * FROM cmd WHERE screenid = ?`
|
|
||||||
screen.Cmds = dbutil.SelectMapsGen[*sstore.CmdType](tx, query, screen.ScreenId)
|
|
||||||
return screen, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// includes archived screens
|
// includes archived screens
|
||||||
func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
|
func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, error) {
|
||||||
return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) ([]*ScreenType, error) {
|
return sstore.WithTxRtn(ctx, func(tx *sstore.TxWrap) ([]*ScreenType, error) {
|
||||||
@ -46,7 +30,7 @@ func GetSessionScreens(ctx context.Context, sessionId string) ([]*ScreenType, er
|
|||||||
// screen may not exist at this point (so don't query screen table)
|
// screen may not exist at this point (so don't query screen table)
|
||||||
func cleanScreenCmds(ctx context.Context, screenId string) error {
|
func cleanScreenCmds(ctx context.Context, screenId string) error {
|
||||||
var removedCmds []string
|
var removedCmds []string
|
||||||
txErr := WithTx(ctx, func(tx *sstore.TxWrap) error {
|
txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
||||||
query := `SELECT lineid FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
|
query := `SELECT lineid FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
|
||||||
removedCmds = tx.SelectStrings(query, screenId, screenId)
|
removedCmds = tx.SelectStrings(query, screenId, screenId)
|
||||||
query = `DELETE FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
|
query = `DELETE FROM cmd WHERE screenid = ? AND lineid NOT IN (SELECT lineid FROM line WHERE screenid = ?)`
|
||||||
@ -57,62 +41,13 @@ func cleanScreenCmds(ctx context.Context, screenId string) error {
|
|||||||
return txErr
|
return txErr
|
||||||
}
|
}
|
||||||
for _, lineId := range removedCmds {
|
for _, lineId := range removedCmds {
|
||||||
DeletePtyOutFile(ctx, screenId, lineId)
|
sstore.DeletePtyOutFile(ctx, screenId, lineId)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (scbus.UpdatePacket, error) {
|
|
||||||
var isActive bool
|
|
||||||
txErr := WithTx(ctx, func(tx *sstore.TxWrap) error {
|
|
||||||
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
|
|
||||||
if !tx.Exists(query, sessionId, screenId) {
|
|
||||||
return fmt.Errorf("cannot close screen (not found)")
|
|
||||||
}
|
|
||||||
if isWebShare(tx, screenId) {
|
|
||||||
return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive.")
|
|
||||||
}
|
|
||||||
query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?`
|
|
||||||
closeVal := tx.GetBool(query, sessionId, screenId)
|
|
||||||
if closeVal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived`
|
|
||||||
numScreens := tx.GetInt(query, sessionId)
|
|
||||||
if numScreens <= 1 {
|
|
||||||
return fmt.Errorf("cannot archive the last screen in a session")
|
|
||||||
}
|
|
||||||
query = `UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ?`
|
|
||||||
tx.Exec(query, time.Now().UnixMilli(), sessionId, screenId)
|
|
||||||
isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
|
|
||||||
if isActive {
|
|
||||||
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId)
|
|
||||||
nextId := getNextId(screenIds, screenId)
|
|
||||||
tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if txErr != nil {
|
|
||||||
return nil, txErr
|
|
||||||
}
|
|
||||||
newScreen, err := GetScreenById(ctx, screenId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot retrive archived screen: %w", err)
|
|
||||||
}
|
|
||||||
update := scbus.MakeUpdatePacket()
|
|
||||||
update.AddUpdate(*newScreen)
|
|
||||||
if isActive {
|
|
||||||
bareSession, err := GetBareSessionById(ctx, sessionId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
update.AddUpdate(*bareSession)
|
|
||||||
}
|
|
||||||
return update, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnArchiveScreen(ctx context.Context, sessionId string, screenId string) error {
|
func UnArchiveScreen(ctx context.Context, sessionId string, screenId string) error {
|
||||||
txErr := WithTx(ctx, func(tx *sstore.TxWrap) error {
|
txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
||||||
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived`
|
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived`
|
||||||
if !tx.Exists(query, sessionId, screenId) {
|
if !tx.Exists(query, sessionId, screenId) {
|
||||||
return fmt.Errorf("cannot re-open screen (not found or not archived)")
|
return fmt.Errorf("cannot re-open screen (not found or not archived)")
|
||||||
@ -155,7 +90,7 @@ func handleScreenDelUpdate(tx *sstore.TxWrap, screenId string) {
|
|||||||
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
|
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertScreenDelUpdate(tx *sstore.TxWrap, screenId string) {
|
func InsertScreenDelUpdate(tx *sstore.TxWrap, screenId string) {
|
||||||
handleScreenDelUpdate(tx, screenId)
|
handleScreenDelUpdate(tx, screenId)
|
||||||
insertScreenUpdate(tx, screenId, sstore.UpdateType_ScreenDel)
|
insertScreenUpdate(tx, screenId, sstore.UpdateType_ScreenDel)
|
||||||
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
|
// don't insert UpdateType_ScreenDel (we already processed it in cmdrunner)
|
||||||
|
@ -17,18 +17,6 @@ type ScreenOptsType struct {
|
|||||||
PTerm string `json:"pterm,omitempty"`
|
PTerm string `json:"pterm,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScreenLinesType struct {
|
|
||||||
ScreenId string `json:"screenid"`
|
|
||||||
Lines []*sstore.LineType `json:"lines" dbmap:"-"`
|
|
||||||
Cmds []*sstore.CmdType `json:"cmds" dbmap:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ScreenLinesType) UseDBMap() {}
|
|
||||||
|
|
||||||
func (ScreenLinesType) GetType() string {
|
|
||||||
return "screenlines"
|
|
||||||
}
|
|
||||||
|
|
||||||
type ScreenWebShareOpts struct {
|
type ScreenWebShareOpts struct {
|
||||||
ShareName string `json:"sharename"`
|
ShareName string `json:"sharename"`
|
||||||
ViewKey string `json:"viewkey"`
|
ViewKey string `json:"viewkey"`
|
||||||
|
@ -142,11 +142,11 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update
|
|||||||
var isActive bool
|
var isActive bool
|
||||||
var screenTombstone *screen.ScreenTombstoneType
|
var screenTombstone *screen.ScreenTombstoneType
|
||||||
txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
||||||
screen, err := screen.GetScreenById(tx.Context(), screenId)
|
scr, err := screen.GetScreenById(tx.Context(), screenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot get screen to delete: %w", err)
|
return fmt.Errorf("cannot get screen to delete: %w", err)
|
||||||
}
|
}
|
||||||
if screen == nil {
|
if scr == nil {
|
||||||
return fmt.Errorf("cannot delete screen (not found)")
|
return fmt.Errorf("cannot delete screen (not found)")
|
||||||
}
|
}
|
||||||
webSharing := sstore.IsWebShare(tx, screenId)
|
webSharing := sstore.IsWebShare(tx, screenId)
|
||||||
@ -164,16 +164,16 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update
|
|||||||
isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
|
isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
|
||||||
if isActive {
|
if isActive {
|
||||||
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId)
|
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId)
|
||||||
nextId := getNextId(screenIds, screenId)
|
nextId := sstore.GetNextId(screenIds, screenId)
|
||||||
tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
|
tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screenTombstone = &ScreenTombstoneType{
|
screenTombstone = &screen.ScreenTombstoneType{
|
||||||
ScreenId: screen.ScreenId,
|
ScreenId: scr.ScreenId,
|
||||||
SessionId: screen.SessionId,
|
SessionId: scr.SessionId,
|
||||||
Name: screen.Name,
|
Name: scr.Name,
|
||||||
DeletedTs: time.Now().UnixMilli(),
|
DeletedTs: time.Now().UnixMilli(),
|
||||||
ScreenOpts: screen.ScreenOpts,
|
ScreenOpts: scr.ScreenOpts,
|
||||||
}
|
}
|
||||||
query := `INSERT INTO screen_tombstone ( screenid, sessionid, name, deletedts, screenopts)
|
query := `INSERT INTO screen_tombstone ( screenid, sessionid, name, deletedts, screenopts)
|
||||||
VALUES (:screenid,:sessionid,:name,:deletedts,:screenopts)`
|
VALUES (:screenid,:sessionid,:name,:deletedts,:screenopts)`
|
||||||
@ -187,7 +187,7 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update
|
|||||||
query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ?`
|
query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ?`
|
||||||
tx.Exec(query, screenId)
|
tx.Exec(query, screenId)
|
||||||
if webSharing {
|
if webSharing {
|
||||||
insertScreenDelUpdate(tx, screenId)
|
screen.InsertScreenDelUpdate(tx, screenId)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -195,15 +195,64 @@ func DeleteScreen(ctx context.Context, screenId string, sessionDel bool, update
|
|||||||
return nil, txErr
|
return nil, txErr
|
||||||
}
|
}
|
||||||
if !sessionDel {
|
if !sessionDel {
|
||||||
GoDeleteScreenDirs(screenId)
|
sstore.GoDeleteScreenDirs(screenId)
|
||||||
}
|
}
|
||||||
if update == nil {
|
if update == nil {
|
||||||
update = scbus.MakeUpdatePacket()
|
update = scbus.MakeUpdatePacket()
|
||||||
}
|
}
|
||||||
update.AddUpdate(*screenTombstone)
|
update.AddUpdate(*screenTombstone)
|
||||||
update.AddUpdate(ScreenType{SessionId: sessionId, ScreenId: screenId, Remove: true})
|
update.AddUpdate(screen.ScreenType{SessionId: sessionId, ScreenId: screenId, Remove: true})
|
||||||
if isActive {
|
if isActive {
|
||||||
bareSession, err := GetBareSessionById(ctx, sessionId)
|
bareSession, err := session.GetBareSessionById(ctx, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
update.AddUpdate(*bareSession)
|
||||||
|
}
|
||||||
|
return update, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (scbus.UpdatePacket, error) {
|
||||||
|
var isActive bool
|
||||||
|
txErr := sstore.WithTx(ctx, func(tx *sstore.TxWrap) error {
|
||||||
|
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?`
|
||||||
|
if !tx.Exists(query, sessionId, screenId) {
|
||||||
|
return fmt.Errorf("cannot close screen (not found)")
|
||||||
|
}
|
||||||
|
if sstore.IsWebShare(tx, screenId) {
|
||||||
|
return fmt.Errorf("cannot archive screen while web-sharing. stop web-sharing before trying to archive.")
|
||||||
|
}
|
||||||
|
query = `SELECT archived FROM screen WHERE sessionid = ? AND screenid = ?`
|
||||||
|
closeVal := tx.GetBool(query, sessionId, screenId)
|
||||||
|
if closeVal {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
query = `SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived`
|
||||||
|
numScreens := tx.GetInt(query, sessionId)
|
||||||
|
if numScreens <= 1 {
|
||||||
|
return fmt.Errorf("cannot archive the last screen in a session")
|
||||||
|
}
|
||||||
|
query = `UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ?`
|
||||||
|
tx.Exec(query, time.Now().UnixMilli(), sessionId, screenId)
|
||||||
|
isActive = tx.Exists(`SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ?`, sessionId, screenId)
|
||||||
|
if isActive {
|
||||||
|
screenIds := tx.SelectStrings(`SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx`, sessionId)
|
||||||
|
nextId := sstore.GetNextId(screenIds, screenId)
|
||||||
|
tx.Exec(`UPDATE session SET activescreenid = ? WHERE sessionid = ?`, nextId, sessionId)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if txErr != nil {
|
||||||
|
return nil, txErr
|
||||||
|
}
|
||||||
|
newScreen, err := screen.GetScreenById(ctx, screenId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot retrive archived screen: %w", err)
|
||||||
|
}
|
||||||
|
update := scbus.MakeUpdatePacket()
|
||||||
|
update.AddUpdate(*newScreen)
|
||||||
|
if isActive {
|
||||||
|
bareSession, err := session.GetBareSessionById(ctx, sessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -274,15 +323,15 @@ func SwitchScreenById(ctx context.Context, sessionId string, screenId string) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
update := scbus.MakeUpdatePacket()
|
update := scbus.MakeUpdatePacket()
|
||||||
update.AddUpdate(ActiveSessionIdUpdate(sessionId))
|
update.AddUpdate(session.ActiveSessionIdUpdate(sessionId))
|
||||||
update.AddUpdate(*bareSession)
|
update.AddUpdate(*bareSession)
|
||||||
memState := GetScreenMemState(screenId)
|
memState := sstore.GetScreenMemState(screenId)
|
||||||
if memState != nil {
|
if memState != nil {
|
||||||
update.AddUpdate(CmdLineUpdate(memState.CmdInputText))
|
update.AddUpdate(sstore.CmdLineUpdate(memState.CmdInputText))
|
||||||
UpdateWithCurrentOpenAICmdInfoChat(screenId, update)
|
sstore.UpdateWithCurrentOpenAICmdInfoChat(screenId, update)
|
||||||
|
|
||||||
// Clear any previous status indicator for this screen
|
// Clear any previous status indicator for this screen
|
||||||
err := ResetStatusIndicator_Update(update, screenId)
|
err := sstore.ResetStatusIndicator_Update(update, screenId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is not a fatal error, so just log it
|
// This is not a fatal error, so just log it
|
||||||
log.Printf("error resetting status indicator when switching screens: %v\n", err)
|
log.Printf("error resetting status indicator when switching screens: %v\n", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user