Просмотр исходного кода

d01 Вытаскивание экранов в отдельные типы

SVI 3 лет назад
Родитель
Сommit
de739287a2

+ 9 - 4
cmd/desktop/main.go

@@ -2,13 +2,18 @@ package main
 
 
 import (
 import (
 	"log"
 	"log"
+	"os"
 	"wartank/desktop"
 	"wartank/desktop"
 )
 )
 
 
 func main() {
 func main() {
-
-	app := desktop.NewDesktop()
-	if err := app.Run(); err != nil {
-		log.Printf("main(): in wor desktop, err=\n\t%v", err)
+	desk, err := desktop.NewDesktop()
+	if err != nil {
+		log.Printf("main(): in create IDesktop, err=\n\t%v", err)
+		os.Exit(1)
+	}
+	if err := desk.Run(); err != nil {
+		log.Printf("main(): in work desktop, err=\n\t%v", err)
+		os.Exit(2)
 	}
 	}
 }
 }

+ 181 - 230
desktop/desktop.go

@@ -3,62 +3,65 @@ package desktop
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"os"
 	"time"
 	"time"
 
 
 	"github.com/charmbracelet/bubbles/textinput"
 	"github.com/charmbracelet/bubbles/textinput"
 	tea "github.com/charmbracelet/bubbletea"
 	tea "github.com/charmbracelet/bubbletea"
 
 
 	"wartank/desktop/client_serv"
 	"wartank/desktop/client_serv"
+	"wartank/desktop/win_config"
+	"wartank/desktop/win_default"
+	"wartank/pkg/alias"
+	"wartank/pkg/cons"
+	"wartank/pkg/types"
 )
 )
 
 
 // Desktop -- консольный клиент для вар-танка
 // Desktop -- консольный клиент для вар-танка
 type Desktop struct {
 type Desktop struct {
 	TeaModel
 	TeaModel
-	p           *tea.Program            // Объект приложения
-	client      *client_serv.ClientServ // Клиент подключния к бото-серверу
-	isConnect   bool                    // Признак подключения к бот-серверу
-	isLogin     bool                    // Признак логина на сервер
-	isMakeAdmin bool                    // Признак создания админа
+	winName     alias.AWinName                    // Режим показа на экране
+	p           *tea.Program                      // Объект приложения
+	client      *client_serv.ClientServ           // Клиент подключния к бото-серверу
+	isConnect   bool                              // Признак подключения к бот-серверу
+	isLogin     bool                              // Признак логина на сервер
+	isMakeAdmin bool                              // Признак создания админа
+	dictWin     map[alias.AWinName]types.ITermWin // Список окон показа
 }
 }
 
 
 var desk *Desktop
 var desk *Desktop
 
 
-func NewDesktop() *Desktop {
+func NewDesktop() (*Desktop, error) {
 	sf := &Desktop{
 	sf := &Desktop{
-		client: client_serv.NewClientServ(),
+		client:  client_serv.NewClientServ(),
+		dictWin: make(map[alias.AWinName]types.ITermWin),
 	}
 	}
 	sf.TeaModel = NewModel()
 	sf.TeaModel = NewModel()
-	go sf.tick()
 	sf.p = tea.NewProgram(sf, tea.WithAltScreen(), tea.WithANSICompressor())
 	sf.p = tea.NewProgram(sf, tea.WithAltScreen(), tea.WithANSICompressor())
 	desk = sf
 	desk = sf
-	if _, err := sf.p.Run(); err != nil {
-		fmt.Printf("Опа! здесь какая-то ошибочка: %v", err)
-		os.Exit(1)
+	{ // Создание окон
+		winDefault, err := win_default.NewWinDefault(sf)
+		if err != nil {
+			return nil, fmt.Errorf("NewDesktop(): in create WinDefault, err=\n\t%w", err)
+		}
+		sf.dictWin[winDefault.Name()] = winDefault
+
+		winConfig, err := win_config.NewWinConfig(sf)
+		if err != nil {
+			return nil, fmt.Errorf("NewDesktop(): in create WinConfig, err=\n\t%w", err)
+		}
+		sf.dictWin[winConfig.Name()] = winConfig
 	}
 	}
-	return sf
+	sf.winName = cons.WinDefault
+	go sf.tick()
+	return sf, nil
 }
 }
 
 
