GUCJEGHSHOTGO333BQSUX44S5YIVIVEPMLLFIRFXD4KD6APBMOYQC }// renderLabelDialog renders the label input dialogfunc (m Model) renderLabelDialog(b *strings.Builder) {before := m.labelText[:m.labelCursor]after := m.labelText[m.labelCursor:]inputLine := before + "█" + aftercharCount := fmt.Sprintf("%d/%d", len(m.labelText), maxLabelLength)content := fmt.Sprintf("Label (Species+CallType):\n%s\n%s\n%s", inputLine, charCount, labelHelpText)b.WriteString(commentBoxStyle.Render(content))
}// handleLabelKey handles key presses in label modefunc (m Model) handleLabelKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {key := msg.Key()// Enter: save labelif key.Code == tea.KeyEnter {return m.handleLabelEnter()}// Escape: cancelif key.Code == tea.KeyEscape || key.Code == tea.KeyEsc {m.labelMode = falsereturn m, nil}// Navigation and editing keysif handled := m.handleLabelKeyCode(key); handled {return m, nil}// Ctrl combosif handled := m.handleLabelCtrl(msg.String()); handled {return m, nil}// Printable ASCII character (space handled via KeySpace below)s := msg.String()if len(s) == 1 && s[0] >= 33 && s[0] <= 126 {if len(m.labelText) < maxLabelLength {m.labelText = m.labelText[:m.labelCursor] + s + m.labelText[m.labelCursor:]m.labelCursor++}}return m, nil
species, callType := datafile.ParseSpeciesCallType(m.labelText)if species == "" {return m, nil}m.stopPlayer()m.state.ApplyBinding(&calls.BindingResult{Species: species, CallType: callType})if err := m.state.Save(); err != nil {m.err = err.Error()}return m.advanceOrQuit()}// handleLabelKeyCode handles navigation and editing keys in label mode.// Returns true if the key was consumed.func (m *Model) handleLabelKeyCode(key tea.Key) bool {switch key.Code {case tea.KeyLeft:if m.labelCursor > 0 {m.labelCursor--}return truecase tea.KeyRight:if m.labelCursor < len(m.labelText) {m.labelCursor++}return truecase tea.KeySpace:if len(m.labelText) < maxLabelLength {m.labelText = m.labelText[:m.labelCursor] + " " + m.labelText[m.labelCursor:]m.labelCursor++}return truecase tea.KeyBackspace:if m.labelCursor > 0 {m.labelText = m.labelText[:m.labelCursor-1] + m.labelText[m.labelCursor:]m.labelCursor--}return truecase tea.KeyDelete:if m.labelCursor < len(m.labelText) {m.labelText = m.labelText[:m.labelCursor] + m.labelText[m.labelCursor+1:]}return true}return false}// handleLabelCtrl handles ctrl-key combos in label mode.// Returns true if the key was consumed.func (m *Model) handleLabelCtrl(s string) bool {switch s {case "ctrl+u":m.labelText = ""m.labelCursor = 0return truecase "ctrl+a":m.labelCursor = 0return truecase "ctrl+e":m.labelCursor = len(m.labelText)return true}return false}
const keyHelpText = "[esc]quit [,]prev [.]next [0]confirm [space]comment [ctrl+s]clip [ctrl+d]bookmark [ctrl+,]prev-bk [ctrl+.]next-bk [enter]play [shift+enter]½speed"
const keyHelpText = "[esc]quit [,]prev [.]next [0]confirm [space]comment [ctrl+l]label [ctrl+s]clip [ctrl+d]bookmark [ctrl+,]prev-bk [ctrl+.]next-bk [enter]play [shift+enter]½speed"
}// GetCurrentSpeciesCallType returns the species+calltype string for the current// segment's first filter label, using the Species+CallType format.// Returns "" if no matching label exists.func (s *ClassifyState) GetCurrentSpeciesCallType() string {seg := s.CurrentSegment()if seg == nil {return ""}filterLabels := seg.GetFilterLabels(s.Config.Filter)if len(filterLabels) == 0 {return ""}l := filterLabels[0]if l.CallType != "" {return l.Species + "+" + l.CallType}return l.Species