summaryrefslogtreecommitdiff
path: root/internal/tui/eventstream
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui/eventstream')
-rw-r--r--internal/tui/eventstream/doc.go2
-rw-r--r--internal/tui/eventstream/exportmodal.go17
-rw-r--r--internal/tui/eventstream/filtermodal.go19
-rw-r--r--internal/tui/eventstream/filtermodal_test.go50
-rw-r--r--internal/tui/eventstream/model.go164
-rw-r--r--internal/tui/eventstream/render.go5
-rw-r--r--internal/tui/eventstream/render_test.go2
-rw-r--r--internal/tui/eventstream/searchmodal.go17
-rw-r--r--internal/tui/eventstream/streamevent.go3
-rw-r--r--internal/tui/eventstream/streamevent_test.go3
10 files changed, 188 insertions, 94 deletions
diff --git a/internal/tui/eventstream/doc.go b/internal/tui/eventstream/doc.go
new file mode 100644
index 0000000..38bc854
--- /dev/null
+++ b/internal/tui/eventstream/doc.go
@@ -0,0 +1,2 @@
+// Package eventstream renders live event rows and interactive filtering controls.
+package eventstream
diff --git a/internal/tui/eventstream/exportmodal.go b/internal/tui/eventstream/exportmodal.go
index cf020f7..3c0e2cd 100644
--- a/internal/tui/eventstream/exportmodal.go
+++ b/internal/tui/eventstream/exportmodal.go
@@ -3,9 +3,9 @@ package eventstream
import (
"strings"
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/bubbles/v2/textinput"
+ tea "charm.land/bubbletea/v2"
+ "charm.land/lipgloss/v2"
)
type ExportModal struct {
@@ -18,7 +18,8 @@ func NewExportModal() ExportModal {
input := textinput.New()
input.Prompt = ""
input.CharLimit = 0
- input.Width = 44
+ input.SetWidth(44)
+ input.SetStyles(textinput.DefaultStyles(true))
return ExportModal{textInput: input}
}
@@ -26,6 +27,12 @@ func (m ExportModal) Visible() bool {
return m.visible
}
+// SetDarkMode updates export modal text input styles.
+func (m ExportModal) SetDarkMode(isDark bool) ExportModal {
+ m.textInput.SetStyles(textinput.DefaultStyles(isDark))
+ return m
+}
+
func (m ExportModal) Open(defaultName string) ExportModal {
m.visible = true
m.err = ""
@@ -47,7 +54,7 @@ func (m ExportModal) Update(msg tea.Msg) (ExportModal, string, bool) {
if !m.visible {
return m, "", false
}
- if keyMsg, ok := msg.(tea.KeyMsg); ok {
+ if keyMsg, ok := msg.(tea.KeyPressMsg); ok {
switch keyMsg.String() {
case "esc":
return m.Close(), "", false
diff --git a/internal/tui/eventstream/filtermodal.go b/internal/tui/eventstream/filtermodal.go
index f98db7f..bd20a03 100644
--- a/internal/tui/eventstream/filtermodal.go
+++ b/internal/tui/eventstream/filtermodal.go
@@ -5,9 +5,9 @@ import (
"strconv"
"strings"
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/bubbles/v2/textinput"
+ tea "charm.land/bubbletea/v2"
+ "charm.land/lipgloss/v2"
)
type fieldKey int
@@ -48,7 +48,8 @@ func NewFilterModal() FilterModal {
input := textinput.New()
input.Prompt = ""
input.CharLimit = 0
- input.Width = 24
+ input.SetWidth(24)
+ input.SetStyles(textinput.DefaultStyles(true))
m := FilterModal{textInput: input}
m.fields = defaultFilterFields()
@@ -63,6 +64,12 @@ func (m FilterModal) Filter() Filter {
return m.filter
}
+// SetDarkMode updates filter modal text input styles.
+func (m FilterModal) SetDarkMode(isDark bool) FilterModal {
+ m.textInput.SetStyles(textinput.DefaultStyles(isDark))
+ return m
+}
+
func (m FilterModal) Open(initial Filter) FilterModal {
m.visible = true
m.activeField = 0
@@ -86,7 +93,7 @@ func (m FilterModal) Update(msg tea.Msg) FilterModal {
return m
}
- if keyMsg, ok := msg.(tea.KeyMsg); ok {
+ if keyMsg, ok := msg.(tea.KeyPressMsg); ok {
switch keyMsg.String() {
case "esc":
if m.editing {
@@ -112,7 +119,7 @@ func (m FilterModal) Update(msg tea.Msg) FilterModal {
m.fields[m.activeField].opIndex = (m.fields[m.activeField].opIndex + 1) % len(compareOps)
}
return m
- case " ":
+ case " ", "space":
if !m.editing && m.fields[m.activeField].fieldKey == fieldErrorsOnly {
if strings.TrimSpace(m.fields[m.activeField].value) == "true" {
m.fields[m.activeField].value = "false"
diff --git a/internal/tui/eventstream/filtermodal_test.go b/internal/tui/eventstream/filtermodal_test.go
index ee53c82..a33cbb1 100644
--- a/internal/tui/eventstream/filtermodal_test.go
+++ b/internal/tui/eventstream/filtermodal_test.go
@@ -3,7 +3,7 @@ package eventstream
import (
"testing"
- tea "github.com/charmbracelet/bubbletea"
+ tea "charm.land/bubbletea/v2"
)
func TestFilterModalOpenClose(t *testing.T) {
@@ -17,7 +17,7 @@ func TestFilterModalOpenClose(t *testing.T) {
t.Fatalf("modal should be visible after open")
}
- m = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEsc})
if m.Visible() {
t.Fatalf("modal should close on esc")
}
@@ -29,11 +29,11 @@ func TestFilterModalNavigateFields(t *testing.T) {
t.Fatalf("activeField=%d, want 0", m.activeField)
}
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
if m.activeField != 1 {
t.Fatalf("activeField=%d, want 1", m.activeField)
}
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("k")})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("k")[0], Text: string([]rune("k"))})
if m.activeField != 0 {
t.Fatalf("activeField=%d, want 0", m.activeField)
}
@@ -43,34 +43,34 @@ func TestFilterModalEditAndBuildFilter(t *testing.T) {
m := NewFilterModal().Open(Filter{})
// Syscall = read
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("read")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("read")[0], Text: string([]rune("read"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
// PID >= 123
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyTab}) // '=' -> '>'
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("123")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyTab}) // '=' -> '>'
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("123")[0], Text: string([]rune("123"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
// Latency >= 1ms
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyTab}) // '=' -> '>='
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("1ms")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyTab}) // '=' -> '>='
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("1ms")[0], Text: string([]rune("1ms"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
// ErrorsOnly = true
for m.activeField < len(m.fields)-1 {
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("j")})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("j")[0], Text: string([]rune("j"))})
}
- m = m.Update(tea.KeyMsg{Type: tea.KeySpace})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeySpace})
- m = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEsc})
if m.Visible() {
t.Fatalf("modal should close on esc")
}
@@ -98,8 +98,8 @@ func TestFilterModalClearAll(t *testing.T) {
}
m := NewFilterModal().Open(initial)
- m = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("c")})
- m = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
+ m = m.Update(tea.KeyPressMsg{Code: []rune("c")[0], Text: string([]rune("c"))})
+ m = m.Update(tea.KeyPressMsg{Code: tea.KeyEsc})
f := m.Filter()
if f.IsActive() {
diff --git a/internal/tui/eventstream/model.go b/internal/tui/eventstream/model.go
index d9c4ee3..12aff4d 100644
--- a/internal/tui/eventstream/model.go
+++ b/internal/tui/eventstream/model.go
@@ -6,7 +6,8 @@ import (
"strconv"
"strings"
- tea "github.com/charmbracelet/bubbletea"
+ "charm.land/bubbles/v2/viewport"
+ tea "charm.land/bubbletea/v2"
)
const (
@@ -23,8 +24,14 @@ const (
streamColumnCount
)
+// Source is the minimal stream buffer contract needed by the stream model.
+type Source interface {
+ Len() int
+ Snapshot() []StreamEvent
+}
+
type Model struct {
- source *RingBuffer
+ source Source
allEvents []StreamEvent
filtered []StreamEvent
@@ -53,11 +60,13 @@ type Model struct {
pendingOpenPath string
statusMessage string
exportDir string
+ isDark bool
width int
height int
showFooter bool
+ viewport viewport.Model
}
type fdTraceViewState struct {
@@ -68,8 +77,8 @@ type fdTraceViewState struct {
offset int
}
-func NewModel(source *RingBuffer) Model {
- return Model{
+func NewModel(source Source) Model {
+ m := Model{
source: source,
filterModal: NewFilterModal(),
exportModal: NewExportModal(),
@@ -79,7 +88,25 @@ func NewModel(source *RingBuffer) Model {
selectedCol: 0,
exportDir: ".",
showFooter: true,
+ isDark: true,
+ viewport: newStreamViewport(),
}
+ m.SetDarkMode(true)
+ return m
+}
+
+func newStreamViewport() viewport.Model {
+ vp := viewport.New()
+ keyMap := viewport.DefaultKeyMap()
+ keyMap.Down.SetKeys("down", "j")
+ keyMap.Up.SetKeys("up", "k")
+ keyMap.Left.SetKeys("left", "h")
+ keyMap.Right.SetKeys("right", "l")
+ keyMap.PageDown.SetKeys("pgdown", "pgdn", "pagedown")
+ keyMap.PageUp.SetKeys("pgup", "pageup")
+ vp.KeyMap = keyMap
+ vp.SoftWrap = true
+ return vp
}
// SetViewport updates the render/scroll viewport dimensions used for
@@ -87,9 +114,11 @@ func NewModel(source *RingBuffer) Model {
func (m *Model) SetViewport(width, height int) {
if width > 0 {
m.width = width
+ m.viewport.SetWidth(width)
}
if height > 0 {
m.height = height
+ m.viewport.SetHeight(m.visibleRows())
}
}
@@ -99,11 +128,19 @@ func (m *Model) SetFooterVisible(visible bool) {
}
// SetSource updates the backing ring buffer and refreshes visible rows.
-func (m *Model) SetSource(source *RingBuffer) {
+func (m *Model) SetSource(source Source) {
m.source = source
m.Refresh()
}
+// SetDarkMode updates stream modal text input styles for the active theme.
+func (m *Model) SetDarkMode(isDark bool) {
+ m.isDark = isDark
+ m.filterModal = m.filterModal.SetDarkMode(isDark)
+ m.exportModal = m.exportModal.SetDarkMode(isDark)
+ m.searchModal = m.searchModal.SetDarkMode(isDark)
+}
+
// FilterModalVisible reports whether the filter modal is currently open.
func (m Model) FilterModalVisible() bool {
return m.filterModal.Visible()
@@ -284,7 +321,8 @@ func (m *Model) HandleKey(keyStr string) bool {
m.moveSelectionTo(len(m.filtered) - 1)
} else {
m.autoScroll = true
- m.scrollOffset = m.maxScrollOffset()
+ m.viewport.GotoBottom()
+ m.scrollOffset = clamp(m.viewport.YOffset(), 0, m.maxScrollOffset())
}
return true
case "g":
@@ -292,6 +330,7 @@ func (m *Model) HandleKey(keyStr string) bool {
m.moveSelectionTo(0)
} else {
m.autoScroll = false
+ m.viewport.GotoTop()
m.scrollOffset = 0
}
return true
@@ -305,14 +344,14 @@ func (m *Model) HandleKey(keyStr string) bool {
if m.paused {
m.moveSelectionBy(1)
} else {
- m.scrollByLines(1)
+ m.handleViewportUpdate(keyMsgFromString("down"))
}
return true
case "k", "up":
if m.paused {
m.moveSelectionBy(-1)
} else {
- m.scrollByLines(-1)
+ m.handleViewportUpdate(keyMsgFromString("up"))
}
return true
case "left", "h":
@@ -320,25 +359,25 @@ func (m *Model) HandleKey(keyStr string) bool {
m.moveSelectedColBy(-1)
return true
}
- return false
+ return m.handleViewportUpdate(keyMsgFromString("left"))
case "right", "l":
if m.paused {
m.moveSelectedColBy(1)
return true
}
- return false
+ return m.handleViewportUpdate(keyMsgFromString("right"))
case "pgdown", "pgdn", "pagedown":
if m.paused {
m.moveSelectionBy(m.pageStep())
} else {
- m.scrollByLines(m.pageStep())
+ m.handleViewportUpdate(keyMsgFromString("pgdown"))
}
return true
case "pgup", "pageup":
if m.paused {
m.moveSelectionBy(-m.pageStep())
} else {
- m.scrollByLines(-m.pageStep())
+ m.handleViewportUpdate(keyMsgFromString("pgup"))
}
return true
case "esc":
@@ -353,8 +392,12 @@ func (m *Model) HandleKey(keyStr string) bool {
// HandleTeaKey handles stream keys based on Bubble Tea key message types first,
// then falls back to string matching for rune-driven shortcuts.
-func (m *Model) HandleTeaKey(msg tea.KeyMsg) bool {
- switch msg.Type {
+func (m *Model) HandleTeaKey(msg tea.KeyPressMsg) bool {
+ if m.handleViewportUpdate(msg) {
+ return true
+ }
+
+ switch msg.Code {
case tea.KeyLeft:
return m.HandleKey("left")
case tea.KeyRight:
@@ -373,14 +416,45 @@ func (m *Model) HandleTeaKey(msg tea.KeyMsg) bool {
return m.HandleKey("esc")
case tea.KeyEnter:
return m.HandleKey("enter")
- case tea.KeyRunes:
- if len(msg.Runes) == 1 {
- return m.HandleKey(string(msg.Runes[0]))
+ default:
+ if msg.Text != "" {
+ runes := []rune(msg.Text)
+ if len(runes) == 1 {
+ return m.HandleKey(msg.Text)
+ }
}
}
return m.HandleKey(msg.String())
}
+func (m *Model) handleViewportUpdate(msg tea.KeyPressMsg) bool {
+ if m.paused || m.fdTraceView.visible || m.filterModal.Visible() || m.exportModal.Visible() || m.searchModal.Visible() {
+ return false
+ }
+
+ switch msg.String() {
+ case "down", "j", "up", "k", "left", "h", "right", "l", "pgup", "pageup", "pgdown", "pgdn", "pagedown":
+ default:
+ return false
+ }
+
+ switch msg.String() {
+ case "pgup", "pageup":
+ m.viewport.ScrollUp(m.pageStep())
+ case "pgdown", "pgdn", "pagedown":
+ m.viewport.ScrollDown(m.pageStep())
+ default:
+ vp, cmd := m.viewport.Update(msg)
+ _ = cmd
+ m.viewport = vp
+ }
+ m.scrollOffset = clamp(m.viewport.YOffset(), 0, m.maxScrollOffset())
+ if m.scrollOffset < m.maxScrollOffset() {
+ m.autoScroll = false
+ }
+ return true
+}
+
func (m *Model) View(width, height int) string {
if width <= 0 {
width = 100
@@ -390,13 +464,16 @@ func (m *Model) View(width, height int) string {
}
m.width = width
m.height = height
+ m.viewport.SetWidth(width)
+ m.viewport.SetHeight(m.visibleRows())
if m.fdTraceView.visible {
return m.viewFDTrace(width)
}
rows := m.visibleRows()
- start := clamp(m.scrollOffset, 0, m.maxScrollOffset())
+ start := clamp(m.viewport.YOffset(), 0, m.maxScrollOffset())
+ m.scrollOffset = start
end := start + rows
if end > len(m.filtered) {
end = len(m.filtered)
@@ -464,6 +541,8 @@ func (m *Model) Refresh() {
m.allEvents = []StreamEvent{}
m.filtered = []StreamEvent{}
m.scrollOffset = 0
+ m.viewport.SetContentLines(nil)
+ m.viewport.SetYOffset(0)
return
}
@@ -476,6 +555,8 @@ func (m *Model) applyFilter() {
m.filtered = []StreamEvent{}
m.scrollOffset = 0
m.selectedIdx = -1
+ m.viewport.SetContentLines(nil)
+ m.viewport.SetYOffset(0)
return
}
@@ -487,12 +568,18 @@ func (m *Model) applyFilter() {
}
}
m.filtered = filtered
+ m.viewport.SetWidth(m.width)
+ m.viewport.SetHeight(m.visibleRows())
+ lines := make([]string, len(m.filtered))
+ m.viewport.SetContentLines(lines)
max := m.maxScrollOffset()
if m.autoScroll {
- m.scrollOffset = max
+ m.viewport.GotoBottom()
+ m.scrollOffset = clamp(m.viewport.YOffset(), 0, max)
} else {
m.scrollOffset = clamp(m.scrollOffset, 0, max)
+ m.viewport.SetYOffset(m.scrollOffset)
}
m.clampSelection()
if m.paused {
@@ -529,26 +616,6 @@ func (m *Model) pageStep() int {
return rows - 1
}
-func (m *Model) scrollByLines(delta int) {
- if delta == 0 {
- return
- }
- max := m.maxScrollOffset()
- next := m.scrollOffset + delta
- if next < 0 {
- next = 0
- }
- if next > max {
- next = max
- }
- if next != m.scrollOffset {
- m.scrollOffset = next
- }
- if m.scrollOffset < max {
- m.autoScroll = false
- }
-}
-
func (m *Model) openFDTraceView() bool {
if m.fdTraceView.visible || m.selectedIdx < 0 || m.selectedIdx >= len(m.filtered) {
return false
@@ -646,6 +713,7 @@ func (m *Model) centerSelection() {
mid := m.visibleRows() / 2
target := m.selectedIdx - mid
m.scrollOffset = clamp(target, 0, m.maxScrollOffset())
+ m.viewport.SetYOffset(m.scrollOffset)
}
func (m *Model) ensureSelection() {
@@ -807,26 +875,26 @@ func (m *Model) clampSelection() {
m.selectedIdx = clamp(m.selectedIdx, 0, len(m.filtered)-1)
}
-func keyMsgFromString(keyStr string) tea.KeyMsg {
+func keyMsgFromString(keyStr string) tea.KeyPressMsg {
switch keyStr {
case "esc":
- return tea.KeyMsg{Type: tea.KeyEsc}
+ return tea.KeyPressMsg{Code: tea.KeyEsc}
case "enter":
- return tea.KeyMsg{Type: tea.KeyEnter}
+ return tea.KeyPressMsg{Code: tea.KeyEnter}
case "tab":
- return tea.KeyMsg{Type: tea.KeyTab}
+ return tea.KeyPressMsg{Code: tea.KeyTab}
case "up":
- return tea.KeyMsg{Type: tea.KeyUp}
+ return tea.KeyPressMsg{Code: tea.KeyUp}
case "down":
- return tea.KeyMsg{Type: tea.KeyDown}
+ return tea.KeyPressMsg{Code: tea.KeyDown}
case " ", "space":
- return tea.KeyMsg{Type: tea.KeySpace}
+ return tea.KeyPressMsg{Code: tea.KeySpace, Text: " "}
}
if keyStr == "" {
- return tea.KeyMsg{}
+ return tea.KeyPressMsg{}
}
runes := []rune(keyStr)
- return tea.KeyMsg{Type: tea.KeyRunes, Runes: runes}
+ return tea.KeyPressMsg{Code: runes[0], Text: keyStr}
}
func rowNumber(start, total int) int {
diff --git a/internal/tui/eventstream/render.go b/internal/tui/eventstream/render.go
index 1f539c6..3ec4d65 100644
--- a/internal/tui/eventstream/render.go
+++ b/internal/tui/eventstream/render.go
@@ -2,11 +2,12 @@ package eventstream
import (
"fmt"
- "ior/internal/tui/common"
"strconv"
"strings"
- "github.com/charmbracelet/lipgloss"
+ "ior/internal/tui/common"
+
+ "charm.land/lipgloss/v2"
)
type columnLayout struct {
diff --git a/internal/tui/eventstream/render_test.go b/internal/tui/eventstream/render_test.go
index b020edf..6240c69 100644
--- a/internal/tui/eventstream/render_test.go
+++ b/internal/tui/eventstream/render_test.go
@@ -4,7 +4,7 @@ import (
"strings"
"testing"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/lipgloss/v2"
)
func TestRenderStatusAndFilterLines(t *testing.T) {
diff --git a/internal/tui/eventstream/searchmodal.go b/internal/tui/eventstream/searchmodal.go
index f744d00..c09542b 100644
--- a/internal/tui/eventstream/searchmodal.go
+++ b/internal/tui/eventstream/searchmodal.go
@@ -3,9 +3,9 @@ package eventstream
import (
"strings"
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/bubbles/v2/textinput"
+ tea "charm.land/bubbletea/v2"
+ "charm.land/lipgloss/v2"
)
type SearchDirection int
@@ -26,7 +26,8 @@ func NewSearchModal() SearchModal {
input := textinput.New()
input.Prompt = ""
input.CharLimit = 0
- input.Width = 44
+ input.SetWidth(44)
+ input.SetStyles(textinput.DefaultStyles(true))
return SearchModal{textInput: input, direction: SearchForward}
}
@@ -38,6 +39,12 @@ func (m SearchModal) Direction() SearchDirection {
return m.direction
}
+// SetDarkMode updates search modal text input styles.
+func (m SearchModal) SetDarkMode(isDark bool) SearchModal {
+ m.textInput.SetStyles(textinput.DefaultStyles(isDark))
+ return m
+}
+
func (m SearchModal) Open(direction SearchDirection, defaultTerm string) SearchModal {
m.visible = true
m.err = ""
@@ -60,7 +67,7 @@ func (m SearchModal) Update(msg tea.Msg) (SearchModal, string, bool) {
if !m.visible {
return m, "", false
}
- if keyMsg, ok := msg.(tea.KeyMsg); ok {
+ if keyMsg, ok := msg.(tea.KeyPressMsg); ok {
switch keyMsg.String() {
case "esc":
return m.Close(), "", false
diff --git a/internal/tui/eventstream/streamevent.go b/internal/tui/eventstream/streamevent.go
index dbe04dd..5f1e27f 100644
--- a/internal/tui/eventstream/streamevent.go
+++ b/internal/tui/eventstream/streamevent.go
@@ -1,9 +1,10 @@
package eventstream
import (
+ "time"
+
"ior/internal/event"
"ior/internal/types"
- "time"
)
type StreamEvent struct {
diff --git a/internal/tui/eventstream/streamevent_test.go b/internal/tui/eventstream/streamevent_test.go
index 6131fed..dd65dd1 100644
--- a/internal/tui/eventstream/streamevent_test.go
+++ b/internal/tui/eventstream/streamevent_test.go
@@ -1,10 +1,11 @@
package eventstream
import (
+ "testing"
+
"ior/internal/event"
"ior/internal/file"
"ior/internal/types"
- "testing"
)
func TestNewStreamEventPopulatesFields(t *testing.T) {