Explorar o código

SVI Переделка под WUI

SVI hai 1 ano
pai
achega
0f4c25a591
Modificáronse 40 ficheiros con 1471 adicións e 107 borrados
  1. 10 10
      app/lev3/serv_web/serv_web.go
  2. 0 0
      app/lev3/serv_web/web_gui/btn_list_bot/block_list_bot.html
  3. 87 0
      app/lev3/serv_web/web_gui/btn_list_bot/btn_list_bot.go
  4. 2 2
      app/lev3/serv_web/web_gui/btn_login/block_login.html
  5. 81 0
      app/lev3/serv_web/web_gui/btn_login/btn_login.go
  6. 2 3
      app/lev3/serv_web/web_gui/index.tmpl.html
  7. 6 0
      app/lev3/serv_web/web_gui/page_bot_list/block_list_bot.html
  8. 1 1
      app/lev3/serv_web/web_gui/page_bot_list/page_bot_list.go
  9. 15 23
      app/lev3/serv_web/web_gui/page_bot_show/bot_show.tmpl.html
  10. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_attack/btn_attack.go
  11. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_bron/btn_bron.go
  12. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_fuel/btn_fuel.go
  13. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_fyne/btn_fyne.go
  14. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_glory/btn_glory.go
  15. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_gold/btn_gold.go
  16. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_hard/btn_hard.go
  17. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_count/btn_mine_count.go
  18. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_level/btn_mine_level.go
  19. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode/btn_mine_mode.go
  20. 49 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_mine_type/btn_mine_type.go
  21. 48 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_power/btn_power.go
  22. 51 0
      app/lev3/serv_web/web_gui/page_bot_show/btn_silver/btn_silver.go
  23. 61 0
      app/lev3/serv_web/web_gui/page_bot_show/dict_bot/dict_bot.go
  24. 75 21
      app/lev3/serv_web/web_gui/page_bot_show/page_bot_show.go
  25. 15 27
      app/lev3/serv_web/web_gui/web_gui.go
  26. 1 0
      cmd/server/main.go
  27. 1 1
      go.mod
  28. 2 2
      go.sum
  29. 171 0
      vendor/github.com/gofiber/fiber/v2/middleware/adaptor/adaptor.go
  30. 159 0
      vendor/github.com/valyala/fasthttp/fasthttpadaptor/adaptor.go
  31. 11 0
      vendor/github.com/valyala/fasthttp/fasthttpadaptor/b2s_new.go
  32. 14 0
      vendor/github.com/valyala/fasthttp/fasthttpadaptor/b2s_old.go
  33. 70 0
      vendor/github.com/valyala/fasthttp/fasthttpadaptor/request.go
  34. 1 1
      vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_serv_http/btn_modules/btn_modules.go
  35. 1 1
      vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_serv_http/btn_monolit/btn_monolit.go
  36. 43 8
      vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_wui/mod_wui.go
  37. 4 4
      vendor/gitp78su.ipnodns.ru/svi/kern/wui/wbutton/wbutton.go
  38. 1 1
      vendor/gitp78su.ipnodns.ru/svi/kern/wui/wtypes/iwui_button.go
  39. 1 1
      vendor/gitp78su.ipnodns.ru/svi/kern/wui/wui.go
  40. 3 1
      vendor/modules.txt

+ 10 - 10
app/lev3/serv_web/serv_web.go

@@ -2,20 +2,19 @@
 package serv_web
 
 import (
-	"fmt"
+	// "fmt"
 
-	"github.com/gofiber/fiber/v2"
+	// "github.com/gofiber/fiber/v2"
 	. "gitp78su.ipnodns.ru/svi/kern"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
 
-	. "wartank/app/lev0/alias"
+	// . "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev3/serv_web/web_api"
 	"wartank/app/lev3/serv_web/web_gui"
 )
 
 type СервВеб struct {
-	конт IKernelCtx
 	серв ИПриложение
 	лог  ILogBuf
 }
@@ -27,20 +26,20 @@ func НовСервВеб() *СервВеб {
 	лог.Info("НовСервВеб()\n")
 
 	сам := &СервВеб{
-		конт: конт,
 		серв: конт.Get("мод_сервер").Val().(ИПриложение),
 		лог:  лог,
 	}
 
-	файбер := конт.Get("fiberApp").Val().(*fiber.App)
-	файбер.Get("/list_bot/get", сам.getListBot)
-	файбер.Post("/list_bot/add", сам.постБотНов)
-	файбер.Post("/bot/stat", сам.постБотСтат)
+	// файбер := конт.Get("fiberApp").Val().(*fiber.App)
+	// файбер.Get("/list_bot/get", сам.getListBot)
+	// файбер.Post("/list_bot/add", сам.постБотНов)
+	// файбер.Post("/bot/stat", сам.постБотСтат)
 	_ = web_api.НовВебАпи()
-	_ = web_gui.НовВебГуи(сам.конт)
+	_ = web_gui.НовВебГуи()
 	return сам
 }
 
+/*
 type ПостБотСтат struct {
 	Номер АБотНомер `form:"bot_number"`
 }
@@ -164,3 +163,4 @@ func (сам *СервВеб) getListBot(кнт *fiber.Ctx) error {
 	}
 	return кнт.JSON(рез)
 }
+*/

+ 0 - 0
app/lev3/serv_web/web_gui/page_bot_list/page_list_bot.tmpl.html → app/lev3/serv_web/web_gui/btn_list_bot/block_list_bot.html


+ 87 - 0
app/lev3/serv_web/web_gui/btn_list_bot/btn_list_bot.go

@@ -0,0 +1,87 @@
+// package btn_list_bot -- кнопка списка ботов
+package btn_list_bot
+
+import (
+	_ "embed"
+	"fmt"
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev1/web_render"
+	"wartank/app/lev3/serv_web/web_gui/btn_login"
+)
+
+// КнпЛогин -- кнопка списка ботов на сайте
+type КнпСписБот struct {
+	конт     IKernelCtx
+	прилож   ИПриложение
+	кнп      IWuiButton
+	кнпЛогин *btn_login.КнпЛогин
+	рендер   ИВебРендер
+}
+
+//go:embed block_list_bot.html
+var стрСписок string
+
+// НовКнпСписБот -- возвращает новую кнопку списка ботов
+func НовКнпСписБот(кнпЛогин *btn_login.КнпЛогин) *КнпСписБот {
+	Hassert(кнпЛогин != nil, "НовКнпСписБот(): кнпЛогин==nil")
+	конт := GetKernelCtx()
+	сам := &КнпСписБот{
+		конт:     конт,
+		прилож:   конт.Get("мод_сервер").Val().(ИПриложение),
+		кнпЛогин: кнпЛогин,
+		рендер:   web_render.НовВебРендер(стрСписок),
+	}
+	сам.кнп = wui.NewWuiButton("_Список ботов_", сам.КликСписБот)
+	сам.кнп.Hx().Target().Set("#main")
+	конт.Set("кнп_спис_бот", сам, "Кнопка показа списка ботов")
+	return сам
+}
+
+// Html -- возвращает HTML-представление кнопки
+func (сам *КнпСписБот) Html() string {
+	return сам.кнп.Html()
+}
+
+var стрСсылкаШаблон = `
+<div>
+    <a class="btn btn-primary" hx-post="/gui/bot/{.id}/show" hx-target="#main">{.name}</a><br><br>
+</div>
+`
+
+// Событие клика по кнопке
+func (сам *КнпСписБот) КликСписБот(слв map[string]string) string {
+	_еслиЛогин := сам.конт.Get("еслиЛогин")
+	if _еслиЛогин == nil { // нет логина
+		return сам.кнпЛогин.Html()
+	}
+	токенКонт := _еслиЛогин.Val().(string)
+	токенСвой_ := сам.кнп.Hx().Vals().Get("токен")
+	if токенСвой_ == nil {
+		сам.кнп.Hx().Vals().Set("токен", токенКонт)
+	}
+	токенСвой := сам.кнп.Hx().Vals().Get("токен").(string)
+	if токенСвой != токенКонт {
+		return сам.кнпЛогин.Html()
+	}
+
+	списокБотов := сам.прилож.ServBots().ListBot()
+	стрСписБот := ""
+	for _, бот := range списокБотов {
+		стрНомер := fmt.Sprint(бот.Номер())
+		стрНомер = strings.ReplaceAll(стрСсылкаШаблон, "{.id}", стрНомер)
+		стрНомер = strings.ReplaceAll(стрНомер, "{.name}", бот.Имя())
+		стрСписБот += стрНомер + "<br>\n"
+	}
+
+	сам.рендер.Доб("{.list_bot}", стрСписБот)
+	стрРез := сам.рендер.Получ()
+	return стрРез
+}