-// AMode -- режим обработки клавиш
-type AMode int
-
-const (
-	ModeDefault          AMode = iota
-	ModeConfig                 // Режим конфигурирования
-	ModeConfigServer           // Режим конфигуррования сервера
-	ModeInputHostServer        // Режим ввода хоста сервера
-	ModeInputLoginServer       // Режим ввода логина на сервер
-	ModeInputPassServer        // Режим ввода пароля входа на сервер
-	ModeConnectServer          // Режим подключения к серверу
-	ModeLoginServer            // Режим логина на сервер
-	ModeServerMakeAdmin        // Создать админа для бот-сервера
-)
+// SetWin -- устанавливает главное окно
+func (sf *Desktop) SetWin(winName alias.AWinName) {
+	sf.winName = winName
+}
 
 
 type TeaModel struct {
 type TeaModel struct {
-	choices   []string         // выбранный пункт в списке
-	cursor    int              // позиция курсора в списке
-	selected  map[int]struct{} // что выбрано из списка
-	mode      AMode            // Режим показа на экране
 	textInput textinput.Model
 	textInput textinput.Model
 	host      string // Имя хоста сервера (с портом)
 	host      string // Имя хоста сервера (с портом)
 	login     string // Логин для сервера
 	login     string // Логин для сервера
@@ -73,9 +76,6 @@ func NewModel() TeaModel {
 	ti.Width = 20
 	ti.Width = 20
 
 
 	m := TeaModel{
 	m := TeaModel{
-		// Список дел
-		choices:   []string{"Купить картошку", "Оплатить мобилку", "Заплатить за кредит"},
-		selected:  make(map[int]struct{}),
 		textInput: ti,
 		textInput: ti,
 	}
 	}
 	return m
 	return m
@@ -92,15 +92,18 @@ func (sf *Desktop) tick() {
 
 
 // Run -- запускает в работу консольный клиент
 // Run -- запускает в работу консольный клиент
 func (sf *Desktop) Run() error {
 func (sf *Desktop) Run() error {
+	if _, err := sf.p.Run(); err != nil {
+		return fmt.Errorf("Deskto.Run(): err=\n\t%w", err)
+	}
 	return nil
 	return nil
 }
 }
 
 
-func (sf Desktop) Init() tea.Cmd {
+func (sf *Desktop) Init() tea.Cmd {
 	// Просто вернуть `nil`, т.е. никаких команд
 	// Просто вернуть `nil`, т.е. никаких команд
 	return textinput.Blink
 	return textinput.Blink
 }
 }
 
 
-func (sf Desktop) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+func (sf *Desktop) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	var cmd tea.Cmd
 	var cmd tea.Cmd
 	switch msg := msg.(type) {
 	switch msg := msg.(type) {
 	// Может клавиша нажата
 	// Может клавиша нажата
@@ -110,176 +113,152 @@ func (sf Desktop) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			return sf, tea.Quit
 			return sf, tea.Quit
 		}
 		}
 	}
 	}
-	switch desk.mode {
-	case ModeDefault:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "up", "k": // Если "up" или "k" перемещение курсора вверх
-				if desk.cursor > 0 {
-					desk.cursor--
-				}
-			case "down", "j": // Если "down" или "j" перемещение курсора вниз
-				if desk.cursor < len(desk.choices)-1 {
-					desk.cursor++
-				}
-			case "enter", " ": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
-				_, ok := desk.selected[desk.cursor]
-				if ok {
-					delete(desk.selected, desk.cursor)
-				} else {
-					desk.selected[desk.cursor] = struct{}{}
-				}
-			case "w": // Настройки графического клиента
-				desk.mode = ModeConfig
-			}
-		}
-	case ModeConfig:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Вернуться на шаг назад
-				desk.mode = ModeDefault
-			case "1": // Выбор один
-				desk.mode = ModeConfigServer
-			case "2": // Вход на сервер
-				if !desk.isConnect {
-					desk.mode = ModeConnectServer
-				}
-			case "3": // Логин на сервер
-				if !desk.isLogin {
-					desk.mode = ModeLoginServer
-				}
-			case "4": // Создать админа
-				desk.mode = ModeServerMakeAdmin
-			}
-		}
-	case ModeConfigServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Вернуться на шаг назад
-				desk.mode = ModeConfig
-			case "1": // Выбор хоста сервера
-				desk.mode = ModeInputHostServer
-				desk.textInput.Reset()
-				desk.textInput.Placeholder = "localhost:18050"
-			case "2": // Выбор логина для входа
-				desk.mode = ModeInputLoginServer
-				desk.textInput.Reset()
-				desk.textInput.Placeholder = "login"
-			case "3": // Выбор пароля для входа
-				desk.mode = ModeInputPassServer
-				desk.textInput.Reset()
-				desk.textInput.Placeholder = "pass"
-			}
-		}
-	case ModeInputHostServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Вернуться на шаг назад
-				desk.mode = ModeConfigServer
-			case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
-				desk.host = desk.textInput.Value()
-				desk.mode = ModeConfigServer
-			default:
-				desk.textInput, cmd = desk.textInput.Update(msg)
-			}
-		}
-	case ModeInputLoginServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Вернуться на шаг назад
-				desk.mode = ModeConfigServer
-			case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
-				desk.login = desk.textInput.Value()
-				desk.mode = ModeConfigServer
-			default:
-				desk.textInput, cmd = desk.textInput.Update(msg)
-			}
-		}
-	case ModeInputPassServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Вернуться на шаг назад
-				desk.mode = ModeConfigServer
-			case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
-				desk.pass = desk.textInput.Value()
-				desk.mode = ModeConfigServer
-			default:
-				desk.textInput, cmd = desk.textInput.Update(msg)
-			}
-		}
-	case ModeConnectServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Нажатие "esc" прекращает подключение к серверу
-				desk.mode = ModeConfigServer
-			}
-		}
-	case ModeLoginServer:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Нажатие "esc" прекращает логин на сервер
-				desk.mode = ModeConfig
-			}
-		}
-	case ModeServerMakeAdmin:
-		switch msg := msg.(type) {
-		// Может клавиша нажата
-		case tea.KeyMsg:
-			switch msg.String() { // Да, нажато, а что именно?
-			case "esc": // Нажатие "esc" прекращает логин на сервер
-				desk.mode = ModeConfig
-			}
-		}
+	mod_, cmd_ := sf.dictWin[sf.winName].Update(msg)
+	if mod_ != nil {
+		return mod_, cmd_
 	}
 	}
