feat: unified navigation

fix: install script copying build binary
chore: updated readme
This commit is contained in:
2026-01-25 21:49:34 -06:00
parent 1bbfc332d8
commit 6165a10481
10 changed files with 273 additions and 209 deletions

View File

@@ -17,13 +17,6 @@ import (
"playback/internal/ui/waveform"
)
// FocusedView represents which view has focus
type FocusedView int
const (
FocusWaveform FocusedView = iota
FocusTranscript
)
// Model is the main application model
type Model struct {
@@ -44,7 +37,6 @@ type Model struct {
transcript transcript.Model
// State
focused FocusedView
showHelp bool
width int
height int
@@ -66,7 +58,6 @@ func New(audioPath, transcriptPath string) Model {
header: header.New(),
waveform: waveform.New(),
transcript: transcript.New(),
focused: FocusWaveform,
}
return m
@@ -144,7 +135,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.updateLayout()
case tea.KeyMsg:
// Global keys
// All shortcuts are now global
switch {
case key.Matches(msg, m.keys.Quit):
m.quitting = true
@@ -157,38 +148,25 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case key.Matches(msg, m.keys.PlayPause):
m.player.Toggle()
case key.Matches(msg, m.keys.FocusWaveform):
m.focused = FocusWaveform
m.waveform.SetFocused(true)
m.transcript.SetFocused(false)
case key.Matches(msg, m.keys.FocusTranscript):
m.focused = FocusTranscript
m.waveform.SetFocused(false)
m.transcript.SetFocused(true)
case key.Matches(msg, m.keys.EnterEdit):
if m.focused == FocusTranscript {
m.player.Pause()
return m, m.launchEditor()
}
}
m.player.Pause()
return m, m.launchEditor()
// Context-specific keys
if m.focused == FocusWaveform {
switch {
case key.Matches(msg, m.keys.SeekForward):
m.player.SeekRelative(m.config.SeekStep)
case key.Matches(msg, m.keys.SeekBackward):
m.player.SeekRelative(-m.config.SeekStep)
case key.Matches(msg, m.keys.SeekForwardBig):
m.player.SeekRelative(m.config.BigSeekStep)
case key.Matches(msg, m.keys.SeekBackwardBig):
m.player.SeekRelative(-m.config.BigSeekStep)
}
}
// Seeking shortcuts (previously waveform-only, now global)
case key.Matches(msg, m.keys.SeekForward):
m.player.SeekRelative(m.config.SeekStep)
if m.focused == FocusTranscript {
case key.Matches(msg, m.keys.SeekBackward):
m.player.SeekRelative(-m.config.SeekStep)
case key.Matches(msg, m.keys.SeekForwardBig):
m.player.SeekRelative(m.config.BigSeekStep)
case key.Matches(msg, m.keys.SeekBackwardBig):
m.player.SeekRelative(-m.config.BigSeekStep)
// Transcript navigation and other keys forward to transcript
default:
cmd := m.transcript.Update(msg)
cmds = append(cmds, cmd)
}
@@ -262,9 +240,6 @@ func (m *Model) updateLayout() {
m.header.SetWidth(m.width)
m.waveform.SetSize(m.width, waveformHeight)
m.transcript.SetSize(m.width, transcriptHeight)
m.waveform.SetFocused(m.focused == FocusWaveform)
m.transcript.SetFocused(m.focused == FocusTranscript)
}
func (m Model) launchEditor() tea.Cmd {
@@ -272,7 +247,7 @@ func (m Model) launchEditor() tea.Cmd {
if t == nil {
return nil
}
lineNum := m.transcript.SelectedCueLineNumber()
lineNum := m.transcript.ActiveCueLineNumber()
c := exec.Command(m.config.Editor, fmt.Sprintf("+%d", lineNum), t.FilePath)
return tea.ExecProcess(c, func(err error) tea.Msg {
return VimExitedMsg{Path: t.FilePath, Err: err}
@@ -330,13 +305,6 @@ func (m Model) renderStatus() string {
modeStyle := ui.ModeStyle
mode := modeStyle.Render(m.transcript.ModeString())
// Focus indicator
focusStr := "Waveform"
if m.focused == FocusTranscript {
focusStr = "Transcript"
}
focus := ui.BaseStyle.Render(fmt.Sprintf("[%s]", focusStr))
// Status message
statusMsg := ui.StatusBarStyle.Render(m.statusMsg)
@@ -347,10 +315,8 @@ func (m Model) renderStatus() string {
lipgloss.Center,
mode,
" ",
focus,
" ",
statusMsg,
lipgloss.NewStyle().Width(m.width-lipgloss.Width(mode)-lipgloss.Width(focus)-lipgloss.Width(statusMsg)-lipgloss.Width(helpHint)-8).Render(""),
lipgloss.NewStyle().Width(m.width-lipgloss.Width(mode)-lipgloss.Width(statusMsg)-lipgloss.Width(helpHint)-4).Render(""),
helpHint,
)
}