diff options
| author | Grail Finder <wohilas@gmail.com> | 2025-11-24 17:42:47 +0300 |
|---|---|---|
| committer | Grail Finder <wohilas@gmail.com> | 2025-11-24 17:42:47 +0300 |
| commit | 41be229423a2dcfbc6d05881e4d5608aae728d2b (patch) | |
| tree | 3d12ea5b074a6ac0c601ae8b58391cbe0b2a84de /helpfuncs.go | |
| parent | 9686915545734caeb2a19b1419ef2a66dc2e1a51 (diff) | |
Chore: refactoring
Diffstat (limited to 'helpfuncs.go')
| -rw-r--r-- | helpfuncs.go | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/helpfuncs.go b/helpfuncs.go new file mode 100644 index 0000000..f238cd4 --- /dev/null +++ b/helpfuncs.go @@ -0,0 +1,216 @@ +package main + +import ( + "fmt" + "gf-lt/models" + "gf-lt/pngmeta" + "image" + "os" + "path" + "strings" +) + +func colorText() { + text := textView.GetText(false) + quoteReplacer := strings.NewReplacer( + `”`, `"`, + `“`, `"`, + `“`, `"`, + `”`, `"`, + `**`, `*`, + ) + text = quoteReplacer.Replace(text) + // Step 1: Extract code blocks and replace them with unique placeholders + var codeBlocks []string + placeholder := "__CODE_BLOCK_%d__" + counter := 0 + // thinking + var thinkBlocks []string + placeholderThink := "__THINK_BLOCK_%d__" + counterThink := 0 + // Replace code blocks with placeholders and store their styled versions + text = codeBlockRE.ReplaceAllStringFunc(text, func(match string) string { + // Style the code block and store it + styled := fmt.Sprintf("[red::i]%s[-:-:-]", match) + codeBlocks = append(codeBlocks, styled) + // Generate a unique placeholder (e.g., "__CODE_BLOCK_0__") + id := fmt.Sprintf(placeholder, counter) + counter++ + return id + }) + text = thinkRE.ReplaceAllStringFunc(text, func(match string) string { + // Style the code block and store it + styled := fmt.Sprintf("[red::i]%s[-:-:-]", match) + thinkBlocks = append(thinkBlocks, styled) + // Generate a unique placeholder (e.g., "__CODE_BLOCK_0__") + id := fmt.Sprintf(placeholderThink, counterThink) + counterThink++ + return id + }) + // Step 2: Apply other regex styles to the non-code parts + text = quotesRE.ReplaceAllString(text, `[orange::-]$1[-:-:-]`) + text = starRE.ReplaceAllString(text, `[turquoise::i]$1[-:-:-]`) + // text = thinkRE.ReplaceAllString(text, `[yellow::i]$1[-:-:-]`) + // Step 3: Restore the styled code blocks from placeholders + for i, cb := range codeBlocks { + text = strings.Replace(text, fmt.Sprintf(placeholder, i), cb, 1) + } + logger.Debug("thinking debug", "blocks", thinkBlocks) + for i, tb := range thinkBlocks { + text = strings.Replace(text, fmt.Sprintf(placeholderThink, i), tb, 1) + } + textView.SetText(text) +} + +func updateStatusLine() { + position.SetText(makeStatusLine()) + helpView.SetText(fmt.Sprintf(helpText, makeStatusLine())) +} + +func initSysCards() ([]string, error) { + labels := []string{} + labels = append(labels, sysLabels...) + cards, err := pngmeta.ReadDirCards(cfg.SysDir, cfg.UserRole, logger) + if err != nil { + logger.Error("failed to read sys dir", "error", err) + return nil, err + } + for _, cc := range cards { + if cc.Role == "" { + logger.Warn("empty role", "file", cc.FilePath) + continue + } + sysMap[cc.Role] = cc + labels = append(labels, cc.Role) + } + return labels, nil +} + +func startNewChat() { + id, err := store.ChatGetMaxID() + if err != nil { + logger.Error("failed to get chat id", "error", err) + } + if ok := charToStart(cfg.AssistantRole); !ok { + logger.Warn("no such sys msg", "name", cfg.AssistantRole) + } + // set chat body + chatBody.Messages = chatBody.Messages[:2] + textView.SetText(chatToText(cfg.ShowSys)) + newChat := &models.Chat{ + ID: id + 1, + Name: fmt.Sprintf("%d_%s", id+1, cfg.AssistantRole), + Msgs: string(defaultStarterBytes), + Agent: cfg.AssistantRole, + } + activeChatName = newChat.Name + chatMap[newChat.Name] = newChat + updateStatusLine() + colorText() +} + +func renameUser(oldname, newname string) { + if oldname == "" { + // not provided; deduce who user is + // INFO: if user not yet spoke, it is hard to replace mentions in sysprompt and first message about thme + roles := chatBody.ListRoles() + for _, role := range roles { + if role == cfg.AssistantRole { + continue + } + if role == cfg.ToolRole { + continue + } + if role == "system" { + continue + } + oldname = role + break + } + if oldname == "" { + // still + logger.Warn("fn: renameUser; failed to find old name", "newname", newname) + return + } + } + viewText := textView.GetText(false) + viewText = strings.ReplaceAll(viewText, oldname, newname) + chatBody.Rename(oldname, newname) + textView.SetText(viewText) +} + +func setLogLevel(sl string) { + switch sl { + case "Debug": + logLevel.Set(-4) + case "Info": + logLevel.Set(0) + case "Warn": + logLevel.Set(4) + } +} + +func listRolesWithUser() []string { + roles := chatBody.ListRoles() + if !strInSlice(cfg.UserRole, roles) { + roles = append(roles, cfg.UserRole) + } + return roles +} + +func loadImage() { + filepath := defaultImage + cc, ok := sysMap[cfg.AssistantRole] + if ok { + if strings.HasSuffix(cc.FilePath, ".png") { + filepath = cc.FilePath + } + } + file, err := os.Open(filepath) + if err != nil { + panic(err) + } + defer file.Close() + img, _, err := image.Decode(file) + if err != nil { + panic(err) + } + imgView.SetImage(img) +} + +func strInSlice(s string, sl []string) bool { + for _, el := range sl { + if strings.EqualFold(s, el) { + return true + } + } + return false +} + +func makeStatusLine() string { + isRecording := false + if asr != nil { + isRecording = asr.IsRecording() + } + persona := cfg.UserRole + if cfg.WriteNextMsgAs != "" { + persona = cfg.WriteNextMsgAs + } + botPersona := cfg.AssistantRole + if cfg.WriteNextMsgAsCompletionAgent != "" { + botPersona = cfg.WriteNextMsgAsCompletionAgent + } + // Add image attachment info to status line + var imageInfo string + if imageAttachmentPath != "" { + // Get just the filename from the path + imageName := path.Base(imageAttachmentPath) + imageInfo = fmt.Sprintf(" | attached img: [orange:-:b]%s[-:-:-]", imageName) + } else { + imageInfo = "" + } + statusLine := fmt.Sprintf(indexLineCompletion, botRespMode, cfg.AssistantRole, activeChatName, + cfg.ToolUse, chatBody.Model, cfg.SkipLLMResp, cfg.CurrentAPI, cfg.ThinkUse, logLevel.Level(), + isRecording, persona, botPersona, injectRole) + return statusLine + imageInfo +} |
