رابط کاربری (UI)#
رابط کاربری پایانی (Terminal UI) ابزار bgscan بر پایه فریمورک BubbleTea (نسخه فورک شده Elvish v2 در مسیر charm.land/bubbletea/v2) و ابزار استایلدهی [Lipgloss] توسعه یافته است. معماری این بخش به صورت یک درخت کامپوننت (Component Tree) همراه با یک سیستم دیالوگ لایهای (Overlay) و یک مدیریتکننده چیدمان مرکزی (Layout Manager) طراحی شده است.
نقشه پکیجها (Package Map)#
| پکیج | وظیفه و مسئولیت |
|---|---|
ui/main/app | مدل ریشه (Root) در BubbleTea. مدیریت چیدمان، هدر/بدنه/فوتر و پشتهی دیالوگهای لایهای. |
ui/main/header | نوار بالایی — شامل نام برنامه و برندینگ آن. |
ui/main/body | بخش محتوای مرکزی — پشتهای از کامپوننتها را نگه میدارد (منوی اصلی ← زیرصفحهها). |
ui/main/footer | نوار وضعیت پایین — نسخه برنامه، نام صفحه فعلی، تعداد گوروتینها و میزان مصرف حافظه رم. |
ui/components/basic | ویجتهای قابل استفاده مجدد: تاییدیه، مدیریت داده (CRUD)، فیلد ورودی، بازرس (Inspector)، نمایش لاگ، منو، اعلان، انتخابگر فایل، نوار پیشرفت، جدول و تبها. |
ui/components/inspector | فرمهای تنظیمات اختصاصی هر پروتکل (DNS، عمومی، HTTP، ICMP، TCP، Xray). |
ui/components/menus | منوهای سطح صفحه: ورودی اصلی (Entry)، لاگها، آوتباندها، نوع اسکن، تنظیمات، منبع هدف. |
ui/components/tables | جدولهای داده: لیست آیپی، نمایشگر آیپی، آوتباندها، لیست نتایج. |
ui/components/scanner | نمای زنده اسکن — نوارهای پیشرفت تببندیشده + جدول نتایج به ازای هر مرحله. |
ui/shared | زیرساختهای مشترک: چیدمان هندسی، دیالوگ، محیط (کلیدها/حالتها)، اینترفیس کامپوننت و اعتبارسنجی. |
ui/theme | پالتهای رنگی تاریک/روشن/خودکار + آداپتور تم برای فرمهای کتابخانه huh. |
اینترفیس کامپوننت (Component Interface)#
تمام صفحهها، ویجتها و دیالوگها اینترفیس ui.shared.ui.Component را پیادهسازی میکنند:
type Component interface {
ID() ComponentID
Name() string
Init() tea.Cmd
Update(tea.Msg) (Component, tea.Cmd)
View() string
OnClose() tea.Cmd
Mode() env.Mode
}ID— یک شناسه یکتا (UUID) که توسطNewComponentID()تولید میشود و برای تطبیق پیامهای باز/بسته شدن کاربرد دارد.Name— یک نام قابل فهم برای انسان که در نوار وضعیت فوتر نشان داده میشود.Init— یکبار در زمان سوار شدن (Mount) کامپوننت فراخوانی میشود و یکtea.Cmdرا برای تنظیمات ناهمگام برمیگرداند.Update— مسیریاب پیامها (Message Router). کامپوننت بهروزرسانی شده و دستورات جدید را برمیگرداند.View— کامپوننت را رندر کرده و خروجی را به صورت رشته (String) خروجی میدهد.OnClose— عملیات پاکسازی و آزادسازی منابع هنگام حذف کامپوننت از پشته.Mode— تعیین میکند کدام کلیدها به عنوان کلید بازگشت یا خروج عمل کنند (بخش حالتهای ورودی را ببینید).
مدل ریشه (App)#
پکیج ui/main/app بالاترین سطح مدل در BubbleTea است. این مدل سه کامپوننت پایه و یک پشته از دیالوگها را مدیریت میکند:
type model struct {
layout *layout.Layout
dialog []ui.Component
dialogPlacements map[ui.ComponentID]*dialogPosition
header ui.Component
body ui.Component
footer ui.Component
}مسیریابی پیامها (app/update.go)#
متد Update در مدل ریشه، هماهنگکننده اصلی است:
tea.WindowSizeMsg— ابعاد چیدمان را دوباره محاسبه میکند:layout.Update(width, height).tea.KeyPressMsg— فشردن کلیدctrl+cبرنامه را فوراً میبندد؛ کلیدctrl+tوضعیت گوروتینها را تخلیه (Dump) میکند.- رهگیری لایهها (Overlay) — اگر دیالوگی باز باشد، بالاترین دیالوگ تمام ورودیهای کیبورد را مصرف میکند و کلیدهای بازگشت/خروج آن را میبندند.
dialog.OpenDialogMsg— یک دیالوگ لایهای جدید را همراه با متادیتای موقعیت مکانیاش به بالای پشته اضافه میکند.ui.CloseComponentMsg— دیالوگ لایهای مطابقتیافته را بر اساس ID حذف کرده و متدOnCloseآن را اجرا میکند.- توزیع پایه — اگر هیچ دیالوگی مسیر ورودی را مسدود نکرده باشد، پیام به صورت همزمان به هدر، بدنه و فوتر فرستاده میشود.
رندر کردن نهایی (app/view.go)#
متد View در مدل ریشه ساختار زیر را رندر میکند:
┌─────────────────────────────────┐
│ header │
├─────────────────────────────────┤
│ │
│ body │ ← پشته کامپوننتها (منو ← صفحه فعلی)
│ │
├─────────────────────────────────┤
│ footer │ ← نسخه · وضعیت · گوروتینها · حافظه
└─────────────────────────────────┘
+ دیالوگهای لایهای (وسطچین شده یا دارای موقعیت خاص)لایههای رویی به کمک کتابخانه bubbletea-overlay روی چیدمان پایه رندر میشوند. حداقل اندازه ترمینال برای اجرای برنامه ۳۵×۷۵ است؛ در ابعاد کوچکتر، پیام “terminal too small” (ترمینال خیلی کوچک است) به نمایش درمیآید.
بدنه — پشته کامپوننتها (Component Stack)#
پکیج ui/main/body یک پشته (Stack) از کامپوننتها را درون خود نگه میدارد. کامپوننتی که در بالای پشته قرار دارد، همان صفحه فعال و نمایان است.
components[0] = mainMenu (منوی اصلی) ← همیشه وجود دارد
components[1] = scan source picker (منبع) ← به بالای پشته اضافه شده
components[2] = scan type picker (نوع اسکن) ← به بالای پشته اضافه شده
components[3] = scanner view (نمای اسکنر) ← به بالای پشته اضافه شدهپیامهای جابهجایی و ناوبری:
| پیام | نتیجه و اثر |
|---|---|
ui.OpenComponentMsg | اضافه کردن (Push) یک کامپوننت جدید به بالای پشته |
ui.CloseComponentMsg | حذف کردن (Pop) یک کامپوننت مشخص بر اساس ID آن |
ui.ResetComponentStacksMsg | پاکسازی کامل پشته و بازگشت به منوی ریشه |
کلید بازگشت (b / Esc) | حذف کردن کامپوننت بالایی (اگر طول پشته بزرگتر از ۱ باشد) |
کلید خروج (q) | نمایش دیالوگ تاییدیه خروج از برنامه |
تنها کامپوننت بالای پشته پیامهای Update را دریافت میکند. وقتی این کامپوننت حذف میشود، کامپوننت زیرین آن فعال میگردد. نوار وضعیت فوتر نیز نام خود را بر اساس خروجی ()Name کامپوننت بالایی جدید بهروزرسانی میکند.
سیستم دیالوگ لایهای (Overlay)#
پکیج ui/shared/dialog دیالوگهای پاپآپی را فراهم میکند که بالاتر از چیدمان پایه رندر میشوند.
type OpenDialogMsg struct {
Component ui.Component
XPos DialogPosition // بالا، راست، پایین، چپ، مرکز
YPos DialogPosition
XOffset int
YOffset int
}دیالوگها برای تنظیم موقعیت خود از کتابخانه rmhubbert/bubbletea-overlay استفاده میکنند. بیشتر دیالوگها در مرکز صفحه قرار میگیرند؛ اما انتخابگرهای فایل و دیالوگهای تاییدیه ممکن است موقعیتهای متفاوتی داشته باشند.
پشته دیالوگها در app.model از چندین لایه تودرتو پشتیبانی میکند — لایهای که بالاتر از همه است، تمام ورودیها را به خود اختصاص میدهد.
حالتهای ورودی (Input Modes)#
فایل ui/shared/env/type.go سه حالت مختلف را تعریف میکند که نحوه تفسیر ورودیهای کیبورد را کنترل میکنند:
| حالت | توصیف | کلیدهای بازگشت | کلیدهای خروج |
|---|---|---|---|
NormalMode | ناوبری پیشفرض در منوها | b، Backspace، Esc | q، ctrl+c |
InputMode | تایپ درون یک فیلد متنی یا انتخابگر | Esc | ctrl+c |
ScanMode | اسکن در حال اجرا است | (هیچکدام — کنترلرهای UI توقف/پایان را مدیریت میکنند) | ctrl+c |
توابع IsBackKey(msg, mode) و IsQuitKey(msg, mode) در مسیر env/keys.go کلیدها را بر اساس حالت فعلی بررسی میکنند. این کار باعث میشود زمانی که کاربر در حال تایپ داخل یک فیلد متنی است، کلیدهای خروج یا بازگشت به اشتباه تحریک نشوند.
چیدمان (Layout)#
فایل ui/shared/layout/layout.go یک محاسبهگر هندسی وضعیتدار است. این پکیج موقعیت و ابعاد هر ناحیه (هدر، بدنه، فوتر) را در هر بار تغییر سایز ترمینال از پیش محاسبه میکند:
type Layout struct {
Terminal TerminalSize // ابعاد خام ترمینال
Content ContentSize // ناحیه قابل ترسیم (پس از اعمال حاشیهها)
Header ComponentSize
Body ComponentSize
Footer ComponentSize
}متد Layout.Update(width, height) در پاسخ به هر پیام tea.WindowSizeMsg فراخوانی میشود. تمام کامپوننتها یک ارجاع به این ساختار مشترک *Layout دریافت میکنند تا بتوانند بدون نیاز به محاسبه مجدد، ابعاد ناحیه خود را استعلام کنند.
ویجتهای قابل استفاده مجدد (Basic)#
پوشه ui/components/basic/ حاوی ویجتهای پایهای و ساختاری است که در سراسر صفحهها استفاده میشوند:
| ویجت | هدف و کاربرد |
|---|---|
confirm | دیالوگ تاییدیه بله/خیر (مورد استفاده برای خروج، حذف) |
crud | اینترفیس جدول مدیریت داده عمومی (لیستهای آیپی، آوتباندها) همراه با انتزاع Provider |
input | پوسته دیالوگ ورودی + ویجتهای ورودی تایپشده (متنی، چندخطی، تکانتخابی، چندانتخابی، سوییچ) |
inspector | ویرایشگر عمومی تنظیمات کلید-مقدار همراه با فرمتدهی فیلدها و آداپتورهای نوع داده |
logview | نمایشگر استریم و اسکرولشونده لاگها (هسته/رابط کاربری/دباگ) |
menu | لیست منوی عمودی با قابلیت ناوبری از طریق کیبورد |
notice | بنر اعلانهای گذرا و موقت |
picker | دیالوگ انتخابگر فایل |
progress | نوار پیشرفت انیمیشنی همراه با نمایش زمان تخمینی پایان (ETA) و نرخ ارسال پکت |
table | جدول داده با قابلیت مرتبسازی و اسکرول |
tabs | کانتینر تببندیشده (مورد استفاده در نمای اسکنر برای تفکیک مراحل) |
هر ویجت از اینترفیس استاندارد Component پیروی میکند و به گونهای طراحی شده تا بتواند در صفحههای بزرگتر ترکیب و استفاده شود.
بازرسهای تنظیمات (Settings Inspectors)#
پوشه ui/components/inspector/ حاوی فرمهای تنظیمات اختصاصی به ازای هر پروتکل است:
| فرم | تنظیماتی که ویرایش میکند |
|---|---|
general | general_settings.toml |
icmp | icmp_settings.toml |
tcp | tcp_settings.toml |
http | http_settings.toml |
dns | dns_settings.toml |
xray | xray_settings.toml |
هر بازرس تنظیمات فعلی را از طریق متدهای ()config.GetXxx میخواند، فیلدهای قابل ویرایش را رندر میکند و هنگام ذخیره، متد ()config.SaveXxxConfig را صدا میزند. فیلدها میتوانند پویا باشند — برای مثال، گزینههای TLS تنها زمانی ظاهر میشوند که HTTPS انتخاب شده باشد، و فیلدهای DNSTT/SlipStream تنها در صورت فعال بودن آن پروبها به نمایش درمیآیند.
ویجت عمومی basic/inspector وظیفه رندر فیلدها، فرمتدهی و تبدیل انواع داده را بر عهده دارد؛ پکیجهای اختصاصی هر پروتکل صرفاً تعریف میکنند چه فیلدهایی نشان داده شوند و چگونه به ساختار تنظیمات (Config Struct) نگاشت گردند.
منوها و جدولها#
منوها (ui/components/menus)#
| منو | نقطه تحریک و فراخوانی | آنچه نمایش میدهد |
|---|---|---|
entry | شروع برنامه | منوی اصلی (اجرای اسکن، فایلهای آیپی، فایلهای نتایج، تنظیمات، آوتباندهای Xray، لاگها) |
targetsource | انتخاب گزینه اجرای اسکن | انتخابگر منبع ورودی (لیست آیپی در مقابل لیست نتایج قبلی) |
scantype | پس از انتخاب منبع ورودی | انتخابگر نوع اسکن (ICMP، TCP، HTTP، DNS، Xray) |
settings | آیتم منوی تنظیمات | انتخابگر دستهبندی تنظیمات |
outboundmenu | آیتم منوی آوتباندهای Xray | مدیریت و پیکربندی آوتباندها |
logs | آیتم منوی لاگها | انتخابگر دستهبندی لاگ (هسته/رابط کاربری/دباگ) |
جدولها (ui/components/tables)#
| جدول | منبع داده | تامینکننده (Provider) |
|---|---|---|
iplist | دایرکتوری ips/ | iplist.Registry |
resultlist | دایرکتوری results/ | result.Registry |
outbounds | کانفیگهای آوتباند Xray | پکیج xray |
ipviewer | مجموعه نتایج درون حافظه (In-memory) | کامپوننت اسکنر |
جدولها از ویجت ابزار basic/table و یک اینترفیس Provider استفاده میکنند که سطرها را تامین کرده و مرتبسازی ستونها را هندل میکند.
نمای اسکنر (Scanner View)#
پکیج ui/components/scanner همان داشبورد زنده اسکن است. این بخش ساختار Scanner را به لایه رابط کاربری متصل میکند:
- یک تب (Tab) به ازای هر مرحله از اسکن (از طریق
basic/tabs). - هر تب شامل یک نوار پیشرفت (
basic/progress) و یک جدول نتایج زنده (tables/ipviewer) است. - وضعیت هر مرحله به صورت مجزا رهگیری میشود:
Waiting ← PreProcess → Scanning → Ended / Error. - بهروزرسانیهای پیشرفت از مسیر
engine.ScanHooks.OnProgress← پیامProgress← متدUpdateکامپوننت اسکنر جریان مییابند. - نتایج موفقیتآمیز در دستههای کوچک به ازای هر مرحله بافر شده و در جدول ipviewer رندر میشوند.
کلیدهای کنترلی در طول اسکن: توقف/ادامه (Pause/Resume)، پایان دادن به اسکن (Stop)، جابهجایی بین تبها برای مشاهده مراحل مختلف.
تم و ظاهر (Theming)#
فایل ui/theme/theme.go پالت رنگی متمرکز برنامه را فراهم میکند:
type Theme struct {
Primary color.Color
Secondary color.Color
Border color.Color
BorderActive color.Color
Text color.Color
Muted color.Color
Info color.Color
Error color.Color
Success color.Color
Orange color.Color
Yellow color.Color
Purple color.Color
ProgressStart color.Color
ProgressEnd color.Color
}حالتها (Modes)#
| حالت | نحوه رفتار |
|---|---|
ModeAuto | رنگ پسزمینه ترمینال را از طریق متغیر $COLORFGBG تشخیص داده و تم تاریک/روشن را انتخاب میکند. |
ModeDark | پالت رنگی تاریک (Dark) را اجبار میکند. |
ModeLight | پالت رنگی روشن (Light) را اجبار میکند. |
متد theme.Init() در زمان راهاندازی برنامه فراخوانی میشود. کامپوننتها پالت فعال را از طریق ()theme.Current دریافت میکنند — رنگها نباید به صورت هاردکد (Hardcode) درون کامپوننتها نوشته شوند.
یکپارچهسازی با Huh#
فایل ui/theme/huh.go یک ساختار huh.Theme را بر اساس پالت رنگی متمرکز برنامه میسازد تا فیلدهای فرم (انتخاب، تاییدیه، ورودی متنی) که توسط کتابخانه huh رندر میشوند، با بقیه بخشهای رابط کاربری هماهنگ باشند و از رنگهای پیشفرض خود کتابخانه استفاده نکنند. این ابزار در ویجتهای ورودی (input) و بازرس (inspector) کاربرد دارد.
عیبیابی (Debugging)#
تخلیه وضعیت گوروتینها (Goroutine Dump)#
فشردن کلید Ctrl+T در هر زمان، تابع ()dumpGoroutines را در مسیر app/update.go تحریک میکند:
- تعداد کل گوروتینهای فعال را لاگ میکند.
- پروفایل کامل گوروتینهای
runtime/pprofرا در لاگ دباگ مینویسد. - خطوط حاوی عبارات
[chan receive]،[chan send]،[select]،[IO wait]و[sleep]را فیلتر و ثبت میکند — این کار برای پیدا کردن ورکرهای قفلشده یا نشت پروبها فوقالعاده مفید است.
آمارهای زمان اجرا (Runtime Stats)#
کامپوننت فوتر معیارهای زمان اجرای Go (Runtime Metrics) را هر ثانیه یکبار استعلام میکند:
()runtime.NumGoroutine— تعداد گوروتینهای فعال()runtime.ReadMemStats— میزان حافظه تخصیص یافته به Heap و کل حافظه سیستم
این آمارها در نوار وضعیت پایین نمایش داده میشوند و برای نظارت زنده بر میزان مصرف منابع سیستم در طول اسکن بسیار کاربردی هستند.
صفحات مرتبط#
- معماری (Architecture) — چیدمان کلی و سطح بالای پروژه
- هسته اصلی (Core) — موتور اسکنر و اینترفیس پروبها