feat: UI legend

This commit is contained in:
2026-01-25 22:28:53 -06:00
parent 6165a10481
commit 7964d4e183
3 changed files with 124 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ import (
"playback/internal/srt"
"playback/internal/ui"
"playback/internal/ui/header"
"playback/internal/ui/legend"
"playback/internal/ui/transcript"
"playback/internal/ui/waveform"
)
@@ -33,6 +34,7 @@ type Model struct {
// UI Components
header header.Model
legend legend.Model
waveform waveform.Model
transcript transcript.Model
@@ -56,6 +58,7 @@ func New(audioPath, transcriptPath string) Model {
audioPath: audioPath,
transcriptPath: transcriptPath,
header: header.New(),
legend: legend.New(),
waveform: waveform.New(),
transcript: transcript.New(),
}
@@ -231,13 +234,17 @@ func (m *Model) updateLayout() {
// Waveform: 5 lines
waveformHeight := 5
// Legend: 1 line
legendHeight := 1
// Status bar: 1 line
statusHeight := 1
// Transcript: remaining space
transcriptHeight := m.height - headerHeight - waveformHeight - statusHeight - 2
transcriptHeight := m.height - headerHeight - waveformHeight - legendHeight - statusHeight - 2
m.header.SetWidth(m.width)
m.legend.SetWidth(m.width)
m.waveform.SetSize(m.width, waveformHeight)
m.transcript.SetSize(m.width, transcriptHeight)
}
@@ -287,6 +294,9 @@ func (m Model) View() string {
// Transcript
transcriptView := m.transcript.View()
// Legend
legendView := m.legend.View()
// Status bar
statusView := m.renderStatus()
@@ -296,6 +306,7 @@ func (m Model) View() string {
"",
waveformView,
transcriptView,
legendView,
statusView,
)
}

View File

@@ -0,0 +1,107 @@
package legend
import (
"strings"
"github.com/charmbracelet/lipgloss"
"playback/internal/ui"
)
// Model represents the legend component
type Model struct {
Width int
}
// New creates a new legend model
func New() Model {
return Model{}
}
// SetWidth sets the legend width
func (m *Model) SetWidth(width int) {
m.Width = width
}
// View renders the key legend
func (m Model) View() string {
if m.Width < 30 {
return "" // Too narrow to show legend
}
// Group keybindings by category
groups := []struct {
title string
entries []keyEntry
}{
{
title: "Playback",
entries: []keyEntry{
{key: "space", desc: "play/pause"},
{key: "q", desc: "quit"},
{key: "?", desc: "help"},
},
},
{
title: "Seeking",
entries: []keyEntry{
{key: "h/←", desc: "seek -5s"},
{key: "l/→", desc: "seek +5s"},
{key: "H", desc: "seek -30s"},
{key: "L", desc: "seek +30s"},
{key: "g/G", desc: "start/end"},
},
},
{
title: "Nav",
entries: []keyEntry{
{key: "j/↓", desc: "next cue"},
{key: "k/↑", desc: "prev cue"},
{key: "ctrl+d/u", desc: "jump 5 cues"},
},
},
{
title: "Edit",
entries: []keyEntry{
{key: "i", desc: "edit transcript"},
},
},
}
// Build legend rows
var rows []string
for _, group := range groups {
rows = append(rows, renderGroup(group.title, group.entries))
}
return lipgloss.JoinHorizontal(
lipgloss.Left,
lipgloss.NewStyle().Width(m.Width).Render(strings.Join(rows, " │ ")),
)
}
type keyEntry struct {
key string
desc string
}
func renderGroup(title string, entries []keyEntry) string {
var keyParts []string
var descParts []string
for _, e := range entries {
keyParts = append(keyParts, ui.HelpKeyStyle.Render(e.key))
descParts = append(descParts, ui.HelpDescStyle.Render(e.desc))
}
keyStr := strings.Join(keyParts, " ")
descStr := strings.Join(descParts, " ")
return lipgloss.JoinHorizontal(
lipgloss.Left,
ui.BaseStyle.Render(title+":"),
" ",
keyStr,
" ",
descStr,
)
}

View File

@@ -96,6 +96,11 @@ var (
HelpDescStyle = lipgloss.NewStyle().
Foreground(ColorMuted)
// Legend styles
LegendGroupStyle = lipgloss.NewStyle().
Foreground(ColorMuted).
PaddingLeft(2)
// Error styles
ErrorStyle = lipgloss.NewStyle().
Foreground(ColorError).