วิธีที่สนุก ใช้งานได้จริง และมีสถานะในการสร้างแอปเทอร์มินัล กรอบการทำงาน Go ที่ใช้สถาปัตยกรรม Elm Bubble Tea เหมาะอย่างยิ่งสำหรับการใช้งานเทอร์มินัลที่เรียบง่ายและซับซ้อน ทั้งแบบอินไลน์ เต็มหน้าต่าง หรือทั้งสองอย่างผสมกัน
Bubble Tea มีการใช้งานในการผลิตและมีคุณสมบัติและการเพิ่มประสิทธิภาพจำนวนหนึ่งที่เราได้เพิ่มเข้าไปตลอดการทำงาน หนึ่งในนั้นคือตัวเรนเดอร์ที่ใช้เฟรมเรตมาตรฐาน ตัวเรนเดอร์สำหรับขอบเขตที่เลื่อนได้ประสิทธิภาพสูงซึ่งทำงานควบคู่ไปกับตัวเรนเดอร์หลัก และการรองรับเมาส์
ในการเริ่มต้น โปรดดูบทแนะนำด้านล่าง ตัวอย่าง เอกสาร วิดีโอบทแนะนำ และแหล่งข้อมูลทั่วไปบางส่วน
อย่าลืมลองดู Bubbles ซึ่งเป็นคลังองค์ประกอบ UI ทั่วไปสำหรับ Bubble Tea
Bubble Tea มีพื้นฐานมาจากกระบวนทัศน์การออกแบบเชิงฟังก์ชันของ The Elm Architecture ซึ่งเข้ากันได้ดีกับ Go เป็นวิธีที่น่ายินดีในการสร้างแอปพลิเคชัน
บทช่วยสอนนี้ถือว่าคุณมีความรู้เกี่ยวกับการทำงานของ Go
อย่างไรก็ตาม ซอร์สโค้ดที่ไม่มีคำอธิบายประกอบสำหรับโปรแกรมนี้มีอยู่ใน GitHub
สำหรับบทช่วยสอนนี้ เรากำลังสร้างรายการช็อปปิ้ง
ในการเริ่มต้น เราจะกำหนดแพ็คเกจของเราและนำเข้าไลบรารีบางส่วน การนำเข้าภายนอกเพียงอย่างเดียวของเราคือห้องสมุด Bubble Tea ซึ่งเราจะเรียกสั้น ๆ ว่า tea
package main
import (
"fmt"
"os"
tea "github.com/charmbracelet/bubbletea"
)
โปรแกรม Bubble Tea ประกอบด้วย แบบจำลอง ที่อธิบายสถานะการใช้งานและวิธีการง่ายๆ สามวิธีในแบบจำลองนั้น:
เริ่มต้นด้วยการกำหนดโมเดลของเราซึ่งจะจัดเก็บสถานะของแอปพลิเคชันของเรา อาจเป็นประเภทใดก็ได้ แต่ struct
มักจะเหมาะสมที่สุด
type model struct {
choices [] string // items on the to-do list
cursor int // which to-do list item our cursor is pointing at
selected map [ int ] struct {} // which to-do items are selected
}
ต่อไป เราจะกำหนดสถานะเริ่มต้นของแอปพลิเคชันของเรา ในกรณีนี้ เรากำลังกำหนดฟังก์ชันเพื่อส่งคืนโมเดลเริ่มต้นของเรา อย่างไรก็ตาม เราสามารถกำหนดโมเดลเริ่มต้นให้เป็นตัวแปรอื่นได้อย่างง่ายดายเช่นกัน
func initialModel () model {
return model {
// Our to-do list is a grocery list
choices : [] string { "Buy carrots" , "Buy celery" , "Buy kohlrabi" },
// A map which indicates which choices are selected. We're using
// the map like a mathematical set. The keys refer to the indexes
// of the `choices` slice, above.
selected : make ( map [ int ] struct {}),
}
}
ต่อไป เราจะกำหนดวิธี Init
Init
สามารถส่งคืน Cmd
ที่สามารถดำเนินการ I/O เริ่มต้นได้ ในตอนนี้ เราไม่ต้องทำ I/O ใดๆ ดังนั้นสำหรับคำสั่ง เราจะคืนค่า nil
ซึ่งแปลว่า "ไม่มีคำสั่ง"
func ( m model ) Init () tea. Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
ต่อไปคือวิธีการอัพเดต ฟังก์ชั่นอัพเดตจะถูกเรียกเมื่อ ”สิ่งต่าง ๆ เกิดขึ้น” หน้าที่ของมันคือการดูว่าเกิดอะไรขึ้นและส่งคืนโมเดลที่อัปเดตเพื่อตอบโต้ นอกจากนี้ยังสามารถส่งคืน Cmd
เพื่อให้สิ่งต่าง ๆ เกิดขึ้นได้ แต่ตอนนี้ไม่ต้องกังวลกับส่วนนั้น
ในกรณีของเรา เมื่อผู้ใช้กดลูกศรลง งานของ Update
คือการสังเกตว่ามีการกดลูกศรลงและเลื่อนเคอร์เซอร์ตามนั้น (หรือไม่)
“มีบางอย่างเกิดขึ้น” จะอยู่ในรูปแบบ Msg
ซึ่งสามารถเป็นประเภทใดก็ได้ ข้อความเป็นผลจาก I/O บางอย่างที่เกิดขึ้น เช่น การกดปุ่ม ขีดจับเวลา หรือการตอบกลับจากเซิร์ฟเวอร์
โดยปกติแล้วเราจะทราบว่าเราได้รับ Msg
ประเภทใดด้วยสวิตช์ประเภท แต่คุณสามารถใช้การยืนยันประเภทได้เช่นกัน
ในตอนนี้ เราจะจัดการกับข้อความ tea.KeyMsg
ซึ่งจะถูกส่งไปยังฟังก์ชันอัปเดตโดยอัตโนมัติเมื่อมีการกดปุ่ม
func ( m model ) Update ( msg tea. Msg ) (tea. Model , tea. Cmd ) {
switch msg := msg .( type ) {
// Is it a key press?
case tea. KeyMsg :
// Cool, what was the actual key pressed?
switch msg . String () {
// These keys should exit the program.
case "ctrl+c" , "q" :
return m , tea . Quit
// The "up" and "k" keys move the cursor up
case "up" , "k" :
if m . cursor > 0 {
m . cursor --
}
// The "down" and "j" keys move the cursor down
case "down" , "j" :
if m . cursor < len ( m . choices ) - 1 {
m . cursor ++
}
// The "enter" key and the spacebar (a literal space) toggle
// the selected state for the item that the cursor is pointing at.
case "enter" , " " :
_ , ok := m . selected [ m . cursor ]
if ok {
delete ( m . selected , m . cursor )
} else {
m . selected [ m . cursor ] = struct {}{}
}
}
}
// Return the updated model to the Bubble Tea runtime for processing.
// Note that we're not returning a command.
return m , nil
}
คุณอาจสังเกตเห็นว่า ctrl+c และ q ด้านบนส่งคืนคำสั่ง tea.Quit
พร้อมกับโมเดล นั่นเป็นคำสั่งพิเศษที่สั่งให้รันไทม์ Bubble Tea ออกและออกจากโปรแกรม
ในที่สุดก็ถึงเวลาเรนเดอร์ UI ของเรา ในบรรดาวิธีการทั้งหมด มุมมองเป็นวิธีที่ง่ายที่สุด เราดูโมเดลในสถานะปัจจุบันและใช้เพื่อส่งคืน string
สตริงนั้นคือ UI ของเรา!
เนื่องจากมุมมองจะอธิบาย UI ทั้งหมดของแอปพลิเคชันของคุณ คุณจึงไม่ต้องกังวลกับการวาดตรรกะใหม่และอะไรทำนองนั้น ชานมไข่มุกดูแลคุณ
func ( m model ) View () string {
// The header
s := "What should we buy at the market? n n "
// Iterate over our choices
for i , choice := range m . choices {
// Is the cursor pointing at this choice?
cursor := " " // no cursor
if m . cursor == i {
cursor = ">" // cursor!
}
// Is this choice selected?
checked := " " // not selected
if _ , ok := m . selected [ i ]; ok {
checked = "x" // selected!
}
// Render the row
s += fmt . Sprintf ( "%s [%s] %s n " , cursor , checked , choice )
}
// The footer
s += " n Press q to quit. n "
// Send the UI for rendering
return s
}
ขั้นตอนสุดท้ายคือการรันโปรแกรมของเรา เราส่งโมเดลเริ่มต้นของเราไปที่ tea.NewProgram
และปล่อยให้มันริพ:
func main () {
p := tea . NewProgram ( initialModel ())
if _ , err := p . Run (); err != nil {
fmt . Printf ( "Alas, there's been an error: %v" , err )
os . Exit ( 1 )
}
}
บทช่วยสอนนี้ครอบคลุมพื้นฐานของการสร้าง UI เทอร์มินัลแบบโต้ตอบ แต่ในโลกแห่งความเป็นจริง คุณจะต้องดำเนินการ I/O ด้วย หากต้องการเรียนรู้เกี่ยวกับเรื่องนี้ โปรดดูที่ Command Tutorial มันค่อนข้างง่าย
นอกจากนี้ยังมีตัวอย่าง Bubble Tea อีกหลายรายการ และแน่นอนว่ายังมี Go Docs อีกด้วย
เนื่องจากแอป Bubble Tea ควบคุม stdin และ stdout คุณจะต้องเรียกใช้ delve ในโหมด headless แล้วเชื่อมต่อกับแอปดังกล่าว:
# Start the debugger
$ dlv debug --headless --api-version=2 --listen=127.0.0.1:43000 .
API server listening at: 127.0.0.1:43000
# Connect to it from another terminal
$ dlv connect 127.0.0.1:43000
หากคุณไม่ได้ระบุแฟล็ก --listen
ไว้อย่างชัดเจน พอร์ตที่ใช้จะแตกต่างกันไปในแต่ละรัน ดังนั้นการส่งผ่านสิ่งนี้จะทำให้ดีบักเกอร์ใช้งานได้ง่ายขึ้นจากสคริปต์หรือ IDE ที่คุณเลือก
นอกจากนี้ เรายังส่งผ่าน --api-version=2
เนื่องจาก delve มีค่าเริ่มต้นเป็นเวอร์ชัน 1 ด้วยเหตุผลด้านความเข้ากันได้แบบย้อนหลัง อย่างไรก็ตาม delve ขอแนะนำให้ใช้เวอร์ชัน 2 สำหรับการพัฒนาใหม่ทั้งหมด และไคลเอ็นต์บางตัวอาจไม่ทำงานกับเวอร์ชัน 1 อีกต่อไป สำหรับข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบของ Delve
คุณไม่สามารถเข้าสู่ระบบ stdout ด้วย Bubble Tea ได้จริงๆ เพราะ TUI ของคุณยุ่งอยู่กับเรื่องนั้น! อย่างไรก็ตาม คุณสามารถเข้าสู่ระบบไฟล์ได้โดยรวมข้อมูลต่อไปนี้ก่อนที่จะเริ่มโปรแกรม Bubble Tea ของคุณ:
if len ( os . Getenv ( "DEBUG" )) > 0 {
f , err := tea . LogToFile ( "debug.log" , "debug" )
if err != nil {
fmt . Println ( "fatal:" , err )
os . Exit ( 1 )
}
defer f . Close ()
}
หากต้องการดูสิ่งที่ถูกบันทึกแบบเรียลไทม์ ให้เรียกใช้ tail -f debug.log
ขณะที่คุณรันโปรแกรมในหน้าต่างอื่น
มีแอปพลิเคชั่นมากกว่า 8,000 รายการที่สร้างด้วย Bubble Tea! นี่คือจำนวนหนึ่งของพวกเขา
สำหรับแอปพลิเคชันเพิ่มเติมที่สร้างด้วย Bubble Tea โปรดดูที่ Charm & Friends มีอะไรเจ๋งๆ ที่คุณทำกับ Bubble Tea ที่คุณอยากแบ่งปันบ้างไหม? PR ยินดีต้อนรับ!
ดูการบริจาค
เราชอบที่จะได้ยินความคิดของคุณเกี่ยวกับโครงการนี้ โปรดแจ้งให้เราทราบ!
Bubble Tea มีต้นแบบมาจาก The Elm Architecture โดย Evan Czaplicki และ Go-Tea ที่ยอดเยี่ยมโดย TJ Holowaychuk ได้รับแรงบันดาลใจจาก Zeichenorientierte Benutzerschnittstellen ผู้ยิ่งใหญ่มากมายในสมัยก่อน
เอ็มไอที
ส่วนหนึ่งของเสน่ห์
Charm热爱เริ่ม源 • Charm รักโอเพ่นซอร์ส • نحن نحب المصادر المفتوحة