优化: 资源消耗优化,发布 v0.2.1
This commit is contained in:
@@ -2,6 +2,8 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"charm.land/bubbletea/v2"
|
"charm.land/bubbletea/v2"
|
||||||
)
|
)
|
||||||
@@ -22,6 +24,11 @@ type Model struct {
|
|||||||
wsTabCur int // tabs 列光标 (0..len(Groups), 最后一个是历史入口)
|
wsTabCur int // tabs 列光标 (0..len(Groups), 最后一个是历史入口)
|
||||||
forkMode bool // 分叉输入模式
|
forkMode bool // 分叉输入模式
|
||||||
forkBuf string // 分叉方向提示输入
|
forkBuf string // 分叉方向提示输入
|
||||||
|
|
||||||
|
// 性能缓存
|
||||||
|
allSessionsCache []*Session // "全部" tab 会话切片缓存
|
||||||
|
allSessionsDirty bool // 缓存是否需要重建
|
||||||
|
cacheDirty bool // session-cache.json 是否需要写盘
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel() *Model {
|
func NewModel() *Model {
|
||||||
@@ -52,6 +59,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.history.Cache = msg.Cache
|
m.history.Cache = msg.Cache
|
||||||
m.history.Loaded = true
|
m.history.Loaded = true
|
||||||
m.history.Scanning = false
|
m.history.Scanning = false
|
||||||
|
m.allSessionsDirty = true
|
||||||
|
m.cacheDirty = true
|
||||||
if m.history.Favorites == nil {
|
if m.history.Favorites == nil {
|
||||||
m.history.Favorites = loadFavorites()
|
m.history.Favorites = loadFavorites()
|
||||||
}
|
}
|
||||||
@@ -77,7 +86,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
case SummaryResultMsg:
|
case SummaryResultMsg:
|
||||||
m.applySummary(msg.SessionID, msg.Summary, msg.Completed, msg.Pending)
|
m.applySummary(msg.SessionID, msg.Summary, msg.Completed, msg.Pending)
|
||||||
return m, m.nextSummaryCmd()
|
m.cacheDirty = true
|
||||||
|
cmds := []tea.Cmd{m.nextSummaryCmd(), cacheFlushTick()}
|
||||||
|
return m, tea.Batch(cmds...)
|
||||||
case UpdateAvailableMsg:
|
case UpdateAvailableMsg:
|
||||||
m.update.Checking = false
|
m.update.Checking = false
|
||||||
m.update.Available = true
|
m.update.Available = true
|
||||||
@@ -94,6 +105,12 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.update.Updating = false
|
m.update.Updating = false
|
||||||
m.update.Error = msg.Err
|
m.update.Error = msg.Err
|
||||||
m.update.Checking = false
|
m.update.Checking = false
|
||||||
|
case cacheFlushMsg:
|
||||||
|
m.flushCache()
|
||||||
|
return m, nil
|
||||||
|
case tea.QuitMsg:
|
||||||
|
m.flushCache()
|
||||||
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
@@ -101,3 +118,21 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
func (m *Model) GetPendingCmd() string {
|
func (m *Model) GetPendingCmd() string {
|
||||||
return m.pendingCmd
|
return m.pendingCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) flushCache() {
|
||||||
|
if !m.cacheDirty || m.history.Cache == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saveCache(home, m.history.Cache)
|
||||||
|
m.cacheDirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheFlushTick() tea.Cmd {
|
||||||
|
return tea.Tick(3*time.Second, func(_ time.Time) tea.Msg {
|
||||||
|
return cacheFlushMsg{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ func scanAllProjects() ([]*ProjectDir, map[string]*cacheEntry, map[string]bool)
|
|||||||
dirMap := make(map[string]*ProjectDir)
|
dirMap := make(map[string]*ProjectDir)
|
||||||
updatedIDs := make(map[string]bool)
|
updatedIDs := make(map[string]bool)
|
||||||
seenIDs := make(map[string]bool)
|
seenIDs := make(map[string]bool)
|
||||||
|
scanBuf := make([]byte, 0, 64*1024) // 共享扫描缓冲区
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !entry.IsDir() {
|
if !entry.IsDir() {
|
||||||
@@ -170,7 +171,7 @@ func scanAllProjects() ([]*ProjectDir, map[string]*cacheEntry, map[string]bool)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
session := scanSessionFile(filePath, sessionID)
|
session := scanSessionFile(filePath, sessionID, &scanBuf)
|
||||||
if session == nil || session.Cwd == "" {
|
if session == nil || session.Cwd == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,7 @@ func addSessionToDir(dirMap map[string]*ProjectDir, s *Session) {
|
|||||||
|
|
||||||
const scanLineLimit = 500
|
const scanLineLimit = 500
|
||||||
|
|
||||||
func scanSessionFile(path string, sessionID string) *Session {
|
func scanSessionFile(path string, sessionID string, buf *[]byte) *Session {
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -255,7 +256,7 @@ func scanSessionFile(path string, sessionID string) *Session {
|
|||||||
|
|
||||||
s := &Session{ID: sessionID, FilePath: path}
|
s := &Session{ID: sessionID, FilePath: path}
|
||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
scanner.Buffer(*buf, 1024*1024)
|
||||||
|
|
||||||
lineCount := 0
|
lineCount := 0
|
||||||
firstMsgFound := false
|
firstMsgFound := false
|
||||||
@@ -615,14 +616,12 @@ func updateSummaryInCache(cache map[string]*cacheEntry, sessionID, summary strin
|
|||||||
entry.AISummary = summary
|
entry.AISummary = summary
|
||||||
entry.Completed = completed
|
entry.Completed = completed
|
||||||
entry.Pending = pending
|
entry.Pending = pending
|
||||||
// 异步持久化
|
// 持久化由 Model.flushCache() 延迟批量写入
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
saveCache(home, cache)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cacheFlushMsg 延迟写缓存定时器消息
|
||||||
|
type cacheFlushMsg struct{}
|
||||||
|
|
||||||
// --- 收藏 ---
|
// --- 收藏 ---
|
||||||
|
|
||||||
func favoritesPath(home string) string {
|
func favoritesPath(home string) string {
|
||||||
|
|||||||
@@ -222,11 +222,19 @@ func (m *Model) moveHistorySess(dir int) {
|
|||||||
|
|
||||||
func (m *Model) currentSessions() []*Session {
|
func (m *Model) currentSessions() []*Session {
|
||||||
if m.history.DirCursor == 0 {
|
if m.history.DirCursor == 0 {
|
||||||
var all []*Session
|
if m.allSessionsDirty || m.allSessionsCache == nil {
|
||||||
|
total := 0
|
||||||
|
for _, pd := range m.history.Projects {
|
||||||
|
total += len(pd.Sessions)
|
||||||
|
}
|
||||||
|
all := make([]*Session, 0, total)
|
||||||
for _, pd := range m.history.Projects {
|
for _, pd := range m.history.Projects {
|
||||||
all = append(all, pd.Sessions...)
|
all = append(all, pd.Sessions...)
|
||||||
}
|
}
|
||||||
return all
|
m.allSessionsCache = all
|
||||||
|
m.allSessionsDirty = false
|
||||||
|
}
|
||||||
|
return m.allSessionsCache
|
||||||
}
|
}
|
||||||
if m.history.DirCursor == 1 {
|
if m.history.DirCursor == 1 {
|
||||||
return collectFavorites(m.history.Projects, m.history.Favorites)
|
return collectFavorites(m.history.Projects, m.history.Favorites)
|
||||||
|
|||||||
@@ -57,4 +57,18 @@ var (
|
|||||||
SessionSummaryStyle = lipgloss.NewStyle().Foreground(Dim).Italic(true).Inline(true)
|
SessionSummaryStyle = lipgloss.NewStyle().Foreground(Dim).Italic(true).Inline(true)
|
||||||
DirCountStyle = lipgloss.NewStyle().Foreground(Accent).Inline(true)
|
DirCountStyle = lipgloss.NewStyle().Foreground(Accent).Inline(true)
|
||||||
ScanningStyle = lipgloss.NewStyle().Foreground(Warning).Bold(true)
|
ScanningStyle = lipgloss.NewStyle().Foreground(Warning).Bold(true)
|
||||||
|
|
||||||
|
// ── 渲染热路径预构建样式 (避免每帧 NewStyle) ──
|
||||||
|
AccentFg = lipgloss.NewStyle().Foreground(Accent)
|
||||||
|
DimFg = lipgloss.NewStyle().Foreground(Dim)
|
||||||
|
SepFg = lipgloss.NewStyle().Foreground(BgPanel)
|
||||||
|
SuccessBoldFg = lipgloss.NewStyle().Foreground(Success).Bold(true)
|
||||||
|
WarningFg = lipgloss.NewStyle().Foreground(Warning)
|
||||||
|
WarningBoldFg = lipgloss.NewStyle().Foreground(Warning).Bold(true)
|
||||||
|
RedFg = lipgloss.NewStyle().Foreground(Red)
|
||||||
|
CyanFg = lipgloss.NewStyle().Foreground(Cyan)
|
||||||
|
AccentItalicFg = lipgloss.NewStyle().Foreground(Accent).Italic(true)
|
||||||
|
AccentBoldFg = lipgloss.NewStyle().Foreground(Accent).Bold(true)
|
||||||
|
TabActiveAccent = lipgloss.NewStyle().Bold(true).Background(BgPanel).Foreground(Accent)
|
||||||
|
TabActiveCyan = lipgloss.NewStyle().Bold(true).Background(BgPanel).Foreground(Cyan)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Version 当前版本,发布时更新
|
// Version 当前版本,发布时更新
|
||||||
const Version = "0.2.0"
|
const Version = "0.2.1"
|
||||||
|
|
||||||
// --- 远程 JSON 结构 ---
|
// --- 远程 JSON 结构 ---
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,20 @@ func toWinPath(s string) string {
|
|||||||
return strings.ReplaceAll(s, "/", "\\")
|
return strings.ReplaceAll(s, "/", "\\")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 分隔线缓存 ---
|
||||||
|
|
||||||
|
var sepCache = make(map[int]string)
|
||||||
|
|
||||||
|
// sepRunes 返回 width 个 "─" 字符,结果按 width 缓存。
|
||||||
|
func sepRunes(w int) string {
|
||||||
|
if s, ok := sepCache[w]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
s := strings.Repeat("─", w)
|
||||||
|
sepCache[w] = s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// --- 字符串宽度 ---
|
// --- 字符串宽度 ---
|
||||||
|
|
||||||
func stringWidth(s string) int {
|
func stringWidth(s string) int {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (m *Model) render() string {
|
|||||||
|
|
||||||
b.WriteString(m.renderTabBar())
|
b.WriteString(m.renderTabBar())
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.BgPanel).Render(strings.Repeat("─", m.width)))
|
b.WriteString(style.SepFg.Render(sepRunes(m.width)))
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
|
||||||
if m.IsHistoryTab() {
|
if m.IsHistoryTab() {
|
||||||
@@ -46,27 +46,27 @@ func (m *Model) render() string {
|
|||||||
b.WriteString(style.InputStyle.Render(fmt.Sprintf(" ⎇ fork:%s [Enter]confirm [Esc]cancel", m.forkBuf)))
|
b.WriteString(style.InputStyle.Render(fmt.Sprintf(" ⎇ fork:%s [Enter]confirm [Esc]cancel", m.forkBuf)))
|
||||||
}
|
}
|
||||||
if m.launched != "" {
|
if m.launched != "" {
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.Accent).Render(fmt.Sprintf(" ✓ %s", m.launched)))
|
b.WriteString(style.AccentFg.Render(fmt.Sprintf(" ✓ %s", m.launched)))
|
||||||
m.launched = ""
|
m.launched = ""
|
||||||
}
|
}
|
||||||
if m.update.Done {
|
if m.update.Done {
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.Success).Bold(true).
|
b.WriteString(style.SuccessBoldFg.
|
||||||
Render(fmt.Sprintf(" updated to v%s, restart to apply", m.update.NewVersion)))
|
Render(fmt.Sprintf(" updated to v%s, restart to apply", m.update.NewVersion)))
|
||||||
} else if m.update.Error != nil {
|
} else if m.update.Error != nil {
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.Red).
|
b.WriteString(style.RedFg.
|
||||||
Render(fmt.Sprintf(" update failed: %v", m.update.Error)))
|
Render(fmt.Sprintf(" update failed: %v", m.update.Error)))
|
||||||
} else if m.update.Updating {
|
} else if m.update.Updating {
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.Warning).
|
b.WriteString(style.WarningFg.
|
||||||
Render(" updating..."))
|
Render(" updating..."))
|
||||||
} else if m.update.Available {
|
} else if m.update.Available {
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(style.Warning).Bold(true).
|
b.WriteString(style.WarningBoldFg.
|
||||||
Render(fmt.Sprintf(" v%s available [u]update", m.update.NewVersion)))
|
Render(fmt.Sprintf(" v%s available [u]update", m.update.NewVersion)))
|
||||||
}
|
}
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
|
||||||
helpParts := m.renderHelp()
|
helpParts := m.renderHelp()
|
||||||
helpStr := " " + strings.Join(helpParts, " ")
|
helpStr := " " + strings.Join(helpParts, " ")
|
||||||
verStr := lipgloss.NewStyle().Foreground(style.Dim).Render("绝尘 v" + Version)
|
verStr := style.DimFg.Render("绝尘 v" + Version)
|
||||||
padW := m.width - lipgloss.Width(helpStr) - lipgloss.Width(verStr)
|
padW := m.width - lipgloss.Width(helpStr) - lipgloss.Width(verStr)
|
||||||
if padW > 0 {
|
if padW > 0 {
|
||||||
b.WriteString(helpStr + strings.Repeat(" ", padW) + verStr)
|
b.WriteString(helpStr + strings.Repeat(" ", padW) + verStr)
|
||||||
@@ -88,9 +88,7 @@ func (m *Model) renderTabBar() string {
|
|||||||
|
|
||||||
wsLabel := " 1 工作空间 "
|
wsLabel := " 1 工作空间 "
|
||||||
if !m.IsHistoryTab() {
|
if !m.IsHistoryTab() {
|
||||||
b.WriteString(lipgloss.NewStyle().
|
b.WriteString(style.TabActiveAccent.Render(wsLabel))
|
||||||
Bold(true).Background(style.BgPanel).Foreground(style.Accent).
|
|
||||||
Render(wsLabel))
|
|
||||||
} else {
|
} else {
|
||||||
b.WriteString(style.TabInactiveStyle.Render(wsLabel))
|
b.WriteString(style.TabInactiveStyle.Render(wsLabel))
|
||||||
}
|
}
|
||||||
@@ -98,9 +96,7 @@ func (m *Model) renderTabBar() string {
|
|||||||
b.WriteString(sep)
|
b.WriteString(sep)
|
||||||
histLabel := " 2 历史对话 "
|
histLabel := " 2 历史对话 "
|
||||||
if m.IsHistoryTab() {
|
if m.IsHistoryTab() {
|
||||||
b.WriteString(lipgloss.NewStyle().
|
b.WriteString(style.TabActiveCyan.Render(histLabel))
|
||||||
Bold(true).Background(style.BgPanel).Foreground(style.Cyan).
|
|
||||||
Render(histLabel))
|
|
||||||
} else {
|
} else {
|
||||||
b.WriteString(style.TabInactiveStyle.Render(histLabel))
|
b.WriteString(style.TabInactiveStyle.Render(histLabel))
|
||||||
}
|
}
|
||||||
@@ -132,12 +128,12 @@ func (m *Model) renderHelp() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) fmtHelp(key, desc string) string {
|
func (m *Model) fmtHelp(key, desc string) string {
|
||||||
return lipgloss.NewStyle().Foreground(style.Accent).Render(key) +
|
return style.AccentFg.Render(key) +
|
||||||
lipgloss.NewStyle().Foreground(style.Dim).Render(":" + desc)
|
style.DimFg.Render(":" + desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderNavEntry(lines *[]string, w int, label string, activeFg string, isCur, focused bool) {
|
func renderNavEntry(lines *[]string, w int, label string, activeFg string, isCur, focused bool) {
|
||||||
sep := lipgloss.NewStyle().Foreground(style.BgPanel).Render(fitWidth(" ─────────", w))
|
sep := style.SepFg.Render(fitWidth(" ─────────", w))
|
||||||
*lines = append(*lines, sep)
|
*lines = append(*lines, sep)
|
||||||
if isCur {
|
if isCur {
|
||||||
curLabel := " → " + label
|
curLabel := " → " + label
|
||||||
@@ -150,6 +146,6 @@ func renderNavEntry(lines *[]string, w int, label string, activeFg string, isCur
|
|||||||
} else {
|
} else {
|
||||||
inactiveLabel := " " + label
|
inactiveLabel := " " + label
|
||||||
line := padRightByWidth(truncateByWidth(inactiveLabel, w), w)
|
line := padRightByWidth(truncateByWidth(inactiveLabel, w), w)
|
||||||
*lines = append(*lines, lipgloss.NewStyle().Foreground(style.Dim).Render(line))
|
*lines = append(*lines, style.DimFg.Render(line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,10 +189,9 @@ func (m *Model) sessColumnLines(w, listH int) []string {
|
|||||||
func (m *Model) detailColumnLines(w, listH int) []string {
|
func (m *Model) detailColumnLines(w, listH int) []string {
|
||||||
s := m.currentSession()
|
s := m.currentSession()
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return []string{lipgloss.NewStyle().Foreground(style.Dim).Render(fitWidth(" ← select to view", w))}
|
return []string{style.DimFg.Render(fitWidth(" ← select to view", w))}
|
||||||
}
|
}
|
||||||
|
|
||||||
sepSty := lipgloss.NewStyle().Foreground(style.BgPanel)
|
|
||||||
var lines []string
|
var lines []string
|
||||||
|
|
||||||
favPrefix := " "
|
favPrefix := " "
|
||||||
@@ -200,7 +199,7 @@ func (m *Model) detailColumnLines(w, listH int) []string {
|
|||||||
favPrefix = "★ "
|
favPrefix = "★ "
|
||||||
}
|
}
|
||||||
lines = append(lines, style.DetailTitle.Render(fitWidth(favPrefix+s.DisplayTitle(), w)))
|
lines = append(lines, style.DetailTitle.Render(fitWidth(favPrefix+s.DisplayTitle(), w)))
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
|
|
||||||
fav := "-"
|
fav := "-"
|
||||||
if m.history.Favorites[s.ID] {
|
if m.history.Favorites[s.ID] {
|
||||||
@@ -218,34 +217,34 @@ func (m *Model) detailColumnLines(w, listH int) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if summary := s.DisplaySummary(); summary != "" {
|
if summary := s.DisplaySummary(); summary != "" {
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
lines = append(lines, style.SessionSummaryStyle.Render(fitWidth(" "+summary, w)))
|
lines = append(lines, style.SessionSummaryStyle.Render(fitWidth(" "+summary, w)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.Completed) > 0 {
|
if len(s.Completed) > 0 {
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Success).Bold(true).Render(fitWidth(" ✓ 已完成", w)))
|
lines = append(lines, style.SuccessBoldFg.Render(fitWidth(" ✓ 已完成", w)))
|
||||||
for _, item := range s.Completed {
|
for _, item := range s.Completed {
|
||||||
lines = append(lines, style.NormStyle.Render(fitWidth(" · "+cleanText(item), w)))
|
lines = append(lines, style.NormStyle.Render(fitWidth(" · "+cleanText(item), w)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.Pending) > 0 {
|
if len(s.Pending) > 0 {
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Warning).Bold(true).Render(fitWidth(" ○ 待办", w)))
|
lines = append(lines, style.WarningBoldFg.Render(fitWidth(" ○ 待办", w)))
|
||||||
for _, item := range s.Pending {
|
for _, item := range s.Pending {
|
||||||
lines = append(lines, style.NormStyle.Render(fitWidth(" · "+cleanText(item), w)))
|
lines = append(lines, style.NormStyle.Render(fitWidth(" · "+cleanText(item), w)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Dim).Render(fitWidth(" [Enter]resume", w)))
|
lines = append(lines, style.DimFg.Render(fitWidth(" [Enter]resume", w)))
|
||||||
|
|
||||||
if s.ForkFrom != "" {
|
if s.ForkFrom != "" {
|
||||||
forkID := s.ForkFrom
|
forkID := s.ForkFrom
|
||||||
if len(forkID) > 8 {
|
if len(forkID) > 8 {
|
||||||
forkID = forkID[:8]
|
forkID = forkID[:8]
|
||||||
}
|
}
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Cyan).Render(fitWidth(fmt.Sprintf(" ⎇ fork from %s", forkID), w)))
|
lines = append(lines, style.CyanFg.Render(fitWidth(fmt.Sprintf(" ⎇ fork from %s", forkID), w)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(lines) > listH {
|
if len(lines) > listH {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"charm.land/lipgloss/v2"
|
"charm.land/lipgloss/v2"
|
||||||
"u-tabs/internal/style"
|
"u-tabs/internal/style"
|
||||||
@@ -127,7 +126,7 @@ func (m *Model) wsListLines(g Group, svcs []Workspace, w, listH int) []string {
|
|||||||
text := truncateByWidth(paddedTitle+" "+ws.Prompt, remainW)
|
text := truncateByWidth(paddedTitle+" "+ws.Prompt, remainW)
|
||||||
sty := style.SelStyle
|
sty := style.SelStyle
|
||||||
if m.wsFocus != 1 {
|
if m.wsFocus != 1 {
|
||||||
sty = lipgloss.NewStyle().Foreground(style.Accent).Bold(true)
|
sty = style.AccentBoldFg
|
||||||
}
|
}
|
||||||
lines = append(lines, sty.Render(padRightByWidth(truncateByWidth(prefix+text, w), w)))
|
lines = append(lines, sty.Render(padRightByWidth(truncateByWidth(prefix+text, w), w)))
|
||||||
} else {
|
} else {
|
||||||
@@ -147,15 +146,14 @@ func (m *Model) wsListLines(g Group, svcs []Workspace, w, listH int) []string {
|
|||||||
|
|
||||||
func (m *Model) wsDetailLines(svcs []Workspace, w, listH int) []string {
|
func (m *Model) wsDetailLines(svcs []Workspace, w, listH int) []string {
|
||||||
if m.cursor >= len(svcs) {
|
if m.cursor >= len(svcs) {
|
||||||
return []string{lipgloss.NewStyle().Foreground(style.Dim).Render(fitWidth(" ← select to view", w))}
|
return []string{style.DimFg.Render(fitWidth(" ← select to view", w))}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := svcs[m.cursor]
|
ws := svcs[m.cursor]
|
||||||
sepSty := lipgloss.NewStyle().Foreground(style.BgPanel)
|
|
||||||
var lines []string
|
var lines []string
|
||||||
|
|
||||||
lines = append(lines, style.DetailTitle.Render(fitWidth(" "+ws.Title, w)))
|
lines = append(lines, style.DetailTitle.Render(fitWidth(" "+ws.Title, w)))
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
|
|
||||||
rows := []struct {
|
rows := []struct {
|
||||||
key string
|
key string
|
||||||
@@ -173,14 +171,14 @@ func (m *Model) wsDetailLines(svcs []Workspace, w, listH int) []string {
|
|||||||
lines = append(lines, style.NormStyle.Render(fitWidth(line, w)))
|
lines = append(lines, style.NormStyle.Render(fitWidth(line, w)))
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, sepSty.Render(fitWidth(strings.Repeat("─", w), w)))
|
lines = append(lines, style.SepFg.Render(fitWidth(sepRunes(w), w)))
|
||||||
|
|
||||||
hintCmd := "wt"
|
hintCmd := "wt"
|
||||||
if !isWindows {
|
if !isWindows {
|
||||||
hintCmd = "bash"
|
hintCmd = "bash"
|
||||||
}
|
}
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Accent).Italic(true).Render(fitWidth(" $ "+hintCmd+" → CC @"+ws.Title, w)))
|
lines = append(lines, style.AccentItalicFg.Render(fitWidth(" $ "+hintCmd+" → CC @"+ws.Title, w)))
|
||||||
lines = append(lines, lipgloss.NewStyle().Foreground(style.Dim).Render(fitWidth(" [Enter]start [Space]multi", w)))
|
lines = append(lines, style.DimFg.Render(fitWidth(" [Enter]start [Space]multi", w)))
|
||||||
|
|
||||||
if len(lines) > listH {
|
if len(lines) > listH {
|
||||||
lines = lines[:listH]
|
lines = lines[:listH]
|
||||||
|
|||||||
Reference in New Issue
Block a user