Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions internal/tui/tui_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package tui

import (
"fmt"
"strings"
"time"

"github.com/tingkai-c/localsend-cli/internal/models"

bubbletea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

// selectDevice 使用 Bubble Tea 库显示可供选择的设备列表并等待用户选择
Expand Down Expand Up @@ -59,8 +61,39 @@ type model struct {
sortedKeys []string // 保持固定的显示顺序
cursor int
updates <-chan []models.SendModel
width int
}

var (
appStyle = lipgloss.NewStyle().
Padding(1, 2).
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("63"))

titleStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("86"))

subtitleStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("245"))

selectedStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("230")).
Background(lipgloss.Color("63")).
Padding(0, 1)

normalStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("252"))

emptyStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("214"))
// Slight emphasis for loading state.

helpStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("244"))
)

// TickMsg 用于定期触发更新
type TickMsg time.Time

Expand Down Expand Up @@ -128,24 +161,48 @@ func (m model) Update(msg bubbletea.Msg) (bubbletea.Model, bubbletea.Cmd) {
default:
}
return m, tick()
case bubbletea.WindowSizeMsg:
m.width = msg.Width
}
return m, nil
}

// View 实现 Bubble Tea 的 View 方法
func (m model) View() string {
panel := appStyle
if m.width > 0 {
maxWidth := m.width - 2
if maxWidth > 32 {
panel = panel.MaxWidth(maxWidth)
}
}

if len(m.devices) == 0 {
return "Scanning Devices...\n\n Press Ctrl+C to exit"
body := strings.Join([]string{
titleStyle.Render("LocalSend"),
"",
emptyStyle.Render("⏳ Scanning for nearby devices..."),
"",
helpStyle.Render("Press Ctrl+C to exit"),
}, "\n")
return panel.Render(body)
}

lines := []string{
titleStyle.Render("LocalSend"),
subtitleStyle.Render(fmt.Sprintf("Found Devices • %d online", len(m.devices))),
"",
}

s := "Found Devices:\n\n"
for i, device := range m.devices {
cursor := " " // 默认没有光标
line := fmt.Sprintf(" %s %s", device.DeviceName, subtitleStyle.Render(device.IP))
if m.cursor == i {
cursor = ">" // 选中的光标
lines = append(lines, selectedStyle.Render("› "+line))
continue
}
s += fmt.Sprintf("%s %s (%s)\n", cursor, device.DeviceName, device.IP)
lines = append(lines, normalStyle.Render(" "+line))
}
s += "\nUse arrow keys to navigate and enter to select. Press Ctrl+C to exit."
return s

lines = append(lines, "", helpStyle.Render("↑/↓ or j/k: navigate • Enter: select • Ctrl+C: quit"))
return panel.Render(strings.Join(lines, "\n"))
}
22 changes: 22 additions & 0 deletions internal/tui/tui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tui

import (
"os"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -51,3 +52,24 @@ func TestSelectDevice(t *testing.T) {
t.Fatalf("SelectDevice returned an unexpected IP: %s", ip)
}
}

func TestViewPreview(t *testing.T) {
m := model{
devices: []models.SendModel{
{IP: "192.168.1.10", DeviceName: "MacBook Pro"},
{IP: "192.168.1.22", DeviceName: "Steam Deck"},
{IP: "192.168.1.35", DeviceName: "Pixel 9"},
},
cursor: 1,
}

view := m.View()
if !strings.Contains(view, "LocalSend") {
t.Fatalf("expected title in view, got: %q", view)
}
if !strings.Contains(view, "Steam Deck") {
t.Fatalf("expected selected device in view, got: %q", view)
}

t.Logf("TUI preview:\n%s", view)
}
Loading