+ 2 - 2
app/lev3/serv_web/web_gui/block_login.html → app/lev3/serv_web/web_gui/btn_login/block_login.html

@@ -2,7 +2,7 @@
 <h1>Логин на сервер</h1>
 
 <!-- Форма логина -->
-<form class="container border rounded">
+<form class="container border rounded" name="form_login">
     <div class="row p-3">
         <label for="login" class="col-2 form-label text-right">Логин:</label>
         <input type="text" id="login" name="login" id="login" class="col form-control" placeholder="admin"><br><br>
@@ -16,7 +16,7 @@
 
     <div class="row p-3">
         <label class="col">:</label>
-        <input type="submit" class="col-2 btn btn-primary" hx-post="/gui/login/make" hx-target="#main" value="Вход">
+        <input type="submit" class="col-2 btn btn-primary" {.id} hx-target="#main" value="Вход">
     </div>
 </form>
 

+ 81 - 0
app/lev3/serv_web/web_gui/btn_login/btn_login.go

@@ -0,0 +1,81 @@
+// package btn_login -- кнопка логина
+package btn_login
+
+import (
+	"crypto/rand"
+	_ "embed"
+	"strings"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+)
+
+// КнпЛогин -- кнопка логина на сайте
+type КнпЛогин struct {
+	Кнп  IWuiButton
+	конт IKernelCtx
+}
+
+// НовКнпЛогин -- возвращает новую кнопку логина
+func НовКнпЛогин() *КнпЛогин {
+	sf := &КнпЛогин{
+		конт: GetKernelCtx(),
+	}
+	sf.Кнп = wui.NewWuiButton("Логин", sf.КликЛогин)
+	sf.Кнп.Hx().Target().Set("#main")
+	return sf
+}
+
+// Html -- возвращает HTML-представление кнопки
+func (sf *КнпЛогин) Html() string {
+	return sf.Кнп.Html()
+}
+
+//go:embed block_login.html
+var стрБлокЛогин string
+
+type ИВебКнопка interface {
+	Html() string
+	КликСписБот(map[string]string) string
+}
+
+// Событие клика по кнопке
+//
+//	FIXME: здесь не прокидываются кукисы. Надо в form вставлять скрытое поле 'token'
+func (сам *КнпЛогин) КликЛогин(слв map[string]string) string {
+	стрРез := strings.ReplaceAll(стрБлокЛогин, "{.id}", сам.Кнп.Hx().Url().String())
+	кнпСписБот := сам.конт.Get("кнп_спис_бот").Val().(ИВебКнопка)
+	еслиЛогин := сам.конт.Get("еслиЛогин")
+	if еслиЛогин != nil {
+		токенЗапр := еслиЛогин.Val().(string)
+		токенСвой_ := сам.Кнп.Hx().Vals().Get("токен")
+		if токенСвой_ == nil {
+			return стрРез
+		}
+		токенСвой := токенСвой_.(string)
+		if токенСвой != токенЗапр {
+			return стрРез
+		}
+		return кнпСписБот.КликСписБот(слв)
+	}
+	логин, еслиОк := слв["login"]
+	if !еслиОк {
+		return стрРез
+	}
+	if логин != "svi" {
+		return стрРез
+	}
+	пароль, еслиОк := слв["password"]
+	if !еслиОк {
+		return стрРез
+	}
+	if пароль != "Lera_07091978" {
+		return стрРез
+	}
+	токен := rand.Text()
+	сам.Кнп.Hx().Vals().Set("токен", токен)
+	сам.конт.Set("еслиЛогин", токен, "С какого IP зашёл царь")
+	return кнпСписБот.КликСписБот(слв)
+}

+ 2 - 3
app/lev3/serv_web/web_gui/index.tmpl.html

@@ -22,9 +22,8 @@
                     </div>
                     <div class="col">
                         <a class="btn btn-primary" href="/">Главная</a>
-                        <a class="btn btn-primary" hx-post="/gui/bot/list_show" hx-target="#main">Список ботов</a>
                         {.btn_lst_bot}
-                        <a class="btn btn-primary" hx-post="/monolit" hx-target="body">Монолит</a>
+                        <a class="btn btn-primary" hx-get="/monolit" hx-target="body">Монолит</a>
                     </div>
                 </div>
             </div>
@@ -35,7 +34,7 @@
             </div>
             <br>
         </div>
-        <div id="main" hx-post="/gui/login_show" hx-trigger="load">
+        <div id="main" {.url_login} hx-trigger="load">
             {.main}
         </div>
         <div id="foother" class="container-fluid border rounded text-info bg-dark">

+ 6 - 0
app/lev3/serv_web/web_gui/page_bot_list/block_list_bot.html

@@ -0,0 +1,6 @@
+<div id="main" hx-swap-oob="true">
+   <h1>Страница списка ботов вартанк</h1>
+   {.list_bot}
+   <br>
+   <a class="btn btn-primary" hx-post="/gui/bot/add/show" hx-target="#main">Добавить бота</a>
+</div>

+ 1 - 1
app/lev3/serv_web/web_gui/page_bot_list/page_bot_list.go

@@ -23,7 +23,7 @@ type СтраницаСписокБотов struct {
 	рендер ИВебРендер
 }
 
-//go:embed page_list_bot.tmpl.html
+//go:embed block_list_bot.html
 var стрСписок string
 
 // НовСтраницаСписокБотов

+ 15 - 23
app/lev3/serv_web/web_gui/page_bot_show/bot_show.tmpl.html

@@ -9,12 +9,7 @@
             <h3>Статистика</h3>
         </div>
         <div class="card-body">
-            <p class="card-text" hx-post="/api/bot/{.id}/angar/gold" hx-trigger="every 5s">Золото: {.золото}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/angar/silver" hx-trigger="every 5s">Серебро: {.серебро}
-            </p>
-            <p class="card-text" hx-post="/api/bot/{.id}/tank/fuel" hx-trigger="every 5s">Топливо: {.топливо}
-            </p>
-            <p class="card-text">Слава: {.слава}</p>
+            {.block_gold} {.block_silver}<br><br> {.block_fuel} {.block_glory}
         </div>
     </div>
 
@@ -25,12 +20,11 @@
             <h3>Сила танка</h3>
         </div>
         <div class="card-body">
-            <p class="card-text" hx-post="/api/bot/{.id}/stat_attack" hx-trigger="every 5s">[Атака: {.атака}]</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/stat_bron" hx-trigger="every 5s">_Броня: {.броня}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/stat_fyne" hx-trigger="every 5s">_Точность: {.точность}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/stat_prot" hx-trigger="every 5s">_Прочность: {.прочность}</p>
+            {.attack} {.броня}<br><br> {.точность} {.прочность}
+        </div>
+        <div class="card-footer border-success">
+            {.мощь}
         </div>
