mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
Replace the line-based REPL with a full-screen Bubble Tea TUI in
interactive mode (non-TTY still falls back to the line editor).
Layout (deveco-code inspired, no emoji):
- Top status bar: HomeAgent brand + connection dot + mode + addr
- Scrollable chat viewport with role-based rendering:
You (purple) user messages
小宅 (orange) agent responses
· reasoning dim gray italic, streaming-merged
[ok]/[fail] tool calls with status + truncated result
[sys] builtin command output
[err] errors
- Rounded-border input box with placeholder
- Bottom status bar: spinner while busy / hints + connection state
Key design points:
- reader generation counter prevents stale errors from the old
reader being mistaken for the new one after reconnect
- handleSubmit always returns waitServer when reader is alive,
so server responses to builtin commands like /status are received
- History stored as *History (was copying sync.Mutex by value)
- Chinese CJK wide-char aware word wrap with hanging indent
- /clear /exit /quit handled in TUI; other /cmds still go through
handleBuiltin with output captured to message area
Verified via tmux PTY: /help, /status, /clear, real chat with LLM
(reasoning -> tool_call -> response chain all render correctly).
59 lines
1020 B
Go
59 lines
1020 B
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type History struct {
|
|
path string
|
|
lines []string
|
|
max int
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func newHistory(path string, max int) *History {
|
|
return &History{path: path, max: max}
|
|
}
|
|
|
|
func (h *History) load() {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
data, err := os.ReadFile(h.path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
h.lines = strings.Split(strings.TrimSpace(string(data)), "\n")
|
|
if len(h.lines) > h.max {
|
|
h.lines = h.lines[len(h.lines)-h.max:]
|
|
}
|
|
}
|
|
|
|
func (h *History) save() {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
data := strings.Join(h.lines, "\n") + "\n"
|
|
os.WriteFile(h.path, []byte(data), 0644)
|
|
}
|
|
|
|
func (h *History) add(line string) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
if len(h.lines) > 0 && h.lines[len(h.lines)-1] == line {
|
|
return
|
|
}
|
|
h.lines = append(h.lines, line)
|
|
if len(h.lines) > h.max {
|
|
h.lines = h.lines[len(h.lines)-h.max:]
|
|
}
|
|
}
|
|
|
|
func (h *History) all() []string {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
r := make([]string, len(h.lines))
|
|
copy(r, h.lines)
|
|
return r
|
|
}
|