burterm/internal/tui/sequencerview.go
2026-09-14 10:55:07 +03:00

160 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tui
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/r3g1tpr0cs/burterm/internal/sequencer"
)
// sequencerModel — вкладка Sequencer: textarea с токенами (по одному на
// строку), запуск FIPS-анализа по ctrl+r и читаемый отчёт по каждому
// проанализированному 20 000-битному блоку.
type sequencerModel struct {
input textarea.Model
output viewport.Model
focus int // 0 — вход, 1 — выход (скролл)
errMsg string
}
func newSequencerView() sequencerModel {
ta := textarea.New()
ta.Placeholder = "Токены по одному на строку — либо hex-строки, либо сырой текст"
ta.ShowLineNumbers = true
ta.Focus()
return sequencerModel{
input: ta,
output: viewport.New(0, 0),
}
}
func (m *sequencerModel) SetSize(width, height int) {
if width < 0 {
width = 0
}
if height < 0 {
height = 0
}
inHeight := height / 2
m.input.SetWidth(width)
m.input.SetHeight(inHeight)
m.output.Width = width
// 6 фиксированных строк вокруг output-viewport (построчный подсчёт по
// View(): "Токены"-метка 1, разделитель 1, "Отчёт FIPS"-метка 1,
// разделитель+футер(две строки) 3 — итого 6). Раньше стояло -4 — не
// совпадало с View() ещё до того, как футер стал двухстрочным.
m.output.Height = height - inHeight - 6
}
func (m sequencerModel) Update(msg tea.Msg) (sequencerModel, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() {
case "tab":
m.focus = (m.focus + 1) % 2
if m.focus == 0 {
m.input.Focus()
} else {
m.input.Blur()
}
return m, nil
case "ctrl+r":
m.analyze()
return m, nil
}
}
var cmd tea.Cmd
if m.focus == 0 {
m.input, cmd = m.input.Update(msg)
} else {
m.output, cmd = m.output.Update(msg)
}
return m, cmd
}
func (m *sequencerModel) analyze() {
var samples [][]byte
for _, line := range strings.Split(m.input.Value(), "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
samples = append(samples, sequencer.ParseSample(line))
}
if len(samples) == 0 {
m.errMsg = "нет сэмплов — вставь токены, по одному на строку"
return
}
results, blocks, bitsAvailable, err := sequencer.AnalyzeConcatenated(samples)
if err != nil {
m.errMsg = err.Error()
m.output.SetContent(fmt.Sprintf("Сэмплов: %d, доступно бит: %d — недостаточно для анализа.", len(samples), bitsAvailable))
return
}
m.errMsg = ""
m.output.SetContent(renderSequencerReport(samples, results, blocks, bitsAvailable))
}
func renderSequencerReport(samples [][]byte, results []sequencer.BlockResult, blocks, bitsAvailable int) string {
var b strings.Builder
b.WriteString(fmt.Sprintf("Сэмплов: %d · доступно бит: %d · проанализировано блоков по 20000 бит: %d\n\n", len(samples), bitsAvailable, blocks))
passCount := 0
for _, r := range results {
if r.AllPass() {
passCount++
}
}
b.WriteString(fmt.Sprintf("Итог: %d/%d блоков прошли все четыре теста\n\n", passCount, blocks))
for i, r := range results {
b.WriteString(fmt.Sprintf("Блок %d:\n", i+1))
b.WriteString(fmt.Sprintf(" Monobit: единиц=%d %s\n", r.MonobitOnes, passLabel(r.MonobitPass)))
b.WriteString(fmt.Sprintf(" Poker: X=%.2f %s\n", r.PokerX, passLabel(r.PokerPass)))
b.WriteString(fmt.Sprintf(" Runs: %s\n", passLabel(r.RunsPass)))
b.WriteString(fmt.Sprintf(" Long run: макс. серия=%d бит %s\n", r.LongRunMax, passLabel(r.LongRunPass)))
b.WriteString("\n")
}
return b.String()
}
func passLabel(pass bool) string {
if pass {
return "PASS"
}
return "FAIL"
}
func (m sequencerModel) View() string {
focusMark := func(i int) string {
if m.focus == i {
return "▸ "
}
return " "
}
var b strings.Builder
b.WriteString(focusMark(0) + "Токены (по одному на строку)\n")
b.WriteString(m.input.View())
b.WriteString("\n\n")
if m.errMsg != "" {
b.WriteString("Ошибка: " + m.errMsg + "\n\n")
}
b.WriteString(focusMark(1) + "Отчёт FIPS 140-2\n")
b.WriteString(m.output.View())
b.WriteString("\n\ntab — между полями · ctrl+r — запустить анализ\n")
b.WriteString("f1-f12, pgup — вкладки\n")
return b.String()
}