Files
nib-v1/internal/tui/linkpicker.go
T
lerko 4517b2e37c
CI / test (pull_request) Successful in 2m25s
feat(tui): include backlinks in link picker
Link picker now shows both outgoing [[links]] and backlinks in
a unified list with section headers. Backlinks follow by entity
ID directly, outgoing links resolve by text. Navigating into a
backlink works the same as following an outgoing link — pushes
to nav stack, esc pops back.
2026-05-21 14:12:28 -04:00

109 lines
2.2 KiB
Go

package tui
import (
"strings"
"github.com/lerko/nib/internal/db"
"github.com/lerko/nib/internal/link"
)
type linkKind int
const (
linkOutgoing linkKind = iota
linkBacklink
)
type linkItem struct {
text string
entityID string
kind linkKind
}
type linkPickerModel struct {
items []linkItem
cursor int
}
func newLinkPicker(body string, backlinks []db.Backlink) linkPickerModel {
var items []linkItem
for _, lt := range link.ExtractLinks(body) {
items = append(items, linkItem{text: lt, kind: linkOutgoing})
}
for _, bl := range backlinks {
label := bl.Body
if bl.Title != nil {
label = *bl.Title
} else if len(label) > 50 {
label = label[:50] + "…"
}
items = append(items, linkItem{text: label, entityID: bl.EntityID, kind: linkBacklink})
}
return linkPickerModel{items: items}
}
func (lp linkPickerModel) selected() linkItem {
if len(lp.items) == 0 || lp.cursor >= len(lp.items) {
return linkItem{}
}
return lp.items[lp.cursor]
}
func (lp linkPickerModel) update(key string) linkPickerModel {
switch key {
case "up", "k":
if lp.cursor > 0 {
lp.cursor--
}
case "down", "j":
if lp.cursor < len(lp.items)-1 {
lp.cursor++
}
}
return lp
}
func (lp linkPickerModel) view(width int) string {
var b strings.Builder
b.WriteString(titleStyle.Render("follow link") + "\n\n")
if len(lp.items) == 0 {
b.WriteString(hintDescStyle.Render(" no links or backlinks"))
b.WriteString("\n\n")
b.WriteString(helpStyle.Render("esc:back"))
return b.String()
}
prevKind := linkKind(-1)
for i, item := range lp.items {
if item.kind != prevKind {
if item.kind == linkOutgoing {
b.WriteString(dateHeaderStyle.Render("── outgoing ──") + "\n")
} else {
b.WriteString(dateHeaderStyle.Render("── backlinks ──") + "\n")
}
prevKind = item.kind
}
var label string
if item.kind == linkOutgoing {
label = "[[" + item.text + "]]"
} else {
label = "← " + item.text
}
if i == lp.cursor {
b.WriteString(selectedItemStyle.Render(" " + label))
} else {
b.WriteString(listItemStyle.Render(label))
}
b.WriteString("\n")
}
b.WriteString("\n")
b.WriteString(helpStyle.Render("enter:follow esc:back"))
return b.String()
}