112 lines
3.8 KiB
Go
112 lines
3.8 KiB
Go
//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
|
||
retryBtn *widget.Button // «Перезапустить» — полный аналог /retry N
|
||
boundTask int64 // >0 — вкладка привязана к задаче №boundTask
|
||
}
|
||
|
||
// NewChatPanel создаёт ChatPanel.
|
||
func NewChatPanel() *ChatPanel {
|
||
p := &ChatPanel{}
|
||
p.convLbl = widget.NewLabel("Выберите задачу.")
|
||
p.convLbl.Wrapping = fyne.TextWrapWord
|
||
p.input = widget.NewEntry()
|
||
p.input.SetPlaceHolder("Сообщение… (Enter — отправить)")
|
||
p.retryBtn = widget.NewButton("Перезапустить", p.retryTask)
|
||
p.retryBtn.Disable() // активируется при привязке вкладки к задаче (SetBoundTask)
|
||
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() } }),
|
||
p.retryBtn,
|
||
widget.NewButton("Отмена", func() { if p.commands != nil { p.commands.Cancel() } }),
|
||
)
|
||
return container.NewBorder(nil, nil, cmdBar, nil, p.input)
|
||
}
|
||
|
||
// retryTask — колбэк кнопки «Перезапустить»: отправляет "/retry N" тем же
|
||
// путём, что и ввод пользователя (Commands → Router → Core), т.е. серверная
|
||
// логика полностью совпадает с Telegram-командой /retry N.
|
||
func (p *ChatPanel) retryTask() {
|
||
if p.commands != nil {
|
||
p.commands.Retry(p.boundTask)
|
||
}
|
||
}
|
||
|
||
// SetBoundTask включает кнопку «Перезапустить», когда вкладка привязана к
|
||
// задаче (taskID > 0); для свободной вкладки кнопка неактивна.
|
||
func (p *ChatPanel) SetBoundTask(taskID int64) {
|
||
p.boundTask = taskID
|
||
if taskID > 0 {
|
||
p.retryBtn.Enable()
|
||
} else {
|
||
p.retryBtn.Disable()
|
||
}
|
||
}
|
||
|
||
// 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) |