fix: token persistence, context windows, CSS tables/bullets/hr, image attachments
All checks were successful
Beta Release / beta (push) Successful in 1m1s

- Fix token count reset on app restart: persist realTokens in conversation.json
- Fix token/context window values: Studio 150K (summarize at 120K), Terminal 100K
- Fix table rendering in terminal tab: correct thead/tbody display model
- Fix copy button always top-right in Studio code blocks
- Add markdown horizontal rule (---) support in Studio and Terminal
- Fix bullet list double dot: remove CSS ::before duplicate bullet point
- Add image attachments support (VLM description, file mentions @file.ext)
- Add sudo detection with cache (sync.Once)
- Fix message content serialization (TextContent wrapper)
- Guide AI to use read_file instead of cat in studio prompt

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-26 15:19:26 +02:00
parent cb3d35756a
commit 12000e523c
17 changed files with 1686 additions and 109 deletions

View File

@@ -13,22 +13,24 @@ import (
"github.com/muyue/muyue/internal/config"
)
const maxTokensApprox = 100000
const summarizeThreshold = 80000
const contextWindowTokens = 150000
const summarizeRatio = 0.80
const charsPerToken = 4
type FeedMessage struct {
ID string `json:"id"`
Role string `json:"role"`
Content string `json:"content"`
Time string `json:"time"`
ID string `json:"id"`
Role string `json:"role"`
Content string `json:"content"`
Time string `json:"time"`
Images []string `json:"images,omitempty"`
}
type Conversation struct {
Messages []FeedMessage `json:"messages"`
Summary string `json:"summary,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Messages []FeedMessage `json:"messages"`
Summary string `json:"summary,omitempty"`
RealTokens int `json:"real_tokens,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type ConversationStore struct {
@@ -85,6 +87,7 @@ func (cs *ConversationStore) load() {
conv.Messages = []FeedMessage{}
}
cs.conv = &conv
cs.realTokens = conv.RealTokens
}
func (cs *ConversationStore) save() error {
@@ -127,15 +130,40 @@ func (cs *ConversationStore) Add(role, content string) FeedMessage {
return msg
}
func (cs *ConversationStore) AddWithImages(role, content string, imageIDs []string) FeedMessage {
cs.mu.Lock()
defer cs.mu.Unlock()
msg := FeedMessage{
ID: generateMsgID(),
Role: role,
Content: content,
Time: time.Now().Format(time.RFC3339),
Images: imageIDs,
}
cs.conv.Messages = append(cs.conv.Messages, msg)
cs.save()
return msg
}
func (cs *ConversationStore) Clear() {
cs.mu.Lock()
defer cs.mu.Unlock()
var imageIDs []string
for _, m := range cs.conv.Messages {
imageIDs = append(imageIDs, m.Images...)
}
cs.conv.Messages = []FeedMessage{}
cs.conv.Summary = ""
cs.conv.RealTokens = 0
cs.conv.CreatedAt = time.Now().Format(time.RFC3339)
cs.conv.UpdatedAt = time.Now().Format(time.RFC3339)
cs.realTokens = 0
cs.save()
go cleanupImages(imageIDs)
}
func (cs *ConversationStore) SetSummary(summary string) {
@@ -169,6 +197,7 @@ func (cs *ConversationStore) AddRealTokens(tokens int) {
}
cs.mu.Lock()
cs.realTokens += tokens
cs.conv.RealTokens = cs.realTokens
cs.mu.Unlock()
}
@@ -196,7 +225,7 @@ func (cs *ConversationStore) ApproxTokenCountDetailed() TokenCount {
}
func (cs *ConversationStore) NeedsSummarization() bool {
return cs.ApproxTokenCount() > summarizeThreshold
return cs.ApproxTokenCount() > int(float64(contextWindowTokens)*summarizeRatio)
}
func (cs *ConversationStore) Search(query string) []SearchResult {