-	// Возвращает обновлённую модель дляe Bubble Tea runtime для обработки.
-	//Здесь команду возвращать надо.
-
+	/*
+		 case ModeConfigServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Вернуться на шаг назад
+		// 			desk.winName = ModeConfig
+		// 		case "1": // Выбор хоста сервера
+		// 			desk.winName = ModeInputHostServer
+		// 			desk.textInput.Reset()
+		// 			desk.textInput.Placeholder = "localhost:18050"
+		// 		case "2": // Выбор логина для входа
+		// 			desk.winName = ModeInputLoginServer
+		// 			desk.textInput.Reset()
+		// 			desk.textInput.Placeholder = "login"
+		// 		case "3": // Выбор пароля для входа
+		// 			desk.winName = ModeInputPassServer
+		// 			desk.textInput.Reset()
+		// 			desk.textInput.Placeholder = "pass"
+		// 		}
+		// 	}
+		 case ModeInputHostServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Вернуться на шаг назад
+		// 			desk.winName = ModeConfigServer
+		// 		case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
+		// 			desk.host = desk.textInput.Value()
+		// 			desk.winName = ModeConfigServer
+		// 		default:
+		// 			desk.textInput, cmd = desk.textInput.Update(msg)
+		// 		}
+		// 	}
+		 case ModeInputLoginServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Вернуться на шаг назад
+		// 			desk.winName = ModeConfigServer
+		// 		case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
+		// 			desk.login = desk.textInput.Value()
+		// 			desk.winName = ModeConfigServer
+		// 		default:
+		// 			desk.textInput, cmd = desk.textInput.Update(msg)
+		// 		}
+		// 	}
+		 case ModeInputPassServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Вернуться на шаг назад
+		// 			desk.winName = ModeConfigServer
+		// 		case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
+		// 			desk.pass = desk.textInput.Value()
+		// 			desk.winName = ModeConfigServer
+		// 		default:
+		// 			desk.textInput, cmd = desk.textInput.Update(msg)
+		// 		}
+		// 	}
+		 case ModeConnectServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Нажатие "esc" прекращает подключение к серверу
+		// 			desk.winName = ModeConfigServer
+		// 		}
+		// 	}
+		 case ModeLoginServer:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Нажатие "esc" прекращает логин на сервер
+		// 			desk.winName = ModeConfig
+		// 		}
+		// 	}
+		 case ModeServerMakeAdmin:
+		// 	switch msg := msg.(type) {
+		// 	// Может клавиша нажата
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Нажатие "esc" прекращает создание админа
+		// 			desk.winName = ModeConfig
+		// 		}
+		// 	}
+		 case ModeShowMenuBot:
+		 	// Может клавиша нажата
+		 	switch msg := msg.(type) {
+		// 	case tea.KeyMsg:
+		// 		switch msg.String() { // Да, нажато, а что именно?
+		// 		case "esc": // Нажатие "esc" прекращает показ меню
+		// 			desk.winName = ModeConfig
+		// 		}
+		 	}
+		// Возвращает обновлённую модель дляe Bubble Tea runtime для обработки.
+		//Здесь команду возвращать надо.
+	*/
 	return desk, cmd
 	return desk, cmd
 }
 }
 
 
 func (sf Desktop) View() string {
 func (sf Desktop) View() string {
 	s := time.Now().Local().Format("[ WarTank ] = 2006-01-02 15:04:05.000")
 	s := time.Now().Local().Format("[ WarTank ] = 2006-01-02 15:04:05.000")
 	s += fmt.Sprintf("\tХост=%q\tЛогин=%q\tПароль=%q\tПодключено=%v\tЛогин=%v\tЕстьАдмин=%v\n\n", desk.host, desk.login, desk.pass, desk.isConnect, desk.isLogin, desk.isMakeAdmin)
 	s += fmt.Sprintf("\tХост=%q\tЛогин=%q\tПароль=%q\tПодключено=%v\tЛогин=%v\tЕстьАдмин=%v\n\n", desk.host, desk.login, desk.pass, desk.isConnect, desk.isLogin, desk.isMakeAdmin)
-	switch desk.mode {
-	case ModeDefault: // Исходное содержимое
-		s += desk.winDefault()
-	case ModeConfig: // Показать настройки клиента
-		s += desk.winConfig()
-	case ModeConfigServer: // Конфигурация сервера
-		s += desk.winConfigServer()
-	case ModeInputHostServer: // Ввод хоста для сервера
-		s += desk.winHostServer()
-	case ModeInputLoginServer: // Ввод логина для входа на сервер
-		s += desk.winLoginServer()
-	case ModeInputPassServer: // ввод пароля
-		s += desk.winPassServer()
-	case ModeConnectServer: // Режим подключения к серверу
-		s += desk.winConnectServer()
-	case ModeLoginServer: // Режим логина на сервер
-		s += desk.winServerLogin()
-	case ModeServerMakeAdmin: // Режим содания админа на бот-сервере
-		s += desk.winServerMakeAdmin()
+	s += desk.dictWin[desk.winName].View()
+	switch desk.winName {
+	// case ModeConfigServer: // Конфигурация сервера
+	// 	s += desk.winConfigServer()
+	// case ModeInputHostServer: // Ввод хоста для сервера
+	// 	s += desk.winHostServer()
+	// case ModeInputLoginServer: // Ввод логина для входа на сервер
+	// 	s += desk.winLoginServer()
+	// case ModeInputPassServer: // ввод пароля
+	// 	s += desk.winPassServer()
+	// case ModeConnectServer: // Режим подключения к серверу
+	// 	s += desk.winConnectServer()
+	// case ModeLoginServer: // Режим логина на сервер
+	// 	s += desk.winServerLogin()
+	// case ModeServerMakeAdmin: // Режим содания админа на бот-сервере
+	// 	s += desk.winServerMakeAdmin()
+	// case ModeShowMenuBot: // Меню бото-фермы
+	// 	s += desk.winMenuBotoFerma()
 	}
 	}
 
 
 	return s
 	return s
 }
 }
 
 
