aboutsummaryrefslogtreecommitdiff
path: root/internal/tui/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui/reader.go')
-rw-r--r--internal/tui/reader.go97
1 files changed, 81 insertions, 16 deletions
diff --git a/internal/tui/reader.go b/internal/tui/reader.go
index 09d3210..72b7d0a 100644
--- a/internal/tui/reader.go
+++ b/internal/tui/reader.go
@@ -31,6 +31,25 @@ const (
// selStyle marks the picker's selected row (reverse video, legible on any theme).
var selStyle = lipgloss.NewStyle().Reverse(true)
+// modalStyle / modalTitleStyle render the bookmark note box and the delete
+// confirmation as a prominent centered, bordered dialog (not a footer line).
+var (
+ modalStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).Padding(1, 3)
+ modalTitleStyle = lipgloss.NewStyle().Bold(true)
+)
+
+// modal centres inner in a bordered box over the whole screen.
+func (m ReaderModel) modal(inner string) string {
+ w, h := m.width, m.height
+ if w <= 0 {
+ w = 80
+ }
+ if h <= 0 {
+ h = 24
+ }
+ return lipgloss.Place(w, h, lipgloss.Center, lipgloss.Center, modalStyle.Render(inner))
+}
+
// ReaderModel is lectio-ui's --reader mode: a fuzzy book picker over the sigla
// dialect's book names, then a scrolling view of a chapter from the embedded
// corpora with chapter navigation and version cycling. It implements tea.Model
@@ -62,6 +81,8 @@ type ReaderModel struct {
markVerseIdx int // verse cursor in modeMarkVerse (index into verses)
markVerse int // the chosen verse number carried into modeMark
markNote string // note being typed in modeMark
+ markTags string // tags being typed in modeMark
+ markField int // 0 = note field, 1 = tags field
marks []bookmarks.Bookmark // loaded list for modeBookmarks
markSel int // selection in the bookmarks list
markTop int // list scroll offset
@@ -328,6 +349,8 @@ func (m ReaderModel) updateMarkVerse(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
if m.markVerseIdx >= 0 && m.markVerseIdx < len(m.verses) {
m.markVerse = m.verses[m.markVerseIdx].Verse
m.markNote = ""
+ m.markTags = ""
+ m.markField = 0
m.mode = modeMark
}
return m, nil
@@ -335,31 +358,39 @@ func (m ReaderModel) updateMarkVerse(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
return m, nil
}
-// updateMark handles the note box for the chosen verse: type an optional note,
-// Enter saves (an empty note is fine -- "skip"), Esc cancels.
+// updateMark handles the note/tags box for the chosen verse: type into the
+// active field, Tab switches note<->tags, Enter saves (both optional), Esc
+// cancels.
func (m ReaderModel) updateMark(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
+ field := &m.markNote
+ if m.markField == 1 {
+ field = &m.markTags
+ }
switch msg.Type {
case tea.KeyCtrlC:
return m, tea.Quit
case tea.KeyEsc:
m.mode = modeRead
- m.markNote = ""
+ m.markNote, m.markTags = "", ""
+ return m, nil
+ case tea.KeyTab, tea.KeyShiftTab, tea.KeyDown, tea.KeyUp:
+ m.markField = 1 - m.markField
return m, nil
case tea.KeyEnter:
- m = m.saveBookmark(m.markVerse, m.markNote)
- m.markNote = ""
+ m = m.saveBookmark(m.markVerse, m.markNote, m.markTags)
+ m.markNote, m.markTags = "", ""
m.mode = modeRead
return m, nil
case tea.KeyBackspace:
- if r := []rune(m.markNote); len(r) > 0 {
- m.markNote = string(r[:len(r)-1])
+ if r := []rune(*field); len(r) > 0 {
+ *field = string(r[:len(r)-1])
}
return m, nil
case tea.KeySpace:
- m.markNote += " "
+ *field += " "
return m, nil
case tea.KeyRunes:
- m.markNote += string(msg.Runes)
+ *field += string(msg.Runes)
return m, nil
}
return m, nil
@@ -524,7 +555,7 @@ func (m ReaderModel) scrollToVerse(verse int) int {
// saveBookmark stores a bookmark of the current book+chapter at the chosen
// verse with an optional note, and sets a flash message.
-func (m ReaderModel) saveBookmark(verse int, note string) ReaderModel {
+func (m ReaderModel) saveBookmark(verse int, note, tags string) ReaderModel {
if m.store == nil || len(m.verses) == 0 {
return m
}
@@ -538,6 +569,7 @@ func (m ReaderModel) saveBookmark(verse int, note string) ReaderModel {
Chapter: chap,
Verse: verse,
Note: strings.TrimSpace(note),
+ Tags: bookmarks.ParseTags(tags),
})
m.flash = fmt.Sprintf("bookmarked %s %d:%d", book.Name, chap, verse)
return m
@@ -699,13 +731,31 @@ func (m ReaderModel) View() string {
switch m.mode {
case modePick:
return m.viewPick()
+ case modeMark:
+ return m.viewMark()
case modeBookmarks:
return m.viewBookmarks()
- default: // modeRead + modeMark (the mark prompt overlays the reading footer)
+ default: // modeRead + modeMarkVerse (verse cursor highlighted in the reading)
return m.viewRead()
}
}
+// viewMark renders the bookmark note/tags box as a prominent centered dialog.
+func (m ReaderModel) viewMark() string {
+ ui := i18n.Get(m.cfg.UILanguage)
+ b := m.books[m.bookIdx]
+ title := modalTitleStyle.Render(fmt.Sprintf("★ %s %d:%d", b.Name, m.currentChapter(), m.markVerse))
+ note := " " + ui.ReaderMarkNote + ": " + m.markNote
+ tags := " " + ui.ReaderMarkTags + ": " + m.markTags
+ if m.markField == 0 {
+ note += "▏"
+ } else {
+ tags += "▏"
+ }
+ inner := title + "\n\n" + note + "\n" + tags + "\n\n" + citationStyle.Render(ui.ReaderMarkHelp)
+ return m.modal(inner)
+}
+
// viewBookmarks renders the saved-bookmarks list.
func (m ReaderModel) viewBookmarks() string {
w := m.width
@@ -713,12 +763,27 @@ func (m ReaderModel) viewBookmarks() string {
w = 80
}
ui := i18n.Get(m.cfg.UILanguage)
- header := headerStyle.Width(w).Render(ui.ReaderBookmarksTitle)
- footerKeys := ui.ReaderBookmarksKeys
- if m.confirmDelete {
- footerKeys = ui.ReaderConfirmDelete
+
+ // Deleting -> a prominent centered confirmation dialog naming the bookmark.
+ if m.confirmDelete && m.markSel >= 0 && m.markSel < len(m.marks) {
+ bm := m.marks[m.markSel]
+ name := bm.Book
+ if idx := m.bookIndex(bm.Book); idx >= 0 {
+ name = m.books[idx].Name
+ }
+ ref := fmt.Sprintf("%s %d", name, bm.Chapter)
+ if bm.Verse > 0 {
+ ref += fmt.Sprintf(":%d", bm.Verse)
+ }
+ if bm.Note != "" {
+ ref += " — " + bm.Note
+ }
+ inner := modalTitleStyle.Render(ui.ReaderConfirmDelete) + "\n\n " + ref + "\n\n" + citationStyle.Render(ui.ReaderConfirmKeys)
+ return m.modal(inner)
}
- footer := footerStyle.Width(w).Render(footerKeys)
+
+ header := headerStyle.Width(w).Render(ui.ReaderBookmarksTitle)
+ footer := footerStyle.Width(w).Render(ui.ReaderBookmarksKeys)
if len(m.marks) == 0 {
return header + "\n" + citationStyle.Render(ui.ReaderNoBookmarks) + "\n" + footer
}