refactor(ui): вынести панель «Диалог» (ChatPanel) из монолита окна
Спеца 12.12: интерфейс + Nil + FormatRole в internal/ui/chat_panel.go, Fyne-реализация в internal/ui/desktop/chat_panel.go (транскрипт + композитор). Окно как chat.Channel рендерит Send/Ask/историю через Append; state_panel не трогаем.
This commit is contained in:
87
internal/ui/desktop/chat_panel.go
Normal file
87
internal/ui/desktop/chat_panel.go
Normal file
@@ -0,0 +1,87 @@
|
||||
//go:build cgo
|
||||
|
||||
package desktop
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
|
||||
"github.com/kamelion/ratatoskr-go/internal/ui"
|
||||
)
|
||||
|
||||
// chatLimit — обрезка буфера транскрипта диалога (спец 12.12).
|
||||
const chatLimit = 200_000
|
||||
|
||||
// ChatPanel — Fyne-реализация ui.ChatPanel (спец 12.8, 12.12): транскрипт
|
||||
// диалога + композитор (поле ввода и кнопки команд).
|
||||
type ChatPanel struct {
|
||||
convLbl *widget.Label
|
||||
input *widget.Entry
|
||||
commands *ui.Commands
|
||||
}
|
||||
|
||||
// NewChatPanel создаёт ChatPanel.
|
||||
func NewChatPanel() *ChatPanel {
|
||||
p := &ChatPanel{}
|
||||
p.convLbl = widget.NewLabel("Выберите задачу.")
|
||||
p.convLbl.Wrapping = fyne.TextWrapWord
|
||||
p.input = widget.NewEntry()
|
||||
p.input.SetPlaceHolder("Сообщение… (Enter — отправить)")
|
||||
return p
|
||||
}
|
||||
|
||||
// Transcript возвращает виджет транскрипта (Scroll) для встраивания в окно.
|
||||
func (p *ChatPanel) Transcript() fyne.CanvasObject {
|
||||
return container.NewScroll(p.convLbl)
|
||||
}
|
||||
|
||||
// Composer возвращает виджет композитора (ввод + кнопки команд).
|
||||
func (p *ChatPanel) Composer() fyne.CanvasObject {
|
||||
cmdBar := container.NewHBox(
|
||||
widget.NewButton("Новая", func() { if p.commands != nil { p.commands.Start() } }),
|
||||
widget.NewButton("Создавай", func() { if p.commands != nil { p.commands.Approve() } }),
|
||||
widget.NewButton("Пропустить", func() { if p.commands != nil { p.commands.Skip() } }),
|
||||
widget.NewButton("Отмена", func() { if p.commands != nil { p.commands.Cancel() } }),
|
||||
)
|
||||
return container.NewBorder(nil, nil, cmdBar, p.input, nil)
|
||||
}
|
||||
|
||||
// SetTranscript заменяет содержимое диалога (спец 12.12).
|
||||
func (p *ChatPanel) SetTranscript(text string) {
|
||||
p.convLbl.SetText(text)
|
||||
}
|
||||
|
||||
// Append добавляет строку в диалог (буфер ограничен).
|
||||
func (p *ChatPanel) Append(text string) {
|
||||
s := p.convLbl.Text + text + "\n\n"
|
||||
if len(s) > chatLimit {
|
||||
s = s[len(s)-chatLimit:]
|
||||
}
|
||||
p.convLbl.SetText(s)
|
||||
}
|
||||
|
||||
// Clear очищает диалог.
|
||||
func (p *ChatPanel) Clear() {
|
||||
p.convLbl.SetText("")
|
||||
}
|
||||
|
||||
// SetCommands подключает команды к композитору.
|
||||
func (p *ChatPanel) SetCommands(c *ui.Commands) {
|
||||
p.commands = c
|
||||
p.input.OnSubmitted = func(s string) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
p.input.SetText("")
|
||||
if c != nil {
|
||||
c.SendText(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compile-time проверка реализации контракта.
|
||||
var _ ui.ChatPanel = (*ChatPanel)(nil)
|
||||
Reference in New Issue
Block a user