+/*
+func (sf *Desktop) winMenuBotoFerma() string {
+	s := "=Меню бото-фермы вар-танк=\n\n"
+	s += "[1]-Список ботов\n"
+	s += "[2]-Добавить бота\n"
+	s += "[3]-Удалить бота\n"
+	s += "\n\n[ctrl+q]-выход [esc]-отмена\n"
+	return s
+}
+
 func (sf *Desktop) winServerMakeAdmin() string {
 func (sf *Desktop) winServerMakeAdmin() string {
 	s := "=Создание админа на бот-сервере вар-танк=\n\n"
 	s := "=Создание админа на бот-сервере вар-танк=\n\n"
 	if !desk.isMakeAdmin {
 	if !desk.isMakeAdmin {
@@ -356,42 +335,14 @@ func (sf *Desktop) winConfigServer() string {
 	return s
 	return s
 }
 }
 
 
-func (sf *Desktop) winConfig() string {
-	s := "Настройки клиента\n\n"
-	s += "[1]-Настройки сервера\n"
-	s += "[2]-Вход на сервер\n"
-	s += "[3]-Логин на сервер\n"
-	s += "[4]-Сделать админа на бот-сервере\n"
-	s += "\n\n[ctrl+q]-выход [esc]-назад\n"
-	return s
-}
-
-func (sf *Desktop) winDefault() string {
-	// Заголовок
-	s := "Что нам нужно сделать?\n\n"
-
-	// Перебор по списку выбора
-	for i, choice := range desk.choices {
-
-		// Курсор указывает на этот выбор?
-		cursor := " " // нет курсора
-		if desk.cursor == i {
-			cursor = ">" // есть курсор
-		}
-
-		// Этот элемент выбран?
-		checked := " " // не выбран
-		if _, ok := desk.selected[i]; ok {
-			checked = "+" // выбран!
-		}
-
-		// отрендерить заново
-		s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
-	}
+*/
 
 
-	// Подвал
-	s += "\n\n[ctrl+q]-выход [w]-настройки\n"
+// IsLogin -- возвращает признак логина на сервер ботов
+func (sf *Desktop) IsLogin() bool {
+	return sf.isLogin
+}
 
 
-	// Вернуть строку экрана для отрисовки
-	return s
+// IsConnect -- возвращает признак подключения на сервер ботов
+func (sf *Desktop) IsConnect() bool {
+	return sf.isConnect
 }
 }