-        <div class="card-footer border-success" hx-post="/api/bot/{.id}/stat_power" hx-trigger="every 5s">_Мощность: {.мощь}</div>
     </div>
 
 
@@ -41,14 +35,8 @@
             <h3>Шахта</h3>
         </div>
         <div class="card-body">
-            <p class="card-text" hx-post="/api/bot/{.id}/mine/level" hx-trigger="every 5s">[Уровень:
-                {.шахта_уровень}]</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/mine/mode" hx-trigger="every 5s">Режим: {.шахта_режим}
-            </p>
-            <p class="card-text" hx-post="/api/bot/{.id}/mine/count_product" hx-trigger="every 5s">Кол:
-                {.шахта_сделать_кол}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/mine/name_product" hx-trigger="every 5s">Тип:
-                {.шахта_сделать_назв}</p>
+            {.шахта_уровень} {.шахта_режим}<br><br>
+            {.шахта_сделать_кол} {.шахта_сделать_назв}
         </div>
         <div class="card-footer border-success" hx-post="/api/bot/{.id}/mine/back_time" hx-trigger="every 5s">
             Время: {.шахта_сделать_время}</div>
@@ -90,10 +78,14 @@
                 {.оружейная_работа}</p>
             <p class="card-text" hx-post="/api/bot/{.id}/arsenal/name_product" hx-trigger="every 5s">Тип:
                 {.оружейная_режим}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/kumul" hx-trigger="every 5s">Кумул: {.оружейная_кумул}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/bron" hx-trigger="every 5s">Бронебойки: {.оружейная_бронебойки}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/fugas" hx-trigger="every 5s">Фугасы: {.оружейная_фугасы}</p>
-            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/remka" hx-trigger="every 5s">Ремки: {.оружейная_ремки}</p>
+            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/kumul" hx-trigger="every 5s">Кумул: {.оружейная_кумул}
+            </p>
+            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/bron" hx-trigger="every 5s">Бронебойки:
+                {.оружейная_бронебойки}</p>
+            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/fugas" hx-trigger="every 5s">Фугасы:
+                {.оружейная_фугасы}</p>
+            <p class="card-text" hx-post="/api/bot/{.id}/arsenal/remka" hx-trigger="every 5s">Ремки: {.оружейная_ремки}
+            </p>
         </div>
         <div class="card-footer border-success" hx-post="/api/bot/{.id}/arsenal/back_time" hx-trigger="every 5s">
             Время: {.оружейная_время}</div>

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_attack/btn_attack.go

@@ -0,0 +1,48 @@
+package btn_attack
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnAttack struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnAttack() *BtnAttack {
+	ctx := GetKernelCtx()
+	sf := &BtnAttack{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Атака:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnAttack) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnAttack) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Атака: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Атака: %v]", bot.Стата().Атака().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_bron/btn_bron.go

@@ -0,0 +1,48 @@
+package btn_bron
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnBron struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnBron() *BtnBron {
+	ctx := GetKernelCtx()
+	sf := &BtnBron{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Броня:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnBron) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnBron) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Броня: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Броня: %v]", bot.Стата().Броня().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_fuel/btn_fuel.go

@@ -0,0 +1,49 @@
+package btn_fuel
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnFuel struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnFuel() *BtnFuel {
+	ctx := GetKernelCtx()
+	sf := &BtnFuel{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Топливо:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnFuel) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnFuel) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Топливо: не бота")
+		return sf.Btn.Html()
+	}
+	ангар := bot.КонтБот().Get("ангар").Val().(ИАренаАнгар)
+	strOut := fmt.Sprintf("[Топливо: %v]", ангар.Топливо().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_fyne/btn_fyne.go

@@ -0,0 +1,48 @@
+package btn_fyne
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnFyne struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnFyne() *BtnFyne {
+	ctx := GetKernelCtx()
+	sf := &BtnFyne{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Точность:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnFyne) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnFyne) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Точность: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Точность: %v]", bot.Стата().Точность().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_glory/btn_glory.go

@@ -0,0 +1,48 @@
+package btn_glory
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnGlory struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnGlory() *BtnGlory {
+	ctx := GetKernelCtx()
+	sf := &BtnGlory{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Слава:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnGlory) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnGlory) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Слава: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Слава: %v]", bot.Стата().Слава().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_gold/btn_gold.go

@@ -0,0 +1,49 @@
+package btn_gold
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnGold struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnGold() *BtnGold {
+	ctx := GetKernelCtx()
+	sf := &BtnGold{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Золото:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnGold) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnGold) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot==nil{
+		sf.Btn.Text().Set("Золото: не бота")
+		return sf.Btn.Html()
+	}
+	ангар := bot.КонтБот().Get("ангар").Val().(ИАренаАнгар)
+	strOut := fmt.Sprintf("[Золото: %v]", ангар.Золото().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_hard/btn_hard.go

@@ -0,0 +1,48 @@
+package btn_hard
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnHard struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnHard() *BtnHard {
+	ctx := GetKernelCtx()
+	sf := &BtnHard{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Прочность:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnHard) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnHard) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Прочность: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Прочность: %v]", bot.Стата().Прочность().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_count/btn_mine_count.go

@@ -0,0 +1,49 @@
+package btn_mine_count
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnMineCount struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnMineCount() *BtnMineCount {
+	ctx := GetKernelCtx()
+	sf := &BtnMineCount{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Кол-во:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnMineCount) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnMineCount) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Кол-во: не бота")
+		return sf.Btn.Html()
+	}
+	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
+	strOut := fmt.Sprintf("[Кол-во: %v]", шахта.ПродуктСейчас().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_level/btn_mine_level.go

@@ -0,0 +1,49 @@
+package btn_mine_level
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnMineLevel struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnMineLevel() *BtnMineLevel {
+	ctx := GetKernelCtx()
+	sf := &BtnMineLevel{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Уровень:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnMineLevel) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnMineLevel) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Уровень: не бота")
+		return sf.Btn.Html()
+	}
+	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
+	strOut := fmt.Sprintf("[Уровень: %v]", шахта.Уровень().Получ())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode/btn_mine_mode.go

@@ -0,0 +1,49 @@
+package btn_mine_mode
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnMineMode struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnMineMode() *BtnMineMode {
+	ctx := GetKernelCtx()
+	sf := &BtnMineMode{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Режим:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnMineMode) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnMineMode) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Режим: не бота")
+		return sf.Btn.Html()
+	}
+	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
+	strOut := fmt.Sprintf("[Режим: %v]", шахта.АренаСостояние().Получ())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 49 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_mine_type/btn_mine_type.go

@@ -0,0 +1,49 @@
+package btn_mine_type
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnMineType struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnMineType() *BtnMineType {
+	ctx := GetKernelCtx()
+	sf := &BtnMineType{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Тип:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnMineType) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnMineType) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Тип: не бота")
+		return sf.Btn.Html()
+	}
+	шахта := bot.КонтБот().Get("шахта").Val().(ИАренаШахта)
+	strOut := fmt.Sprintf("[Тип: %v]", шахта.ПродуктСейчас().Имя())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 48 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_power/btn_power.go