+ 86 - 0
desktop/win_config/win_config.go

@@ -0,0 +1,86 @@
+// package win_config -- окно конфигурирования
+package win_config
+
+import (
+	"fmt"
+
+	tea "github.com/charmbracelet/bubbletea"
+
+	"wartank/pkg/alias"
+	"wartank/pkg/cons"
+	"wartank/pkg/types"
+)
+
+// WinConfig -- окно конфигурирования
+type WinConfig struct {
+	desk  types.IDesktop
+	model tea.Model      // Модель для обработки
+	name  alias.AWinName // Имя экрана
+}
+
+// NewWinConfig -- возвращает новое окно конфигурирования
+func NewWinConfig(desk types.IDesktop) (*WinConfig, error) {
+	if desk == nil {
+		return nil, fmt.Errorf("NewWinConfig(): IDesktop==nil")
+	}
+	sf := &WinConfig{
+		desk: desk,
+		name: cons.WinConfig,
+	}
+	return sf, nil
+}
+
+// Name -- возвращает собственное имя
+func (sf *WinConfig) Name() alias.AWinName {
+	return sf.name
+}
+
+func (sf *WinConfig) Init() tea.Cmd {
+	// Просто вернуть `nil`, т.е. никаких команд
+	return nil
+}
+
+// Update -- обрабатывает команду цикла
+func (sf *WinConfig) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+	winName := alias.AWinName("")
+	switch msg := msg.(type) {
+	// Может клавиша нажата
+	case tea.KeyMsg:
+		switch msg.String() { // Да, нажато, а что именно?
+		case "esc": // Вернуться на шаг назад
+			winName = cons.WinDefault
+		case "1": // Выбор один
+			winName = cons.WinConfigServer
+		case "2": // Вход на сервер
+			if !sf.desk.IsConnect() {
+				winName = cons.WinConnectServer
+			}
+		case "3": // Логин на сервер
+			if !sf.desk.IsLogin() {
+				winName = cons.WinLoginServer
+			}
+		case "4": // Создать админа
+			winName = cons.WinServerMakeAdmin
+		case "5": // Показать меню бото-фермы
+			winName = cons.WinShowMenuBot
+		}
+
+	}
+	if winName != "" {
+		sf.desk.SetWin(winName)
+		return sf.model, nil
+	}
+	return nil, nil
+}
+
+// View -- перерисовывание окна по требованию
+func (sf *WinConfig) View() string {
+	s := "Настройки клиента\n\n"
+	s += "[1]-Настройки сервера\n"
+	s += "[2]-Вход на сервер\n"
+	s += "[3]-Логин на сервер\n"
+	s += "[4]-Сделать админа на бот-сервере\n"
+	s += "[5]-Меню бото-фермы\n"
+	s += "\n\n[ctrl+q]-выход [esc]-назад\n"
+	return s
+}

+ 114 - 0
desktop/win_default/win_default.go

@@ -0,0 +1,114 @@
+// package win_default -- окно по умолчанию
+package win_default
+
+import (
+	"fmt"
+
+	tea "github.com/charmbracelet/bubbletea"
+
+	"wartank/pkg/alias"
+	"wartank/pkg/cons"
+	"wartank/pkg/types"
+)
+
+// WinDefault -- окно по умолчанию
+type WinDefault struct {
+	desk     types.IDesktop
+	cursor   int              // позиция курсора в списке
+	choices  []string         // выбранный пункт в списке
+	selected map[int]struct{} // что выбрано из списка
+	model    tea.Model        // Модель для обработки
+	name     alias.AWinName   // Имя экрана
+}
+
+// NewWinDefault -- возвращает новое окно по умолчанию
+func NewWinDefault(desk types.IDesktop) (*WinDefault, error) {
+	if desk == nil {
+		return nil, fmt.Errorf("NewWinDefault(): IDesktop==nil")
+	}
+	sf := &WinDefault{
+		desk: desk,
+		name: cons.WinDefault,
+		// Список дел
+		choices:  []string{"Купить картошку", "Оплатить мобилку", "Заплатить за кредит"},
+		selected: make(map[int]struct{}),
+	}
+	return sf, nil
+}
+
+// Name -- возвращает собственное имя
+func (sf *WinDefault) Name() alias.AWinName {
+	return sf.name
+}
+
+func (sf *WinDefault) Init() tea.Cmd {
+	// Просто вернуть `nil`, т.е. никаких команд
+	return nil
+}
+
+// Update -- обрабатывает команду цикла
+func (sf *WinDefault) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+	isMsg := false
+	switch msg := msg.(type) {
+	// Может клавиша нажата
+	case tea.KeyMsg:
+		switch msg.String() { // Да, нажато, а что именно?
+		case "up", "k": // Если "up" или "k" перемещение курсора вверх
+			if sf.cursor > 0 {
+				sf.cursor--
+				isMsg = true
+			}
+		case "down", "j": // Если "down" или "j" перемещение курсора вниз
+			if sf.cursor < len(sf.choices)-1 {
+				sf.cursor++
+				isMsg = true
+			}
+		case "enter", " ": // Нажатие "enter" или "пробел" переключает выбранный элемент списка
+			_, ok := sf.selected[sf.cursor]
+			if ok {
+				delete(sf.selected, sf.cursor)
+			} else {
+				sf.selected[sf.cursor] = struct{}{}
+			}
+			isMsg = true
+		case "w": // Настройки графического клиента
+			sf.desk.SetWin(cons.WinConfig)
+		}
+	}
+	if isMsg {
+		sf.desk.SetWin(sf.name)
+		return sf.model, nil
+	}
+	return nil, nil
+}
+
+// View -- перерисовывание окна по требованию
+func (sf *WinDefault) View() string {
+	// Заголовок
+	s := "Что нам нужно сделать?\n\n"
+
+	// Перебор по списку выбора
+	for i, choice := range sf.choices {
+
+		// Курсор указывает на этот выбор?
+		cursor := " " // нет курсора
+		if sf.cursor == i {
+			cursor = ">" // есть курсор
+		}
+
+		// Этот элемент выбран?
+		checked := " " // не выбран
+		if _, ok := sf.selected[i]; ok {
+			checked = "+" // выбран!
+		}
+
+		// отрендерить заново
+		s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
+	}
+
+	// Подвал
+	s += "\n\n[ctrl+q]-выход [w]-настройки\n"
+
+	// Вернуть строку экрана для отрисовки
+	return s
+}

+ 2 - 1
dev_serv.sh

@@ -1,5 +1,6 @@
 # Адрес сервер-бота
 # Адрес сервер-бота
-export SERVER_PORT="18050"
+export SERVER_RPC_URL="localhost:18060"
+
 
 
 cd ./bin && \
 cd ./bin && \
 ./dev_serv
 ./dev_serv