@@ -0,0 +1,48 @@
+package btn_power
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnPower struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnPower() *BtnPower {
+	ctx := GetKernelCtx()
+	sf := &BtnPower{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+	}
+	sf.Btn = wui.NewWuiButton("Мощь:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnPower) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnPower) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot == nil {
+		sf.Btn.Text().Set("Мощь: не бота")
+		return sf.Btn.Html()
+	}
+	strOut := fmt.Sprintf("[Мощь: %v]", bot.Стата().Мощь().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 51 - 0
app/lev3/serv_web/web_gui/page_bot_show/btn_silver/btn_silver.go

@@ -0,0 +1,51 @@
+// package btn_silver
+package btn_silver
+
+import (
+	"fmt"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+	"gitp78su.ipnodns.ru/svi/kern/wui"
+	. "gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
+
+	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/dict_bot"
+)
+
+type BtnSilver struct {
+	ctx     IKernelCtx
+	Btn     IWuiButton
+	app     ИПриложение
+	dictBot *dict_bot.DictBot
+}
+
+func NewBtnSilver() *BtnSilver {
+	ctx := GetKernelCtx()
+	sf := &BtnSilver{
+		ctx:     ctx,
+		app:     ctx.Get("мод_сервер").Val().(ИПриложение),
+		dictBot: dict_bot.GetDictBot(),
+
+	}
+	sf.Btn = wui.NewWuiButton("Серебро:", sf.click)
+	sf.Btn.Hx().Trigger().Set("every 5s")
+	sf.Btn.Hx().Swap().Set("outerHTML")
+	return sf
+}
+
+func (sf *BtnSilver) Html() string {
+	return sf.Btn.Html()
+}
+
+func (sf *BtnSilver) click(dict map[string]string) string {
+	bot := sf.dictBot.Get(dict)
+	if bot==nil{
+		sf.Btn.Text().Set("Серебро: не бота")
+		return sf.Btn.Html()
+	}
+	ангар := bot.КонтБот().Get("ангар").Val().(ИАренаАнгар)
+	strOut := fmt.Sprintf("[Серебро: %v]", ангар.Серебро().ЗначСтр())
+	sf.Btn.Text().Set(strOut)
+	return sf.Btn.Html()
+}

+ 61 - 0
app/lev3/serv_web/web_gui/page_bot_show/dict_bot/dict_bot.go

@@ -0,0 +1,61 @@
+// package dict_bot -- костыль для кеширования ботов
+package dict_bot
+
+import (
+	"strconv"
+	"sync"
+
+	. "gitp78su.ipnodns.ru/svi/kern"
+	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
+
+	"wartank/app/lev0/alias"
+	. "wartank/app/lev0/types"
+)
+
+type DictBot struct {
+	sync.Mutex
+	ctx  IKernelCtx
+	dict map[string]ИБот
+	app  ИПриложение
+}
+
+var (
+	sf    *DictBot
+	block sync.Mutex
+)
+
+func GetDictBot() *DictBot {
+	block.Lock()
+	defer block.Unlock()
+	if sf != nil {
+		return sf
+	}
+	ctx := GetKernelCtx()
+	sf := &DictBot{
+		ctx:  ctx,
+		dict: map[string]ИБот{},
+		app:  ctx.Get("мод_сервер").Val().(ИПриложение),
+	}
+	return sf
+}
+
+func (sf *DictBot) Get(dict map[string]string) ИБот {
+	strId, isOk := dict["id"]
+	if !isOk {
+		return nil
+	}
+	bot, isOk := sf.dict[strId]
+	if isOk {
+		return bot
+	}
+	iId, err := strconv.Atoi(strId)
+	if err != nil {
+		return nil
+	}
+	бот := sf.app.ServBots().Get(alias.АБотНомер(iId))
+	if бот == nil {
+		return nil
+	}
+	sf.dict[strId] = бот
+	return бот
+}

+ 75 - 21
app/lev3/serv_web/web_gui/page_bot_show/page_bot_show.go

@@ -14,13 +14,39 @@ import (
 	. "wartank/app/lev0/alias"
 	. "wartank/app/lev0/types"
 	"wartank/app/lev1/web_render"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_attack"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_bron"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_fuel"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_fyne"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_glory"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_gold"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_hard"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_count"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_level"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_mode"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_mine_type"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_power"
+	"wartank/app/lev3/serv_web/web_gui/page_bot_show/btn_silver"
 )
 
 // СтраницаБотПоказать -- страница показа бота
 type СтраницаБотПоказать struct {
-	лог    ILogBuf
-	прилож ИПриложение
-	рендер ИВебРендер
+	лог             ILogBuf
+	прилож          ИПриложение
+	рендер          ИВебРендер
+	кнпЗолото       *btn_gold.BtnGold
+	кнпСеребро      *btn_silver.BtnSilver
+	кнпТопливо      *btn_fuel.BtnFuel
+	кнпСлава        *btn_glory.BtnGlory
+	кнпАтака        *btn_attack.BtnAttack
+	кнпБроня        *btn_bron.BtnBron
+	кнпТочность     *btn_fyne.BtnFyne
+	кнпПрочность    *btn_hard.BtnHard
+	кнпМощь         *btn_power.BtnPower
+	кнпШахтаУровень *btn_mine_level.BtnMineLevel
+	кнпШахтаРежим    *btn_mine_mode.BtnMineMode
+	кнпШахтаПродукт *btn_mine_count.BtnMineCount
+	кнпШахтаТип *btn_mine_type.BtnMineType
 }
 
 //go:embed bot_show.tmpl.html
@@ -31,9 +57,22 @@ func НовСтраницаБотПоказать(конт IKernelCtx) *Стра
 	лог := NewLogBuf()
 	Hassert(конт != nil, "НовСтраницаБотПоказать(): ИЯдроКонтекст==nil")
 	сам := &СтраницаБотПоказать{
-		лог:    лог,
-		прилож: конт.Get("мод_сервер").Val().(ИПриложение),
-		рендер: web_render.НовВебРендер(стрБотПоказать),
+		лог:             лог,
+		прилож:          конт.Get("мод_сервер").Val().(ИПриложение),
+		рендер:          web_render.НовВебРендер(стрБотПоказать),
+		кнпЗолото:       btn_gold.NewBtnGold(),
+		кнпСеребро:      btn_silver.NewBtnSilver(),
+		кнпТопливо:      btn_fuel.NewBtnFuel(),
+		кнпСлава:        btn_glory.NewBtnGlory(),
+		кнпАтака:        btn_attack.NewBtnAttack(),
+		кнпБроня:        btn_bron.NewBtnBron(),
+		кнпТочность:     btn_fyne.NewBtnFyne(),
+		кнпПрочность:    btn_hard.NewBtnHard(),
+		кнпМощь:         btn_power.NewBtnPower(),
+		кнпШахтаУровень: btn_mine_level.NewBtnMineLevel(),
+		кнпШахтаРежим: btn_mine_mode.NewBtnMineMode(),
+		кнпШахтаПродукт: btn_mine_count.NewBtnMineCount(),
+		кнпШахтаТип: btn_mine_type.NewBtnMineType(),
 	}
 	файбер := конт.Get("fiberApp").Val().(*fiber.App)
 	файбер.Post("/gui/bot/:id/show", сам.гетБотПоказ)
@@ -52,35 +91,50 @@ func (сам *СтраницаБотПоказать) гетБотПоказ(к
 		return кнт.Redirect("/gui/bot", http.StatusSeeOther)
 	}
 	ботНомер := АБотНомер(иНомер)
+
 	сам.лог.Debug("СтраницаБотПоказать.гетБотПоказ(): ботНомер=%d\n", стрНомер)
 	бот := сам.прилож.ServBots().Get(ботНомер)
 	if бот == nil {
 		return кнт.Redirect("/gui/bot", http.StatusSeeOther)
 	}
 	{ // Глобальные показатели
-		ангар := бот.КонтБот().Get("ангар").Val().(ИАренаАнгар)
+		сам.кнпЗолото.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпСеребро.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпТопливо.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпСлава.Btn.Hx().Vals().Set("id", иНомер)
+
 		сам.рендер.Доб("{.имя}", бот.Имя())
 		номер := бот.Номер()
 		сам.рендер.Доб("{.id}", номер)
-		сам.рендер.Доб("{.золото}", ангар.Золото().ЗначСтр())
-		сам.рендер.Доб("{.серебро}", ангар.Серебро().ЗначСтр())
-		сам.рендер.Доб("{.топливо}", ангар.Топливо().ЗначСтр())
-		сам.рендер.Доб("{.слава}", бот.Стата().Слава())
+		сам.рендер.Доб("{.block_gold}", сам.кнпЗолото.Html())
+		сам.рендер.Доб("{.block_silver}", сам.кнпСеребро.Html())
+		сам.рендер.Доб("{.block_fuel}", сам.кнпТопливо.Html())
+		сам.рендер.Доб("{.block_glory}", сам.кнпСлава.Html())
 	}
 	{ // Сила танка
-		стата := бот.Стата()
-		сам.рендер.Доб("{.атака}", стата.Атака().ЗначСтр())
-		сам.рендер.Доб("{.броня}", стата.Броня().ЗначСтр())
-		сам.рендер.Доб("{.точность}", стата.Точность().ЗначСтр())
-		сам.рендер.Доб("{.прочность}", стата.Прочность().ЗначСтр())
-		сам.рендер.Доб("{.мощь}", стата.Мощь().ЗначСтр())
+		сам.кнпАтака.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпБроня.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпТочность.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпПрочность.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпМощь.Btn.Hx().Vals().Set("id", иНомер)
+
+		сам.рендер.Доб("{.attack}", сам.кнпАтака.Html())
+		сам.рендер.Доб("{.броня}", сам.кнпБроня.Html())
+		сам.рендер.Доб("{.точность}", сам.кнпТочность.Html())
+		сам.рендер.Доб("{.прочность}", сам.кнпПрочность.Html())
+		сам.рендер.Доб("{.мощь}", сам.кнпМощь.Html())
 	}
 	{ // Шахта
+		сам.кнпШахтаУровень.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпШахтаРежим.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпШахтаПродукт.Btn.Hx().Vals().Set("id", иНомер)
+		сам.кнпШахтаТип.Btn.Hx().Vals().Set("id", иНомер)
+
 		шахта := бот.КонтБот().Get("шахта").Val().(ИАренаШахта)
-		сам.рендер.Доб("{.шахта_уровень}", шахта.Уровень().Получ())
-		сам.рендер.Доб("{.шахта_режим}", шахта.АренаСостояние().Получ())
-		сам.рендер.Доб("{.шахта_сделать_кол}", шахта.ПродуктСейчас().ЗначСтр())
-		сам.рендер.Доб("{.шахта_сделать_назв}", шахта.ПродуктСейчас().Имя())
+		сам.рендер.Доб("{.шахта_уровень}", сам.кнпШахтаУровень.Html())
+		сам.рендер.Доб("{.шахта_режим}", сам.кнпШахтаРежим.Html())
+		сам.рендер.Доб("{.шахта_сделать_кол}", сам.кнпШахтаПродукт.Html())
+		сам.рендер.Доб("{.шахта_сделать_назв}", сам.кнпШахтаТип.Html())
 		сам.рендер.Доб("{.шахта_сделать_время}", шахта.ПродуктВремяСейчас())
 	}
 	{ // Полигон

+ 15 - 27
app/lev3/serv_web/web_gui/web_gui.go

@@ -8,12 +8,11 @@ import (
 
 	"github.com/gofiber/fiber/v2"
 	. "gitp78su.ipnodns.ru/svi/kern"
-	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	. "gitp78su.ipnodns.ru/svi/kern/krn/ktypes"
-	"gitp78su.ipnodns.ru/svi/kern/wui"
-	"gitp78su.ipnodns.ru/svi/kern/wui/wtypes"
 
 	. "wartank/app/lev0/types"
+	"wartank/app/lev3/serv_web/web_gui/btn_list_bot"
+	"wartank/app/lev3/serv_web/web_gui/btn_login"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_add"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_list"
 	"wartank/app/lev3/serv_web/web_gui/page_bot_show"
@@ -22,23 +21,24 @@ import (
 
 // ВебГип -- веб-сервер для графики
 type ВебГуи struct {
-	прилож  ИПриложение
-	конт    IKernelCtx
-	лог     ILogBuf
-	кнпБоты wtypes.IWuiButton
+	прилож   ИПриложение
+	конт     IKernelCtx
+	лог      ILogBuf
+	кнпЛогин *btn_login.КнпЛогин
+	кнпБоты  *btn_list_bot.КнпСписБот
 }
 
 // НовВебГуи -- возвращает новый веб-сервер для графики
-func НовВебГуи(конт IKernelCtx) *ВебГуи {
+func НовВебГуи() *ВебГуи {
 	лог := NewLogBuf()
-	Hassert(конт != nil, "НовВебГуи(): ИЯдроКонтекст==nil")
+	конт := GetKernelCtx()
 	сам := &ВебГуи{
-		прилож: конт.Get("мод_сервер").Val().(ИПриложение),
-		конт:   конт,
-		лог:    лог,
+		прилож:   конт.Get("мод_сервер").Val().(ИПриложение),
+		конт:     конт,
+		лог:      лог,
+		кнпЛогин: btn_login.НовКнпЛогин(),
 	}
-	сам.кнпБоты = wui.NewWuiButton("Список ботов", сам.ботыКлик)
-	сам.кнпБоты.Hx().Target().Set("#main")
+	сам.кнпБоты = btn_list_bot.НовКнпСписБот(сам.кнпЛогин)
 	файбер := конт.Get("fiberApp").Val().(*fiber.App)
 	// файбер.Get("/", сам.кукиПроверить) //, сам.индекс)
 	файбер.Get("/", сам.индекс)
@@ -49,19 +49,6 @@ func НовВебГуи(конт IKernelCtx) *ВебГуи {
 	return сам
 }
 
-var стрБлокБоты string
-
-//go:embed block_login.html
-var стрБлокЛогин string
-
-func (сам *ВебГуи) ботыКлик() string {
-	еслиЛогин := сам.конт.Get("еслиЛогин").Val().(bool)
-	if !еслиЛогин {
-		return стрБлокЛогин
-	}
-	return стрБлокБоты
-}
-
 //go:embed index.tmpl.html
 var стрГлавная string
 
@@ -72,6 +59,7 @@ func (сам *ВебГуи) индекс(кнт *fiber.Ctx) error {
 	кнт.Set("Cache-Control", "no-cache")
 	_, _ = кнт.WriteString("\n\n")
 	стрВых := strings.ReplaceAll(стрГлавная, "{.btn_lst_bot}", сам.кнпБоты.Html())
+	стрВых = strings.ReplaceAll(стрВых, "{.url_login}", сам.кнпЛогин.Кнп.Hx().Url().String())
 	return кнт.SendString(стрВых)
 }
 

+ 1 - 0
cmd/server/main.go

@@ -29,6 +29,7 @@ func main() {
 
 	сервер := build.НовМодСервер()
 	монолит.Add(сервер)
+
 	монолит.Run()
 	монолит.Wait()
 }

+ 1 - 1
go.mod

@@ -4,7 +4,7 @@ go 1.24.0
 
 require (
 	github.com/gofiber/fiber/v2 v2.52.6
-	gitp78su.ipnodns.ru/svi/kern v1.10.0
+	gitp78su.ipnodns.ru/svi/kern v1.11.0
 )
 
 require (

+ 2 - 2
go.sum

@@ -55,8 +55,8 @@ github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY
 github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
 github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-gitp78su.ipnodns.ru/svi/kern v1.10.0 h1:TUSGNCeXGfXdHVip2hqva2JVrKTWTEtyLmJH1DtToNA=
-gitp78su.ipnodns.ru/svi/kern v1.10.0/go.mod h1:+8wsxQThUx9wegfPZffhRJx+s+hnyDHv4n3ODMQm6+w=
+gitp78su.ipnodns.ru/svi/kern v1.11.0 h1:h7VGleV9VrdDBAyK/U+wXNEtbJlpVDgFOO7QIRJpFWQ=
+gitp78su.ipnodns.ru/svi/kern v1.11.0/go.mod h1:+8wsxQThUx9wegfPZffhRJx+s+hnyDHv4n3ODMQm6+w=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=

+ 171 - 0
vendor/github.com/gofiber/fiber/v2/middleware/adaptor/adaptor.go

@@ -0,0 +1,171 @@
+package adaptor
+
+import (
+	"io"
+	"net"
+	"net/http"
+	"reflect"
+	"unsafe"
+
+	"github.com/valyala/fasthttp"
+	"github.com/valyala/fasthttp/fasthttpadaptor"
+
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/fiber/v2/utils"
+)
+
+// HTTPHandlerFunc wraps net/http handler func to fiber handler
+func HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler {
+	return HTTPHandler(h)
+}
+
+// HTTPHandler wraps net/http handler to fiber handler
+func HTTPHandler(h http.Handler) fiber.Handler {
+	return func(c *fiber.Ctx) error {
+		handler := fasthttpadaptor.NewFastHTTPHandler(h)
+		handler(c.Context())
+		return nil
+	}
+}
+
+// ConvertRequest converts a fiber.Ctx to a http.Request.
+// forServer should be set to true when the http.Request is going to be passed to a http.Handler.
+func ConvertRequest(c *fiber.Ctx, forServer bool) (*http.Request, error) {
+	var req http.Request
+	if err := fasthttpadaptor.ConvertRequest(c.Context(), &req, forServer); err != nil {
+		return nil, err //nolint:wrapcheck // This must not be wrapped
+	}
+	return &req, nil
+}
+
+// CopyContextToFiberContext copies the values of context.Context to a fasthttp.RequestCtx
+func CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx) {
+	contextValues := reflect.ValueOf(context).Elem()
+	contextKeys := reflect.TypeOf(context).Elem()
+	if contextKeys.Kind() == reflect.Struct {
+		var lastKey interface{}
+		for i := 0; i < contextValues.NumField(); i++ {
+			reflectValue := contextValues.Field(i)
+			/* #nosec */
+			reflectValue = reflect.NewAt(reflectValue.Type(), unsafe.Pointer(reflectValue.UnsafeAddr())).Elem()
+
+			reflectField := contextKeys.Field(i)
+
+			if reflectField.Name == "noCopy" {
+				break
+			} else if reflectField.Name == "Context" {
+				CopyContextToFiberContext(reflectValue.Interface(), requestContext)
+			} else if reflectField.Name == "key" {
+				lastKey = reflectValue.Interface()
+			} else if lastKey != nil && reflectField.Name == "val" {
+				requestContext.SetUserValue(lastKey, reflectValue.Interface())
+			} else {
+				lastKey = nil
+			}
+		}
+	}
+}
+
+// HTTPMiddleware wraps net/http middleware to fiber middleware
+func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler {
+	return func(c *fiber.Ctx) error {
+		var next bool
+		nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			next = true
+			// Convert again in case request may modify by middleware
+			c.Request().Header.SetMethod(r.Method)
+			c.Request().SetRequestURI(r.RequestURI)
+			c.Request().SetHost(r.Host)
+			c.Request().Header.SetHost(r.Host)
+			for key, val := range r.Header {
+				for _, v := range val {
+					c.Request().Header.Set(key, v)
+				}
+			}
+			CopyContextToFiberContext(r.Context(), c.Context())
+		})
+
+		if err := HTTPHandler(mw(nextHandler))(c); err != nil {
+			return err
+		}
+
+		if next {
+			return c.Next()
+		}
+		return nil
+	}
+}
+
+// FiberHandler wraps fiber handler to net/http handler
+func FiberHandler(h fiber.Handler) http.Handler {
+	return FiberHandlerFunc(h)
+}
+
+// FiberHandlerFunc wraps fiber handler to net/http handler func
+func FiberHandlerFunc(h fiber.Handler) http.HandlerFunc {
+	return handlerFunc(fiber.New(), h)
+}
+
+// FiberApp wraps fiber app to net/http handler func
+func FiberApp(app *fiber.App) http.HandlerFunc {
+	return handlerFunc(app)
+}
+
+func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		// New fasthttp request
+		req := fasthttp.AcquireRequest()
+		defer fasthttp.ReleaseRequest(req)
+		// Convert net/http -> fasthttp request
+		if r.Body != nil {
+			n, err := io.Copy(req.BodyWriter(), r.Body)
+			req.Header.SetContentLength(int(n))
+
+			if err != nil {
+				http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
+				return
+			}
+		}
+		req.Header.SetMethod(r.Method)
+		req.SetRequestURI(r.RequestURI)
+		req.SetHost(r.Host)
+		req.Header.SetHost(r.Host)
+		for key, val := range r.Header {
+			for _, v := range val {
+				req.Header.Set(key, v)
+			}
+		}
+		if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil && err.(*net.AddrError).Err == "missing port in address" { //nolint:errorlint, forcetypeassert // overlinting
+			r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
+		}
+		remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
+		if err != nil {
+			http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
+			return
+		}
+
+		// New fasthttp Ctx
+		var fctx fasthttp.RequestCtx
+		fctx.Init(req, remoteAddr, nil)
+		if len(h) > 0 {
+			// New fiber Ctx
+			ctx := app.AcquireCtx(&fctx)
+			defer app.ReleaseCtx(ctx)
+			// Execute fiber Ctx
+			err := h[0](ctx)
+			if err != nil {
+				_ = app.Config().ErrorHandler(ctx, err) //nolint:errcheck // not needed
+			}
+		} else {
+			// Execute fasthttp Ctx though app.Handler
+			app.Handler()(&fctx)
+		}
+
+		// Convert fasthttp Ctx > net/http
+		fctx.Response.Header.VisitAll(func(k, v []byte) {
+			w.Header().Add(string(k), string(v))
+		})
+		w.WriteHeader(fctx.Response.StatusCode())
+		_, _ = w.Write(fctx.Response.Body()) //nolint:errcheck // not needed
+	}
+}

+ 159 - 0
vendor/github.com/valyala/fasthttp/fasthttpadaptor/adaptor.go

@@ -0,0 +1,159 @@
+// Package fasthttpadaptor provides helper functions for converting net/http
+// request handlers to fasthttp request handlers.
+package fasthttpadaptor
+
+import (
+	"bufio"
+	"io"
+	"net"
+	"net/http"
+	"sync"
+
+	"github.com/valyala/fasthttp"
+)
+
+// NewFastHTTPHandlerFunc wraps net/http handler func to fasthttp
+// request handler, so it can be passed to fasthttp server.
+//
+// While this function may be used for easy switching from net/http to fasthttp,
+// it has the following drawbacks comparing to using manually written fasthttp
+// request handler:
+//
+//   - A lot of useful functionality provided by fasthttp is missing
+//     from net/http handler.
+//   - net/http -> fasthttp handler conversion has some overhead,
+//     so the returned handler will be always slower than manually written
+//     fasthttp handler.
+//
+// So it is advisable using this function only for quick net/http -> fasthttp
+// switching. Then manually convert net/http handlers to fasthttp handlers
+// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
+func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler {
+	return NewFastHTTPHandler(h)
+}
+
+// NewFastHTTPHandler wraps net/http handler to fasthttp request handler,
+// so it can be passed to fasthttp server.
+//
+// While this function may be used for easy switching from net/http to fasthttp,
+// it has the following drawbacks comparing to using manually written fasthttp
+// request handler:
+//
+//   - A lot of useful functionality provided by fasthttp is missing
+//     from net/http handler.
+//   - net/http -> fasthttp handler conversion has some overhead,
+//     so the returned handler will be always slower than manually written
+//     fasthttp handler.
+//
+// So it is advisable using this function only for quick net/http -> fasthttp
+// switching. Then manually convert net/http handlers to fasthttp handlers
+// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
+func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler {
+	return func(ctx *fasthttp.RequestCtx) {
+		var r http.Request
+		if err := ConvertRequest(ctx, &r, true); err != nil {
+			ctx.Logger().Printf("cannot parse requestURI %q: %v", r.RequestURI, err)
+			ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError)
+			return
+		}
+		w := netHTTPResponseWriter{
+			w:   ctx.Response.BodyWriter(),
+			ctx: ctx,
+		}
+		h.ServeHTTP(&w, r.WithContext(ctx))
+
+		ctx.SetStatusCode(w.StatusCode())
+		haveContentType := false
+		for k, vv := range w.Header() {
+			if k == fasthttp.HeaderContentType {
+				haveContentType = true
+			}
+
+			for _, v := range vv {
+				ctx.Response.Header.Add(k, v)
+			}
+		}
+		if !haveContentType {
+			// From net/http.ResponseWriter.Write:
+			// If the Header does not contain a Content-Type line, Write adds a Content-Type set
+			// to the result of passing the initial 512 bytes of written data to DetectContentType.
+			l := 512
+			b := ctx.Response.Body()
+			if len(b) < 512 {
+				l = len(b)
+			}
+			ctx.Response.Header.Set(fasthttp.HeaderContentType, http.DetectContentType(b[:l]))
+		}
+	}
+}
+
+type netHTTPResponseWriter struct {
+	w          io.Writer
+	h          http.Header
+	ctx        *fasthttp.RequestCtx
+	statusCode int
+}
+
+func (w *netHTTPResponseWriter) StatusCode() int {
+	if w.statusCode == 0 {
+		return http.StatusOK
+	}
+	return w.statusCode
+}
+
+func (w *netHTTPResponseWriter) Header() http.Header {
+	if w.h == nil {
+		w.h = make(http.Header)
+	}
+	return w.h
+}
+
+func (w *netHTTPResponseWriter) WriteHeader(statusCode int) {
+	w.statusCode = statusCode
+}
+
+func (w *netHTTPResponseWriter) Write(p []byte) (int, error) {
+	return w.w.Write(p)
+}
+
+func (w *netHTTPResponseWriter) Flush() {}
+
+type wrappedConn struct {
+	net.Conn
+
+	wg   sync.WaitGroup
+	once sync.Once
+}
+
+func (c *wrappedConn) Close() (err error) {
+	c.once.Do(func() {
+		err = c.Conn.Close()
+		c.wg.Done()
+	})
+	return
+}
+
+func (w *netHTTPResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	// Hijack assumes control of the connection, so we need to prevent fasthttp from closing it or
+	// doing anything else with it.
+	w.ctx.HijackSetNoResponse(true)
+
+	conn := &wrappedConn{Conn: w.ctx.Conn()}
+	conn.wg.Add(1)
+	w.ctx.Hijack(func(net.Conn) {
+		conn.wg.Wait()
+	})
+
+	bufW := bufio.NewWriter(conn)
+
+	// Write any unflushed body to the hijacked connection buffer.
+	unflushedBody := w.ctx.Response.Body()
+	if len(unflushedBody) > 0 {
+		if _, err := bufW.Write(unflushedBody); err != nil {
+			conn.Close()
+			return nil, nil, err
+		}
+	}
+
+	return conn, &bufio.ReadWriter{Reader: bufio.NewReader(conn), Writer: bufW}, nil
+}

+ 11 - 0
vendor/github.com/valyala/fasthttp/fasthttpadaptor/b2s_new.go

@@ -0,0 +1,11 @@
+//go:build go1.20
+
+package fasthttpadaptor
+
+import "unsafe"
+
+// b2s converts byte slice to a string without memory allocation.
+// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
+func b2s(b []byte) string {
+	return unsafe.String(unsafe.SliceData(b), len(b))
+}

+ 14 - 0
vendor/github.com/valyala/fasthttp/fasthttpadaptor/b2s_old.go

@@ -0,0 +1,14 @@
+//go:build !go1.20
+
+package fasthttpadaptor
+
+import "unsafe"
+
+// b2s converts byte slice to a string without memory allocation.
+// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
+//
+// Note it may break if string and/or slice header will change
+// in the future go versions.
+func b2s(b []byte) string {
+	return *(*string)(unsafe.Pointer(&b))
+}

+ 70 - 0
vendor/github.com/valyala/fasthttp/fasthttpadaptor/request.go

@@ -0,0 +1,70 @@
+package fasthttpadaptor
+
+import (
+	"bytes"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+
+	"github.com/valyala/fasthttp"
+)
+
+// ConvertRequest converts a fasthttp.Request to an http.Request.
+// forServer should be set to true when the http.Request is going to be passed to a http.Handler.
+//
+// The http.Request must not be used after the fasthttp handler has returned!
+// Memory in use by the http.Request will be reused after your handler has returned!
+func ConvertRequest(ctx *fasthttp.RequestCtx, r *http.Request, forServer bool) error {
+	body := ctx.PostBody()
+	strRequestURI := b2s(ctx.RequestURI())
+
+	rURL, err := url.ParseRequestURI(strRequestURI)
+	if err != nil {
+		return err
+	}
+
+	r.Method = b2s(ctx.Method())
+	r.Proto = b2s(ctx.Request.Header.Protocol())
+	if r.Proto == "HTTP/2" {
+		r.ProtoMajor = 2
+	} else {
+		r.ProtoMajor = 1
+	}
+	r.ProtoMinor = 1
+	r.ContentLength = int64(len(body))
+	r.RemoteAddr = ctx.RemoteAddr().String()
+	r.Host = b2s(ctx.Host())
+	r.TLS = ctx.TLSConnectionState()
+	r.Body = io.NopCloser(bytes.NewReader(body))
+	r.URL = rURL
+
+	if forServer {
+		r.RequestURI = strRequestURI
+	}
+
+	if r.Header == nil {
+		r.Header = make(http.Header)
+	} else if len(r.Header) > 0 {
+		for k := range r.Header {
+			delete(r.Header, k)
+		}
+	}
+
+	ctx.Request.Header.VisitAll(func(k, v []byte) {
+		sk := b2s(k)
+		sv := b2s(v)
+
+		switch sk {
+		case "Transfer-Encoding":
+			r.TransferEncoding = append(r.TransferEncoding, sv)
+		default:
+			if sk == fasthttp.HeaderCookie {
+				sv = strings.Clone(sv)
+			}
+			r.Header.Set(sk, sv)
+		}
+	})
+
+	return nil
+}

+ 1 - 1
vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_serv_http/btn_modules/btn_modules.go

@@ -38,7 +38,7 @@ var strBlockModules string
 var strBlockRow string
 
 // Событие клика по кнопке
-func (sf *BtnModules) clickMonolit() string {
+func (sf *BtnModules) clickMonolit(dict map[string]string) string {
 	mon := sf.kCtx.Get("monolit").Val().(IKernelMonolit)
 	chLst := mon.Ctx().SortedList()
 	strOut := ``

+ 1 - 1
vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_serv_http/btn_monolit/btn_monolit.go

@@ -29,6 +29,6 @@ func (sf *BtnMonolit) Html() string {
 var strBlockMonolit string
 
 // Событие клика по кнопке
-func (sf *BtnMonolit) clickMonolit() string {
+func (sf *BtnMonolit) clickMonolit(dict map[string]string) string {
 	return strBlockMonolit
 }

+ 43 - 8
vendor/gitp78su.ipnodns.ru/svi/kern/mds/mod_wui/mod_wui.go

@@ -3,9 +3,14 @@ package mod_wui
 
 import (
 	"fmt"
+	"net/http"
+	"strings"
 	"sync"
 
 	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/fiber/v2/middleware/adaptor"
+
+	. "gitp78su.ipnodns.ru/svi/kern/kc/helpers"
 	"gitp78su.ipnodns.ru/svi/kern/krn/kctx"
 	"gitp78su.ipnodns.ru/svi/kern/krn/kmodule"
 	"gitp78su.ipnodns.ru/svi/kern/krn/kserv_http"
@@ -44,13 +49,13 @@ func GetModuleWui() *ModuleWui {
 		IKernelModule: kmodule.NewKernelModule("wui"),
 		kServHttp:     kserv_http.GetKernelServHttp(),
 	}
-	sf.log = sf.Ctx().Log()
+	sf.log = sf.wCtx.Log()
 	_ = page_monolit.GetPageMonolit()
 	_ = page_module.GetPageModule()
 
 	_ = http_api.NewHttpApi()
 	fibApp := sf.kCtx.Get("fiberApp").Val().(*fiber.App)
-	fibApp.Post("/wui/click/:id", sf.wuiClick)
+	fibApp.Post("/wui/click/:id", adaptor.HTTPHandlerFunc(sf.wuiClick)) // adaptor.HTTPHandlerFunc(greet)
 	mod = sf
 	return sf
 }
@@ -61,24 +66,54 @@ func (sf *ModuleWui) Run() {
 	go sf.kServHttp.Run()
 }
 
+// Log -- возвращает буферный лог
+func (sf *ModuleWui) Log() ILogBuf {
+	return sf.log
+}
+
 // IsWork -- признак работы модуля
 func (sf *ModuleWui) IsWork() bool {
 	return sf.kCtx.Wg().IsWork()
 }
 
 // Получает событие из сети
-func (sf *ModuleWui) wuiClick(ctx *fiber.Ctx) error {
-	id := ctx.Params("id")
+func (sf *ModuleWui) wuiClick(resp http.ResponseWriter, req *http.Request) {
+	url := req.RequestURI
+	id := strings.TrimPrefix(url, "/wui/click/")
 	widget0 := sf.wCtx.Get(id)
 	if widget0 == nil {
 		strOut := fmt.Sprintf("ModuleWui.wuiClick(): id(%v), widget not exists", id)
-		return ctx.SendString(strOut)
+		sf.log.Err(strOut)
+		fmt.Fprint(resp, strOut)
+		return
 	}
 	widget1, isOk := widget0.Val().(IWuiButton)
 	if !isOk {
 		strOut := fmt.Sprintf("ModuleWui.wuiClick(): widget(%T) not button", widget0.Val())
-		return ctx.SendString(strOut)
+		sf.log.Err(strOut)
+		fmt.Fprint(resp, strOut)
+		return
+	}
+	dict := map[string]string{}
+
+	// headers := ctx.GetReqHeaders()
+	for key, lstVal := range req.Header {
+		if len(lstVal) >= 1 {
+			dict[key] = lstVal[0]
+			continue
+		}
+	}
+
+	err := req.ParseForm()
+	Hassert(err == nil, "ModuleWui.wuiClick(): in parse form, err=\n\t%v", err)
+	// Получаем все form-значения
+	//values := req.ParseForm()
+	for key, lstVal := range req.Form {
+		if len(lstVal) >= 1 {
+			dict[key] = lstVal[0]
+			continue
+		}
 	}
-	strOut := widget1.Click()
-	return ctx.SendString(strOut)
+	strOut := widget1.Click(dict)
+	fmt.Fprint(resp, strOut)
 }

+ 4 - 4
vendor/gitp78su.ipnodns.ru/svi/kern/wui/wbutton/wbutton.go

@@ -17,12 +17,12 @@ import (
 type WuiButton struct {
 	IWuiWidget
 	text   IWuiText
-	fnBack func() string
+	fnBack func(map[string]string) string
 	hx     IWuiHx
 }
 
 // NewWuiButton -- возвращает новую WUI-кнопку
-func NewWuiButton(text string, fnBack func() string) *WuiButton {
+func NewWuiButton(text string, fnBack func(map[string]string) string) *WuiButton {
 	Hassert(fnBack != nil, "NewWuiButton(): fnBack==nil")
 	sf := &WuiButton{
 		IWuiWidget: wwidget.NewWuiWidget(),
@@ -47,8 +47,8 @@ func (sf *WuiButton) Text() IWuiText {
 }
 
 // Click -- событие нажатия
-func (sf *WuiButton) Click() string {
-	return sf.fnBack()
+func (sf *WuiButton) Click(dict map[string]string) string {
+	return sf.fnBack(dict)
 }
 
 const (

+ 1 - 1
vendor/gitp78su.ipnodns.ru/svi/kern/wui/wtypes/iwui_button.go

@@ -6,7 +6,7 @@ type IWuiButton interface {
 	// Text -- возвращает текст кнопки
 	Text() IWuiText
 	// Click -- нажатие кнопки
-	Click() string
+	Click(map[string]string) string
 	// Hx -- атрибуты HTMX
 	Hx() IWuiHx
 }

+ 1 - 1
vendor/gitp78su.ipnodns.ru/svi/kern/wui/wui.go

@@ -8,7 +8,7 @@ import (
 )
 
 // NewWuiButton -- возвращает новую WUI-кнопку
-func NewWuiButton(text string, fnClick func() string) IWuiButton {
+func NewWuiButton(text string, fnClick func(map[string]string) string) IWuiButton {
 	btn := wbutton.NewWuiButton(text, fnClick)
 	return btn
 }

+ 3 - 1
vendor/modules.txt

@@ -49,6 +49,7 @@ github.com/gofiber/fiber/v2/internal/gopsutil/process
 github.com/gofiber/fiber/v2/internal/schema
 github.com/gofiber/fiber/v2/internal/wmi
 github.com/gofiber/fiber/v2/log
+github.com/gofiber/fiber/v2/middleware/adaptor
 github.com/gofiber/fiber/v2/middleware/compress
 github.com/gofiber/fiber/v2/middleware/filesystem
 github.com/gofiber/fiber/v2/middleware/monitor
@@ -98,11 +99,12 @@ github.com/valyala/bytebufferpool
 # github.com/valyala/fasthttp v1.59.0
 ## explicit; go 1.21
 github.com/valyala/fasthttp
+github.com/valyala/fasthttp/fasthttpadaptor
 github.com/valyala/fasthttp/fasthttputil
 github.com/valyala/fasthttp/reuseport
 github.com/valyala/fasthttp/stackless
 github.com/valyala/fasthttp/tcplisten
-# gitp78su.ipnodns.ru/svi/kern v1.10.0
+# gitp78su.ipnodns.ru/svi/kern v1.11.0
 ## explicit; go 1.24.0
 gitp78su.ipnodns.ru/svi/kern
 gitp78su.ipnodns.ru/svi/kern/kc/helpers