+ 3 - 0
pkg/alias/alias.go

@@ -9,3 +9,6 @@ type AMilSec int
 
 
 // ADAmage -- урон от выстрела
 // ADAmage -- урон от выстрела
 type ADamage int
 type ADamage int
+
+// AWinName -- имя окно показа
+type AWinName string

+ 18 - 0
pkg/cons/cons.go

@@ -1,5 +1,9 @@
 package cons
 package cons
 
 
+import (
+	"wartank/pkg/alias"
+)
+
 /*
 /*
 	Содержит константы для работы
 	Содержит константы для работы
 */
 */
@@ -14,3 +18,17 @@ const (
 	TypeMsgResource = 1 // Тип сообщения -- ресурсы
 	TypeMsgResource = 1 // Тип сообщения -- ресурсы
 	TypeMsgArsenal  = 2 // Тип сообщения ресурсы
 	TypeMsgArsenal  = 2 // Тип сообщения ресурсы
 )
 )
+
+const (
+	WinDefault          alias.AWinName = "winDefault"
+	WinConfig           alias.AWinName = "winConfig" // Режим конфигурирования
+	WinConfigServer                                  // Режим конфигуррования сервера
+	WinInputHostServer                               // Режим ввода хоста сервера
+	WinInputLoginServer                              // Режим ввода логина на сервер
+	WinInputPassServer                               // Режим ввода пароля входа на сервер
+	WinConnectServer                                 // Режим подключения к серверу
+	WinLoginServer                                   // Режим логина на сервер
+	WinServerMakeAdmin                               // Создать админа для бот-сервера
+	WinServerMakeUser                                // Создать пользователя для бот-сервера
+	WinShowMenuBot                                   // Показать меню ботов
+)

+ 8 - 9
pkg/types/idesktop.go

@@ -1,14 +1,13 @@
 package types
 package types
 
 
+import "wartank/pkg/alias"
+
 // IDesktop -- интерфейс к десктоп-приложению
 // IDesktop -- интерфейс к десктоп-приложению
 type IDesktop interface {
 type IDesktop interface {
-	IKernel
-	// Store -- возвращает хранилище клиента
-	Store() IStore
-	// Ws -- возвращает веб-сокет до сервера
-	Ws() IWebSocket
-	// Root -- возвращает объект рута
-	Root() IRoot
-	// DictBot -- возвращает словарь ботов игрока
-	DictBot() IDictBot
+	// SetWin -- устанавливает окно для показа
+	SetWin(alias.AWinName)
+	// IsConnect -- возвращает признак подключенности к серверу ботов
+	IsConnect() bool
+	// IsLogin -- возвращает признак логина на сервере ботов
+	IsLogin() bool
 }
 }

+ 14 - 0
pkg/types/iterm_win.go

@@ -0,0 +1,14 @@
+package types
+
+import (
+	"wartank/pkg/alias"
+
+	tea "github.com/charmbracelet/bubbletea"
+)
+
+// ITermWin -- терминальное окно для показа
+type ITermWin interface {
+	tea.Model
+	// Name -- имя окна
+	Name() alias.AWinName
+}

+ 8 - 1
server/serv_rpc/serv_rpc.go

@@ -7,6 +7,7 @@ import (
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"net/rpc"
 	"net/rpc"
+	"os"
 
 
 	"wartank/pkg/types"
 	"wartank/pkg/types"
 )
 )
@@ -30,7 +31,13 @@ func NewServRpc(serv types.IServer) (*ServRpc, error) {
 func (sf *ServRpc) Run() {
 func (sf *ServRpc) Run() {
 	rpc.Register(sf)
 	rpc.Register(sf)
 	rpc.HandleHTTP()
 	rpc.HandleHTTP()
-	lister, err := net.Listen("tcp", ":18050")
+	url := os.Getenv("SERVER_RPC_URL")
+	if url == "" {
+		log.Printf("ServRpc.Run(): env SERVER_RPC_URL not set\n")
+		return
+	}
+	log.Printf("SERVER_RPC_URL=%v\n", url)
+	lister, err := net.Listen("tcp", url)
 	if err != nil {
 	if err != nil {
 		log.Printf("NewServRc(): listen error, err=\n\t%v\n", err)
 		log.Printf("NewServRc(): listen error, err=\n\t%v\n", err)
 		sf.serv.CancelApp()
 		sf.serv.CancelApp()