Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package renovate-pretty-log for 
openSUSE:Factory checked in at 2026-03-23 17:13:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/renovate-pretty-log (Old)
 and      /work/SRC/openSUSE:Factory/.renovate-pretty-log.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "renovate-pretty-log"

Mon Mar 23 17:13:29 2026 rev:2 rq:1341913 version:0.5.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/renovate-pretty-log/renovate-pretty-log.changes  
2025-06-13 18:47:32.651984906 +0200
+++ 
/work/SRC/openSUSE:Factory/.renovate-pretty-log.new.8177/renovate-pretty-log.changes
        2026-03-23 17:14:37.504810874 +0100
@@ -1,0 +2,64 @@
+Mon Mar 09 07:53:12 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.5.2:
+  * Bug Fixes
+    - f67ff8d1 fix(tui): don't show "More ..." indicator when at
+      bottom
+- Update to version 0.5.2:
+  * Bug Fixes
+    - de60a406 fix(tui): don't add an additional line when
+      displaying "More ..." indicator
+- Update to version 0.5.0:
+  * Features
+    - 9716e18e feat(tui): default to debug logs
+- Update to version 0.4.0:
+  * Features
+    - 18aafdb2 feat: add tool to convert from text-based logs to
+      JSONL
+- Update to version 0.3.0:
+  * Features
+    - 8141d99f feat(tui): add search interface
+  * Others
+    - 49984a69 fixup! chore(deps): upgrade to Chroma v2
+- Update to version 0.2.0:
+  * Features
+    - 2f8559ff feat(styles): always pretty-print fields with Chroma
+    - 39755f6f feat(styles): use Srcery colour scheme for JSON
+    - 78aacf91 feat(tui): colour code JSON
+    - 0a6a286b feat(tui): rewrite UI to be split-pane
+    - 4da6971e feat: allow specifying -max-line-size
+  * Bug Fixes
+    - dffdc26d fix(styles): don't re-use bytes.Buffer in the loop
+    - b843db7c fix(tui): log when failing to read log file
+    - 8e140801 fix(tui): parse log lines up to 10MB
+  * Others
+    - 67cdc2e9 Merge branch 'rewrite' into 'main'
+    - 045ee925 chore(deps): upgrade to Chroma v2
+
+-------------------------------------------------------------------
+Thu Mar 05 10:24:01 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.1.6:
+  * ci: use `CI_JOB_TOKEN` for publishing
+
+-------------------------------------------------------------------
+Thu Mar 05 10:18:51 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.1.5:
+  * ci: override `goreleaser` entrypoint
+
+-------------------------------------------------------------------
+Thu Mar 05 10:15:37 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.1.4:
+  * ci: correct Docker image path
+  * docs: add Claude usage
+
+-------------------------------------------------------------------
+Thu Mar 05 10:13:30 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- Update to version 0.1.3:
+  * ci: add Goreleaser setup
+  * Add renovate.json
+
+-------------------------------------------------------------------

Old:
----
  renovate-pretty-log-0.1.2.obscpio

New:
----
  renovate-pretty-log-0.5.2.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ renovate-pretty-log.spec ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old  2026-03-23 17:14:38.752862780 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new  2026-03-23 17:14:38.752862780 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           renovate-pretty-log
-Version:        0.1.2
+Version:        0.5.2
 Release:        0
 Summary:        Two utilities for exploring Renovate debug log files
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old  2026-03-23 17:14:38.796864610 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new  2026-03-23 17:14:38.800864777 +0100
@@ -3,7 +3,7 @@
     <param 
name="url">https://gitlab.com/tanna.dev/renovate-pretty-log.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">refs/tags/v0.1.2</param>
+    <param name="revision">refs/tags/v0.5.2</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old  2026-03-23 17:14:38.824865775 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new  2026-03-23 17:14:38.828865941 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://gitlab.com/tanna.dev/renovate-pretty-log.git</param>
-              <param 
name="changesrevision">c29241031096ff5d3f36a751167f88440a79eae5</param></service></servicedata>
+              <param 
name="changesrevision">f67ff8d1e2034f80b0b5b2b8be58d15438388c2e</param></service></servicedata>
 (No newline at EOF)
 

++++++ renovate-pretty-log-0.1.2.obscpio -> renovate-pretty-log-0.5.2.obscpio 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/.goreleaser.yaml 
new/renovate-pretty-log-0.5.2/.goreleaser.yaml
--- old/renovate-pretty-log-0.1.2/.goreleaser.yaml      1970-01-01 
01:00:00.000000000 +0100
+++ new/renovate-pretty-log-0.5.2/.goreleaser.yaml      2026-03-06 
16:03:10.000000000 +0100
@@ -0,0 +1,121 @@
+version: 2
+
+before:
+  hooks:
+    - go mod tidy
+
+builds:
+  - id: renovate-pretty-log
+    main: ./cmd/renovate-pretty-log
+    binary: renovate-pretty-log
+    flags:
+      - -trimpath
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - linux
+      - darwin
+      - windows
+    goarch:
+      - amd64
+      - arm64
+    ignore:
+      - goarch: "386"
+        goos: windows
+    ldflags:
+      - -s -w
+      - -X main.version={{.Version}}
+      - -X main.commit={{.Commit}}
+      - -X main.date={{.Date}}
+
+  - id: renovate-pretty-log-tui
+    main: ./cmd/renovate-pretty-log-tui
+    binary: renovate-pretty-log-tui
+    flags:
+      - -trimpath
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - linux
+      - darwin
+      - windows
+    goarch:
+      - amd64
+      - arm64
+    ignore:
+      - goarch: "386"
+        goos: windows
+    ldflags:
+      - -s -w
+      - -X main.version={{.Version}}
+      - -X main.commit={{.Commit}}
+      - -X main.date={{.Date}}
+
+  - id: renovate-text-to-json
+    main: ./cmd/renovate-text-to-json
+    binary: renovate-text-to-json
+    flags:
+      - -trimpath
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - linux
+      - darwin
+      - windows
+    goarch:
+      - amd64
+      - arm64
+    ignore:
+      - goarch: "386"
+        goos: windows
+    ldflags:
+      - -s -w
+      - -X main.version={{.Version}}
+      - -X main.commit={{.Commit}}
+      - -X main.date={{.Date}}
+
+archives:
+  - id: no-version
+    formats:
+      - zip
+    name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
+    wrap_in_directory: false
+    files:
+      - LICENSE
+      - README.md
+
+checksum:
+  name_template: 'checksums.txt'
+
+snapshot:
+  version_template: "{{ incpatch .Version }}-next"
+
+changelog:
+  sort: asc
+  filters:
+    exclude:
+      - '^docs:'
+      - '^test:'
+      - '^ci:'
+      - '^chore:'
+      - 'README'
+  groups:
+    - title: Features
+      regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
+      order: 0
+    - title: Bug Fixes
+      regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
+      order: 1
+    - title: Others
+      order: 999
+
+gitlab_urls:
+  api: https://gitlab.com/api/v4/
+  download: https://gitlab.com
+  use_package_registry: true
+  use_job_token: true
+
+release:
+  gitlab:
+    owner: tanna.dev
+    name: renovate-pretty-log
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/README.md 
new/renovate-pretty-log-0.5.2/README.md
--- old/renovate-pretty-log-0.1.2/README.md     2025-05-23 18:08:47.000000000 
+0200
+++ new/renovate-pretty-log-0.5.2/README.md     2026-03-06 16:03:10.000000000 
+0100
@@ -1,6 +1,12 @@
 # `renovate-pretty-log`
 
-Two utilities for exploring [Renovate](https://docs.renovatebot.com/) debug 
log files.
+Three utilities for exploring [Renovate](https://docs.renovatebot.com/) debug 
log files.
+
+## Installing
+
+There are pre-built binaries [attached to the GitLab 
Releases](https://gitlab.com/tanna.dev/renovate-pretty-log/-/releases/), which 
contains `renovate-pretty-log`, `renovate-pretty-log-tui`, and 
`renovate-text-to-json`.
+
+You can also see these [in the GitLab Package 
Registry](https://gitlab.com/tanna.dev/renovate-pretty-log/-/packages).
 
 ## `cmd/renovate-pretty-log-tui`
 
@@ -44,6 +50,16 @@
 renovate-pretty-log -path /path/to/debug.log -level debug
 ```
 
+## `cmd/renovate-text-to-json`
+
+Given a plain-text "pretty" log output from Renovate, convert it to JSONL 
format for `renovate-pretty-log` or `renovate-pretty-log-tui`.
+
+This can also allow usage such as:
+
+```sh
+renovate-text-to-json < log-lines.txt | renovate-pretty-log-tui -path 
/dev/stdin
+```
+
 ## License
 
 This project is licensed under the Apache-2.0 license.
@@ -54,3 +70,4 @@
 - claude:3.7-sonnet-thinking
 - gpt:4o
 - gpt:4.1
+- Claude Sonnet 4.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log/main.go 
new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log/main.go       
2025-05-23 18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log/main.go       
2026-03-06 16:03:10.000000000 +0100
@@ -44,6 +44,7 @@
 
        levelFlag := flag.String("level", "info", "Set the log level (trace, 
debug, info, warn, error, fatal)")
        pathFlag := flag.String("path", "", "The path to the debug log file 
(`RENOVATE_DEBUG_LOG_FILE`)")
+       maxLineSizeMB := flag.Int("max-line-size", 10, "Maximum line size in MB 
(default: 10)")
        flag.Parse()
 
        if pathFlag == nil {
@@ -65,6 +66,10 @@
 
        defer f.Close()
        scanner := bufio.NewScanner(f)
+       // via https://stackoverflow.com/a/37455465, make sure we can handle 
lines up to configured size
+       buf := make([]byte, 0, bufio.MaxScanTokenSize)
+       maxBytes := *maxLineSizeMB * 1024 * 1024
+       scanner.Buffer(buf, maxBytes)
        for scanner.Scan() {
                var line log.Line
                data := scanner.Bytes()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui/main.go 
new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log-tui/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui/main.go   
2025-05-23 18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log-tui/main.go   
2026-03-06 16:03:10.000000000 +0100
@@ -8,36 +8,45 @@
        "os"
        "strings"
 
+       "github.com/charmbracelet/bubbles/textinput"
+       "github.com/charmbracelet/bubbles/viewport"
        tea "github.com/charmbracelet/bubbletea"
+       "github.com/charmbracelet/lipgloss"
        charmlog "github.com/charmbracelet/log"
        "gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
        "gitlab.com/tanna.dev/renovate-pretty-log/internal/style"
 )
 
-// parentViewportHeight indicates how many lines of logs will be shown on a 
given "page" of logs
-const parentViewportHeight = 30
-
-// extrasViewportHeight indicates how many lines of "extra" metadata will be 
shown under a given log, if expanded
-const extrasViewportHeight = 20
-
 type model struct {
-       // logs contains the log lines themselves. They must be pre-filtered
-       logs   []log.Line
-       cursor int
-       // parentViewportStart is the index of the first visible log
-       parentViewportStart int
-       // expanded tracks which indexes are currently expanded
-       expanded map[int]bool
-       // extrasExpandedViewport allows expanding a given log.Line's Extras, 
to show any additional context the log line has
-       extrasExpandedViewport extrasExpandedViewport
+       logs           []log.Line
+       filteredLogs   []int // indices into logs array
+       cursor         int
+       logsViewport   viewport.Model
+       extrasViewport viewport.Model
+       ready          bool
+       width          int
+       height         int
+       searchMode     bool
+       searchInput    textinput.Model
+       searchQuery    string
 }
 
 func newModel(lines []log.Line) model {
+       ti := textinput.New()
+       ti.Placeholder = "Search log messages..."
+       ti.CharLimit = 100
+
+       // Initialize filteredLogs with all indices
+       filteredLogs := make([]int, len(lines))
+       for i := range lines {
+               filteredLogs[i] = i
+       }
+
        return model{
-               logs:                lines,
-               cursor:              0,
-               parentViewportStart: 0,
-               expanded:            make(map[int]bool),
+               logs:         lines,
+               filteredLogs: filteredLogs,
+               cursor:       0,
+               searchInput:  ti,
        }
 }
 
@@ -47,278 +56,296 @@
 
 func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
        switch msg := msg.(type) {
-       case tea.KeyMsg:
-               switch msg.String() {
-               case
-                       "up",
-                       "k":
-                       m.MoveUp()
-               case
-                       "down",
-                       "j":
-                       m.MoveDown()
-               case
-                       "ctrl+u",
-                       "pgup":
-                       m.cursor -= parentViewportHeight
-                       if m.cursor < 0 {
-                               m.cursor = 0
-                       }
-                       m.parentViewportStart -= parentViewportHeight
-                       if m.parentViewportStart < 0 {
-                               m.parentViewportStart = 0
-                       }
-
-               case
-                       "ctrl+d",
-                       "pgdown":
-                       m.cursor += parentViewportHeight
+       case tea.WindowSizeMsg:
+               m.width = msg.Width
+               m.height = msg.Height
+
+               footerHeight := 1
+               searchBarHeight := 0
+               if m.searchMode {
+                       searchBarHeight = 1
+               }
+               availableHeight := m.height - footerHeight - searchBarHeight
+               contentHeight := availableHeight
+
+               leftPaneWidth := (m.width - 1) / 2 // -1 for divider
+               rightPaneWidth := m.width - leftPaneWidth - 1
+
+               if !m.ready {
+                       m.logsViewport = viewport.New(leftPaneWidth, 
contentHeight)
+                       m.extrasViewport = viewport.New(rightPaneWidth, 
contentHeight)
+                       m.logsViewport.YPosition = 0
+                       m.extrasViewport.YPosition = 0
+                       // Enable scroll indicators for extras viewport
+                       m.extrasViewport.Style = lipgloss.NewStyle()
+                       m.ready = true
+                       m.updateLogsViewport()
+                       m.updateExtrasViewport()
+               } else {
+                       m.logsViewport.Width = leftPaneWidth
+                       m.logsViewport.Height = contentHeight
+                       m.extrasViewport.Width = rightPaneWidth
+                       m.extrasViewport.Height = contentHeight
+                       m.updateLogsViewport()
+                       m.updateExtrasViewport()
+               }
 
-                       if m.cursor >= len(m.logs)-1 {
-                               m.cursor = len(m.logs) - 1
-                       }
+               return m, nil
 
-                       m.parentViewportStart += parentViewportHeight
-                       if m.parentViewportStart >= len(m.logs)-1 {
-                               m.parentViewportStart = len(m.logs) - 1
+       case tea.KeyMsg:
+               // If in search mode, handle text input
+               if m.searchMode {
+                       switch msg.String() {
+                       case "enter":
+                               m.searchMode = false
+                               m.searchInput.Blur()
+                               m.searchQuery = m.searchInput.Value()
+                               m.filterLogs()
+                               return m, nil
+                       case "esc":
+                               m.searchMode = false
+                               m.searchInput.Blur()
+                               // Reset search if ESC pressed
+                               m.searchInput.SetValue("")
+                               m.searchQuery = ""
+                               m.filterLogs()
+                               return m, nil
+                       default:
+                               var cmd tea.Cmd
+                               m.searchInput, cmd = m.searchInput.Update(msg)
+                               return m, cmd
                        }
-               case "enter":
-                       m.expanded[m.cursor] = !m.expanded[m.cursor]
+               }
 
-                       log := m.logs[m.cursor]
-                       extras := strings.Split(style.RenderLineExtras(log), 
"\n")
-                       m.extrasExpandedViewport.cursor = 0
-                       m.extrasExpandedViewport.length = len(extras)
-               case "q", "esc", "ctrl+c":
+               // Normal mode key handling
+               switch msg.String() {
+               case "up", "k":
+                       if m.cursor > 0 {
+                               m.cursor--
+                               m.updateLogsViewport()
+                               m.updateExtrasViewport()
+                       }
+               case "down", "j":
+                       if m.cursor < len(m.filteredLogs)-1 {
+                               m.cursor++
+                               m.updateLogsViewport()
+                               m.updateExtrasViewport()
+                       }
+               case "ctrl+u", "pgup":
+                       pageSize := m.logsViewport.Height
+                       m.cursor = max(0, m.cursor-pageSize)
+                       m.updateLogsViewport()
+                       m.updateExtrasViewport()
+               case "ctrl+d", "pgdown":
+                       pageSize := m.logsViewport.Height
+                       m.cursor = min(len(m.filteredLogs)-1, m.cursor+pageSize)
+                       m.updateLogsViewport()
+                       m.updateExtrasViewport()
+               case "left", "h":
+                       m.extrasViewport.LineUp(1)
+                       return m, nil
+               case "right", "l":
+                       m.extrasViewport.LineDown(1)
+                       return m, nil
+               case "/":
+                       m.searchMode = true
+                       m.searchInput.Focus()
+                       return m, nil
+               case "q", "ctrl+c":
                        return m, tea.Quit
+               case "esc":
+                       // Clear search with ESC in normal mode
+                       if m.searchQuery != "" {
+                               m.searchInput.SetValue("")
+                               m.searchQuery = ""
+                               m.filterLogs()
+                       }
                }
        }
 
        return m, nil
 }
 
-func (m model) View() string {
-       var b strings.Builder
-
-       end := min(m.parentViewportStart+parentViewportHeight, len(m.logs))
-
-       for i := m.parentViewportStart; i < end; i++ {
-               line := m.logs[i]
+func (m *model) updateLogsViewport() {
+       if !m.ready {
+               return
+       }
 
+       var lines []string
+       for i, logIdx := range m.filteredLogs {
+               line := m.logs[logIdx]
                cursor := " "
                if i == m.cursor {
                        cursor = ">"
                }
 
-               hasExtras := len(line.Extras) > 0
-               hasExtrasIndicator := " "
-               if hasExtras {
-                       hasExtrasIndicator = 
style.LogLineHasExtrasStyle.Render("+")
-                       if m.expanded[i] {
-                               hasExtrasIndicator = 
style.LogLineHasExtrasStyle.Render("-")
-                       }
+               hasExtras := " "
+               if len(line.Extras) > 0 {
+                       hasExtras = style.LogLineHasExtrasStyle.Render("+")
                }
 
                renderedLine := style.RenderLine(line, false)
-
-               fmt.Fprintf(&b, "%s %s %s", cursor, hasExtrasIndicator, 
renderedLine)
-               if m.expanded[i] && hasExtras {
-                       extras := strings.Split(style.RenderLineExtras(line), 
"\n")
-
-                       tooLongPrefix := ""
-                       tooLongSuffix := ""
-
-                       start := 0
-                       end := extrasViewportHeight
-                       if end > len(extras) {
-                               end = len(extras)
-                       }
-
-                       if len(extras) > extrasViewportHeight {
-                               tooLongSuffix = "\n" + style.LogLineExtraIndent 
+ "..."
-                       }
-
-                       if i == m.cursor {
-                               if m.extrasExpandedViewport.AtTop() {
-                                       tooLongPrefix = ""
-                               } else {
-                                       tooLongPrefix = fmt.Sprintf("%s... 
%d/%d\n", style.LogLineExtraIndent, m.extrasExpandedViewport.cursor, 
m.extrasExpandedViewport.length)
-                               }
-
-                               if m.extrasExpandedViewport.AtBottom() {
-                                       tooLongSuffix = ""
-                               } else {
-                                       tooLongSuffix = fmt.Sprintf("\n%s... 
%d/%d", style.LogLineExtraIndent, 
m.extrasExpandedViewport.cursor+extrasViewportHeight, 
m.extrasExpandedViewport.length)
-                               }
-
-                               start = m.extrasExpandedViewport.cursor
-                               end = m.extrasExpandedViewport.cursor + 
extrasViewportHeight
-                               if end > len(extras) {
-                                       end = len(extras)
-                               }
-                       }
-
-                       fmt.Fprintf(&b, "\n%s%s%s",
-                               tooLongPrefix,
-                               
style.LogLineExtraValueStyle.Render(strings.Join(extras[start:end], "\n")),
-                               tooLongSuffix)
-               }
-               fmt.Fprintln(&b)
+               lines = append(lines, fmt.Sprintf("%s %s %s", cursor, 
hasExtras, renderedLine))
        }
 
-       if len(m.logs) > end {
-               fmt.Fprintf(&b, "\nViewing %d/%d logs\n", m.cursor, 
len(m.logs)-end)
-       }
+       m.logsViewport.SetContent(strings.Join(lines, "\n"))
 
-       // TODO: migrate this to a help widget
-       currentLog := m.logs[m.cursor]
-       if len(currentLog.Extras) > 0 {
-               if m.expanded[m.cursor] {
-                       fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q 
to quit]", style.LogLineHasExtrasStyle.Render("Enter to collapse"))
-               } else {
-                       fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q 
to quit]", style.LogLineHasExtrasStyle.Render("Enter to expand"))
-               }
-       } else {
-               fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q to 
quit]", style.LogLineTimestampStyle.Render("Enter to expand"))
+       // Calculate line position and scroll to make cursor visible
+       cursorLine := m.cursor
+       if cursorLine < m.logsViewport.YOffset {
+               m.logsViewport.YOffset = cursorLine
+       } else if cursorLine >= m.logsViewport.YOffset+m.logsViewport.Height {
+               m.logsViewport.YOffset = cursorLine - m.logsViewport.Height + 1
        }
-
-       return b.String()
 }
 
-func (m *model) MoveUp() {
-       // if we're currently at an expanded log line ...
-       if m.expanded[m.cursor] {
-               // and we're already at the top
-               if m.extrasExpandedViewport.AtTop() {
-                       // then scroll up as normal, out of the expanded log 
line
-                       m.cursor--
-                       if m.cursor < 0 {
-                               m.cursor = 0
-                       }
-
-                       // if we've gone out of view (as we're now before the 
previous start point) scroll us up
-                       if m.cursor < m.parentViewportStart {
-                               m.parentViewportStart--
-                       }
-                       if m.parentViewportStart < 0 {
-                               m.parentViewportStart = 0
-                       }
-
-                       // we may now be on a log line that is expanded, so we 
need to makes sure our internal state is reset
-                       m.updateExtrasExpandedViewportStateAfterMoving()
-               } else {
-                       // otherwise, we want to scroll our expanded log line 
up a line
-                       m.extrasExpandedViewport.MoveUp()
-               }
-       } else if m.cursor > 0 {
-               // if we're on a regular line, then move us up
-               m.cursor--
-               if m.cursor < 0 {
-                       m.cursor = 0
-               }
-
-               // if we've gone out of view (as we're now before the previous 
start point) scroll us up
-               if m.cursor < m.parentViewportStart {
-                       m.parentViewportStart--
-               }
-               if m.parentViewportStart < 0 {
-                       m.parentViewportStart = 0
-               }
-
-               previous := m.cursor - 1
-               if previous < 0 {
-                       previous = 0
-               }
-
-               // we may now be on a log line that is expanded, so we need to 
makes sure our internal state is reset
-               m.updateExtrasExpandedViewportStateAfterMoving()
+func (m *model) updateExtrasViewport() {
+       if !m.ready || m.cursor >= len(m.filteredLogs) {
+               return
        }
-}
 
-func (m *model) MoveDown() {
-       // if we're currently at an expanded log line ...
-       if m.expanded[m.cursor] {
-               // and we're already at the bottom
-               if m.extrasExpandedViewport.AtBottom() {
-                       // then scroll down as normal, out of the expanded log 
line
-                       m.cursor++
-                       if m.cursor > len(m.logs)-1 {
-                               m.cursor = len(m.logs) - 1
-                       }
-
-                       // if we've gone out of view (as we're now before the 
previous start point) scroll us down
-                       if m.cursor > m.parentViewportStart {
-                               m.parentViewportStart++
-                       }
-                       if m.parentViewportStart > len(m.logs)-1 {
-                               m.parentViewportStart = len(m.logs) - 1
-                       }
-
-                       // we may now be on a log line that is expanded, so we 
need to makes sure our internal state is reset
-                       m.updateExtrasExpandedViewportStateAfterMoving()
-               } else {
-                       // otherwise, we want to scroll our expanded log line 
down a line
-                       m.extrasExpandedViewport.MoveDown()
-               }
-       } else if m.cursor < len(m.logs)-1 {
-               // if we're on a regular line, then move us down
-               m.cursor++
-               if m.cursor > len(m.logs)-1 {
-                       m.cursor = len(m.logs) - 1
-               }
+       logIdx := m.filteredLogs[m.cursor]
+       currentLog := m.logs[logIdx]
 
-               // if we've gone out of view (as we're now before the previous 
start point) scroll us down
-               if m.cursor > m.parentViewportStart {
-                       m.parentViewportStart++
+       if len(currentLog.Extras) == 0 {
+               emptyMsg := lipgloss.NewStyle().
+                       Foreground(lipgloss.Color("240")).
+                       Italic(true).
+                       Render("No additional context for this log")
+               m.extrasViewport.SetContent(emptyMsg)
+       } else {
+               extrasText := style.RenderLineExtras(currentLog)
+               // Trim any trailing newlines that might cause height mismatches
+               extrasText = strings.TrimRight(extrasText, "\n")
+               m.extrasViewport.SetContent(extrasText)
+       }
+
+       // Reset scroll position when changing logs
+       m.extrasViewport.YOffset = 0
+}
+
+func (m *model) filterLogs() {
+       if m.searchQuery == "" {
+               // No search - show all logs
+               m.filteredLogs = make([]int, len(m.logs))
+               for i := range m.logs {
+                       m.filteredLogs[i] = i
                }
-               if m.parentViewportStart > len(m.logs)-1 {
-                       m.parentViewportStart = len(m.logs) - 1
+       } else {
+               // Filter logs based on search query
+               query := strings.ToLower(m.searchQuery)
+               m.filteredLogs = []int{}
+               for i, line := range m.logs {
+                       if strings.Contains(strings.ToLower(line.Message), 
query) {
+                               m.filteredLogs = append(m.filteredLogs, i)
+                       }
                }
+       }
 
-               // we may now be on a log line that is expanded, so we need to 
makes sure our internal state is reset
-               m.updateExtrasExpandedViewportStateAfterMoving()
+       // Reset cursor if it's out of bounds
+       if m.cursor >= len(m.filteredLogs) {
+               m.cursor = max(0, len(m.filteredLogs)-1)
        }
-}
 
-func (m *model) updateExtrasExpandedViewportStateAfterMoving() {
-       log := m.logs[m.cursor]
-       extras := strings.Split(style.RenderLineExtras(log), "\n")
-       m.extrasExpandedViewport.cursor = 0
-       m.extrasExpandedViewport.length = len(extras)
+       m.updateLogsViewport()
+       m.updateExtrasViewport()
 }
 
-// extrasExpandedViewport allows expanding a given log line's "extra" log 
metadata, in a scrollable fashion.
-type extrasExpandedViewport struct {
-       cursor int
-       length int
-}
+func (m model) View() string {
+       if !m.ready {
+               return "Loading..."
+       }
 
-func (e extrasExpandedViewport) AtTop() bool {
-       return e.cursor == 0
-}
+       leftPaneWidth := m.logsViewport.Width
+       rightPaneWidth := m.extrasViewport.Width
+       paneHeight := m.logsViewport.Height
+
+       // Create layout with divider - fix height to match viewport
+       leftPaneStyled := lipgloss.NewStyle().
+               BorderStyle(lipgloss.NormalBorder()).
+               BorderRight(true).
+               Width(leftPaneWidth).
+               Height(paneHeight).
+               Align(lipgloss.Left, lipgloss.Top).
+               Render(m.logsViewport.View())
+
+       // Add scroll percentage indicator for extras if there's scrollable 
content
+       extrasView := m.extrasViewport.View()
+       if m.extrasViewport.TotalLineCount() > m.extrasViewport.Height {
+               scrollIndicator := ""
+
+               // Show arrow indicators based on position
+               if !m.extrasViewport.AtTop() && !m.extrasViewport.AtBottom() {
+                       scrollPercent := int(m.extrasViewport.ScrollPercent() * 
100)
+                       scrollIndicator = lipgloss.NewStyle().
+                               Foreground(lipgloss.Color("240")).
+                               Render(fmt.Sprintf(" ↑↓ %d%%", scrollPercent))
+               } else if !m.extrasViewport.AtBottom() {
+                       // At top but not at bottom - can scroll down
+                       scrollIndicator = lipgloss.NewStyle().
+                               Foreground(lipgloss.Color("240")).
+                               Render(" ↓ More")
+               } else if !m.extrasViewport.AtTop() {
+                       // At bottom but not at top - can scroll up
+                       scrollPercent := int(m.extrasViewport.ScrollPercent() * 
100)
+                       scrollIndicator = lipgloss.NewStyle().
+                               Foreground(lipgloss.Color("240")).
+                               Render(fmt.Sprintf(" ↑ %d%%", scrollPercent))
+               }
+
+               // Overlay indicator on the last line of the viewport
+               if scrollIndicator != "" {
+                       lines := strings.Split(extrasView, "\n")
+                       if len(lines) > 0 && !m.extrasViewport.AtBottom() {
+                               lines[len(lines)-1] = scrollIndicator
+                               extrasView = strings.Join(lines, "\n")
+                       }
+               }
+       }
+
+       rightPaneStyled := lipgloss.NewStyle().
+               Width(rightPaneWidth).
+               Height(paneHeight).
+               Align(lipgloss.Left, lipgloss.Top).
+               Render(extrasView)
+
+       content := lipgloss.JoinHorizontal(lipgloss.Top, leftPaneStyled, 
rightPaneStyled)
+
+       var parts []string
+
+       // Add search bar if in search mode
+       if m.searchMode {
+               searchBar := lipgloss.NewStyle().
+                       Foreground(lipgloss.Color("14")).
+                       Render("/") + " " + m.searchInput.View()
+               parts = append(parts, content, searchBar)
+       } else {
+               parts = append(parts, content)
+       }
 
-func (e extrasExpandedViewport) AtBottom() bool {
-       if e.cursor == (e.length - 1) {
-               return true
+       // Footer with help
+       helpText := "↑/↓: Navigate | ←/→: Scroll extras | /: Search | q: Quit"
+       if m.searchQuery != "" {
+               helpText = fmt.Sprintf("↑/↓: Navigate | ←/→: Scroll extras | /: 
Search | ESC: Clear search (%d/%d matches)", len(m.filteredLogs), len(m.logs))
+       }
+       if len(m.filteredLogs) > 0 {
+               logIdx := m.filteredLogs[m.cursor]
+               currentLog := m.logs[logIdx]
+               if len(currentLog.Extras) == 0 {
+                       helpText = "↑/↓: Navigate | /: Search | q: Quit | (No 
extras for this log)"
+               }
        }
 
-       isShowingLastPage := (e.length)-extrasViewportHeight <= e.cursor
+       footer := lipgloss.NewStyle().
+               Foreground(lipgloss.Color("240")).
+               Render(helpText)
 
-       return isShowingLastPage
-}
+       parts = append(parts, footer)
 
-func (e *extrasExpandedViewport) MoveUp() {
-       e.cursor--
-       if e.cursor <= 0 {
-               e.cursor = 0
-       }
-}
+       // Combine all parts
+       view := lipgloss.JoinVertical(lipgloss.Left, parts...)
 
-func (e *extrasExpandedViewport) MoveDown() {
-       e.cursor++
-       if e.cursor > e.length {
-               e.cursor = e.length
-       }
+       return view
 }
 
 func parseLevelFlag(level *string) (log.Level, error) {
@@ -349,8 +376,9 @@
 func main() {
        cliLogger := charmlog.New(os.Stderr)
 
-       levelFlag := flag.String("level", "info", "Set the log level (trace, 
debug, info, warn, error, fatal)")
+       levelFlag := flag.String("level", "debug", "Set the log level (trace, 
debug, info, warn, error, fatal)")
        pathFlag := flag.String("path", "", "The path to the debug log file 
(`RENOVATE_DEBUG_LOG_FILE`)")
+       maxLineSizeMB := flag.Int("max-line-size", 10, "Maximum line size in MB 
(default: 10)")
        flag.Parse()
 
        if pathFlag == nil {
@@ -378,8 +406,12 @@
        }
 
        var lines []log.Line
-       // TODO via 
https://gitlab.com/jamietanna/dotfiles-arch/-/blob/67d79966/go/home/go/src/jvt.me/dotfiles/jjj/main.go#L24-26
 we may want to allow handling /very/ long log lines
+
        scanner := bufio.NewScanner(f)
+       // via https://stackoverflow.com/a/37455465, make sure we can handle 
lines up to configured size
+       buf := make([]byte, 0, bufio.MaxScanTokenSize)
+       maxBytes := *maxLineSizeMB * 1024 * 1024
+       scanner.Buffer(buf, maxBytes)
        for scanner.Scan() {
                var line log.Line
                data := scanner.Bytes()
@@ -394,12 +426,17 @@
                }
        }
 
+       if err := scanner.Err(); err != nil {
+               cliLogger.Fatal(fmt.Sprintf("Failed to parse log file: %v", 
err), "err", err)
+               os.Exit(1)
+       }
+
        if len(lines) == 0 {
                fmt.Printf("The file you provided did not contain any %s level 
logs\n", allowedLogLevel.String())
                os.Exit(0)
        }
 
-       p := tea.NewProgram(newModel(lines))
+       p := tea.NewProgram(newModel(lines), tea.WithAltScreen())
        if _, err := p.Run(); err != nil {
                fmt.Fprintf(os.Stderr, "Error starting app: %v\n", err)
                os.Exit(1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/renovate-pretty-log-0.1.2/cmd/renovate-text-to-json/main.go 
new/renovate-pretty-log-0.5.2/cmd/renovate-text-to-json/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-text-to-json/main.go     
1970-01-01 01:00:00.000000000 +0100
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-text-to-json/main.go     
2026-03-06 16:03:10.000000000 +0100
@@ -0,0 +1,216 @@
+package main
+
+import (
+       "bufio"
+       "encoding/json"
+       "fmt"
+       "os"
+       "regexp"
+       "strings"
+       "time"
+
+       charmlog "github.com/charmbracelet/log"
+
+       log "gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
+)
+
+var (
+       // Matches "LEVEL: Message" or "LEVEL Message"
+       logLineRegex = 
regexp.MustCompile(`^(TRACE|DEBUG|INFO|WARN|ERROR|FATAL):?\s+(.+)$`)
+)
+
+func parseLevel(levelStr string) log.Level {
+       switch strings.ToUpper(levelStr) {
+       case "TRACE":
+               return log.LevelTrace
+       case "DEBUG":
+               return log.LevelDebug
+       case "INFO":
+               return log.LevelInfo
+       case "WARN":
+               return log.LevelWarn
+       case "ERROR":
+               return log.LevelError
+       case "FATAL":
+               return log.LevelFatal
+       default:
+               return log.LevelInfo
+       }
+}
+
+func startsJSON(line string) bool {
+       line = strings.TrimSpace(line)
+       return strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[")
+}
+
+func main() {
+       cliLogger := charmlog.New(os.Stderr)
+
+       scanner := bufio.NewScanner(os.Stdin)
+       buf := make([]byte, 0, bufio.MaxScanTokenSize)
+       maxBytes := 10 * 1024 * 1024 // 10MB
+       scanner.Buffer(buf, maxBytes)
+
+       var currentLine *log.Line
+       var jsonBuffer strings.Builder
+       inJSON := false
+
+       for scanner.Scan() {
+               text := scanner.Text()
+               text = strings.TrimRight(text, "\r\n")
+
+               // Skip empty lines unless we're in JSON
+               if strings.TrimSpace(text) == "" && !inJSON {
+                       continue
+               }
+
+               // Check if this is a log line (starts with level)
+               if matches := logLineRegex.FindStringSubmatch(text); matches != 
nil && !inJSON {
+                       // If we have a pending line with JSON, output it first
+                       if currentLine != nil && jsonBuffer.Len() > 0 {
+                               if err := parseAndAddExtras(currentLine, 
jsonBuffer.String()); err != nil {
+                                       cliLogger.Warn("Failed to parse JSON 
extras", "err", err, "json", jsonBuffer.String())
+                               }
+                               outputLine(currentLine)
+                               jsonBuffer.Reset()
+                       } else if currentLine != nil {
+                               // Output line without extras
+                               outputLine(currentLine)
+                       }
+
+                       // Start new line
+                       levelStr := matches[1]
+                       message := matches[2]
+
+                       currentLine = &log.Line{
+                               Name:       "renovate",
+                               Hostname:   "unknown",
+                               Pid:        0,
+                               Level:      parseLevel(levelStr),
+                               LogContext: "unknown",
+                               Repository: "",
+                               Message:    message,
+                               Time:       time.Time{},
+                               Version:    0,
+                               Extras:     make(map[string]any),
+                       }
+               } else if startsJSON(text) && currentLine != nil && !inJSON {
+                       // Start of JSON block
+                       inJSON = true
+                       jsonBuffer.WriteString(text)
+                       jsonBuffer.WriteString("\n")
+               } else if inJSON {
+                       // Continue collecting JSON
+                       jsonBuffer.WriteString(text)
+                       jsonBuffer.WriteString("\n")
+
+                       // Try to parse to see if we have complete JSON
+                       trimmed := strings.TrimSpace(jsonBuffer.String())
+                       var test any
+                       if json.Unmarshal([]byte(trimmed), &test) == nil {
+                               // Valid JSON, end of block
+                               inJSON = false
+                       }
+               } else {
+                       // Could be continuation of message or unknown format
+                       if currentLine != nil && !inJSON {
+                               // Append to current message
+                               currentLine.Message += " " + text
+                       } else if !inJSON {
+                               // Treat as INFO line if we don't have context
+                               currentLine = &log.Line{
+                                       Name:       "renovate",
+                                       Hostname:   "unknown",
+                                       Pid:        0,
+                                       Level:      log.LevelInfo,
+                                       LogContext: "unknown",
+                                       Repository: "",
+                                       Message:    text,
+                                       Time:       time.Time{}, // Zero time 
(0001-01-01T00:00:00Z) as placeholder
+                                       Version:    0,
+                                       Extras:     make(map[string]any),
+                               }
+                       }
+               }
+       }
+
+       // Output the last line if there is one
+       if currentLine != nil {
+               if jsonBuffer.Len() > 0 {
+                       if err := parseAndAddExtras(currentLine, 
jsonBuffer.String()); err != nil {
+                               cliLogger.Warn("Failed to parse JSON extras", 
"err", err, "json", jsonBuffer.String())
+                       }
+               }
+               outputLine(currentLine)
+       }
+
+       if err := scanner.Err(); err != nil {
+               cliLogger.Fatal("Error reading stdin", "err", err)
+       }
+}
+
+func parseAndAddExtras(line *log.Line, jsonStr string) error {
+       jsonStr = strings.TrimSpace(jsonStr)
+
+       var extras map[string]any
+       if err := json.Unmarshal([]byte(jsonStr), &extras); err != nil {
+               return err
+       }
+
+       if line.Extras == nil {
+               line.Extras = make(map[string]any)
+       }
+
+       for k, v := range extras {
+               // Check if it's a known field that should be set on the line 
struct
+               if k == "repository" {
+                       if repo, ok := v.(string); ok {
+                               line.Repository = repo
+                               continue
+                       }
+               }
+               line.Extras[k] = v
+       }
+
+       return nil
+}
+
+func outputLine(line *log.Line) {
+       // Build JSON manually to maintain field order
+       var sb strings.Builder
+       sb.WriteString("{")
+
+       // Standard fields in order (msg, time, v at the end)
+       fmt.Fprintf(&sb, `"name":%q,`, line.Name)
+       fmt.Fprintf(&sb, `"hostname":%q,`, line.Hostname)
+       fmt.Fprintf(&sb, `"pid":%d,`, line.Pid)
+       fmt.Fprintf(&sb, `"level":%d,`, line.Level)
+       fmt.Fprintf(&sb, `"logContext":%q`, line.LogContext)
+
+       // Only include repository if it's set
+       if line.Repository != "" {
+               sb.WriteString(",")
+               fmt.Fprintf(&sb, `"repository":%q`, line.Repository)
+       }
+
+       // Add extras
+       if len(line.Extras) > 0 {
+               for k, v := range line.Extras {
+                       sb.WriteString(",")
+                       jsonVal, err := json.Marshal(v)
+                       if err != nil {
+                               charmlog.New(os.Stderr).Warn("Failed to marshal 
extra field", "key", k, "err", err)
+                               continue
+                       }
+                       fmt.Fprintf(&sb, `%q:%s`, k, string(jsonVal))
+               }
+       }
+
+       // msg, time, v always at the end
+       fmt.Fprintf(&sb, `,"msg":%q,`, line.Message)
+       fmt.Fprintf(&sb, `"time":%q,`, line.Time.Format(time.RFC3339))
+       fmt.Fprintf(&sb, `"v":%d`, line.Version)
+
+       sb.WriteString("}")
+       fmt.Println(sb.String())
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/go.mod 
new/renovate-pretty-log-0.5.2/go.mod
--- old/renovate-pretty-log-0.1.2/go.mod        2025-05-23 18:08:47.000000000 
+0200
+++ new/renovate-pretty-log-0.5.2/go.mod        2026-03-06 16:03:10.000000000 
+0100
@@ -1,32 +1,38 @@
 module gitlab.com/tanna.dev/renovate-pretty-log
 
-go 1.24
+go 1.24.2
 
 require (
-       github.com/charmbracelet/bubbletea v1.3.5
+       github.com/alecthomas/chroma/v2 v2.23.1
+       github.com/charmbracelet/bubbles v1.0.0
+       github.com/charmbracelet/bubbletea v1.3.10
        github.com/charmbracelet/lipgloss v1.1.0
        github.com/charmbracelet/log v0.4.2
 )
 
 require (
+       github.com/atotto/clipboard v0.1.4 // indirect
        github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
-       github.com/charmbracelet/colorprofile 
v0.2.3-0.20250311203215-f60798e515dc // indirect
-       github.com/charmbracelet/x/ansi v0.8.0 // indirect
-       github.com/charmbracelet/x/cellbuf 
v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
-       github.com/charmbracelet/x/term v0.2.1 // indirect
+       github.com/charmbracelet/colorprofile v0.4.1 // indirect
+       github.com/charmbracelet/x/ansi v0.11.6 // indirect
+       github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
+       github.com/charmbracelet/x/term v0.2.2 // indirect
+       github.com/clipperhouse/displaywidth v0.9.0 // indirect
+       github.com/clipperhouse/stringish v0.1.1 // indirect
+       github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
+       github.com/dlclark/regexp2 v1.11.5 // indirect
        github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // 
indirect
        github.com/go-logfmt/logfmt v0.6.0 // indirect
-       github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+       github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/mattn/go-localereader v0.0.1 // indirect
-       github.com/mattn/go-runewidth v0.0.16 // indirect
+       github.com/mattn/go-runewidth v0.0.19 // indirect
        github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
        github.com/muesli/cancelreader v0.2.2 // indirect
        github.com/muesli/termenv v0.16.0 // indirect
        github.com/rivo/uniseg v0.4.7 // indirect
        github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
        golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
-       golang.org/x/sync v0.13.0 // indirect
-       golang.org/x/sys v0.32.0 // indirect
+       golang.org/x/sys v0.38.0 // indirect
        golang.org/x/text v0.3.8 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/go.sum 
new/renovate-pretty-log-0.5.2/go.sum
--- old/renovate-pretty-log-0.1.2/go.sum        2025-05-23 18:08:47.000000000 
+0200
+++ new/renovate-pretty-log-0.5.2/go.sum        2026-03-06 16:03:10.000000000 
+0100
@@ -1,33 +1,53 @@
+github.com/alecthomas/assert/v2 v2.11.0 
h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod 
h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/chroma/v2 v2.23.1 
h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
+github.com/alecthomas/chroma/v2 v2.23.1/go.mod 
h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
+github.com/alecthomas/repr v0.5.2 
h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
+github.com/alecthomas/repr v0.5.2/go.mod 
h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
+github.com/atotto/clipboard v0.1.4 
h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod 
h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 github.com/aymanbagabas/go-osc52/v2 v2.0.1 
h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod 
h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/charmbracelet/bubbletea v1.3.5 
h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
-github.com/charmbracelet/bubbletea v1.3.5/go.mod 
h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc 
h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
-github.com/charmbracelet/colorprofile 
v0.2.3-0.20250311203215-f60798e515dc/go.mod 
h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
+github.com/charmbracelet/bubbles v1.0.0 
h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
+github.com/charmbracelet/bubbles v1.0.0/go.mod 
h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
+github.com/charmbracelet/bubbletea v1.3.10 
h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
+github.com/charmbracelet/bubbletea v1.3.10/go.mod 
h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
+github.com/charmbracelet/colorprofile v0.4.1 
h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
+github.com/charmbracelet/colorprofile v0.4.1/go.mod 
h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
 github.com/charmbracelet/lipgloss v1.1.0 
h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
 github.com/charmbracelet/lipgloss v1.1.0/go.mod 
h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
 github.com/charmbracelet/log v0.4.2 
h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
 github.com/charmbracelet/log v0.4.2/go.mod 
h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
-github.com/charmbracelet/x/ansi v0.8.0 
h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
-github.com/charmbracelet/x/ansi v0.8.0/go.mod 
h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd 
h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
-github.com/charmbracelet/x/cellbuf 
v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod 
h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/term v0.2.1 
h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
-github.com/charmbracelet/x/term v0.2.1/go.mod 
h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
+github.com/charmbracelet/x/ansi v0.11.6 
h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
+github.com/charmbracelet/x/ansi v0.11.6/go.mod 
h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
+github.com/charmbracelet/x/cellbuf v0.0.15 
h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
+github.com/charmbracelet/x/cellbuf v0.0.15/go.mod 
h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
+github.com/charmbracelet/x/term v0.2.2 
h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
+github.com/charmbracelet/x/term v0.2.2/go.mod 
h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
+github.com/clipperhouse/displaywidth v0.9.0 
h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
+github.com/clipperhouse/displaywidth v0.9.0/go.mod 
h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
+github.com/clipperhouse/stringish v0.1.1 
h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
+github.com/clipperhouse/stringish v0.1.1/go.mod 
h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
+github.com/clipperhouse/uax29/v2 v2.5.0 
h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
+github.com/clipperhouse/uax29/v2 v2.5.0/go.mod 
h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
 github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.11.5 
h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+github.com/dlclark/regexp2 v1.11.5/go.mod 
h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f 
h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod 
h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
 github.com/go-logfmt/logfmt v0.6.0 
h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
 github.com/go-logfmt/logfmt v0.6.0/go.mod 
h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/lucasb-eyer/go-colorful v1.2.0 
h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod 
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/hexops/gotextdiff v1.0.3 
h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod 
h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
+github.com/lucasb-eyer/go-colorful v1.3.0 
h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
+github.com/lucasb-eyer/go-colorful v1.3.0/go.mod 
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/mattn/go-isatty v0.0.20 
h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod 
h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 
h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
 github.com/mattn/go-localereader v0.0.1/go.mod 
h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.16 
h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod 
h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.19 
h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
+github.com/mattn/go-runewidth v0.0.19/go.mod 
h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 
h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod 
h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 github.com/muesli/cancelreader v0.2.2 
h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -36,7 +56,6 @@
 github.com/muesli/termenv v0.16.0/go.mod 
h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
 github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.2.0/go.mod 
h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 github.com/rivo/uniseg v0.4.7/go.mod 
h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
@@ -45,12 +64,10 @@
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod 
h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 golang.org/x/exp v0.0.0-20231006140011-7918f672742d 
h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
 golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod 
h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
-golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
-golang.org/x/sync v0.13.0/go.mod 
h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/internal/style/main.go 
new/renovate-pretty-log-0.5.2/internal/style/main.go
--- old/renovate-pretty-log-0.1.2/internal/style/main.go        2025-05-23 
18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/internal/style/main.go        2026-03-06 
16:03:10.000000000 +0100
@@ -4,11 +4,16 @@
        "bytes"
        "encoding/json"
        "fmt"
+       "io"
        "regexp"
        "slices"
        "strings"
        "time"
 
+       "github.com/alecthomas/chroma/v2"
+       "github.com/alecthomas/chroma/v2/formatters"
+       "github.com/alecthomas/chroma/v2/lexers"
+       "github.com/alecthomas/chroma/v2/styles"
        "github.com/charmbracelet/lipgloss"
        "gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
 )
@@ -21,6 +26,59 @@
        27, 91, 48, 109, 10, 27, 91, 51, 56, 59, 50, 59, 49, 56, 54, 59, 49, 
54, 54, 59, 49, 50, 55, 109, 27, 91, 48, 109, 10,
 }
 
+// Srcery color scheme for Chroma
+var srceryStyle = styles.Register(chroma.MustNewStyle("srcery", 
chroma.StyleEntries{
+       chroma.Background:             "#1C1B19",
+       chroma.Text:                   "#BAA67F",
+       chroma.Comment:                "#918175",
+       chroma.CommentPreproc:         "#68A8E4",
+       chroma.Keyword:                "#EF2F27",
+       chroma.KeywordNamespace:       "#E02C6D",
+       chroma.KeywordType:            "#FBB829",
+       chroma.Operator:               "#FF5F00",
+       chroma.Punctuation:            "#BAA67F",
+       chroma.Name:                   "#BAA67F",
+       chroma.NameBuiltin:            "#2C78BF",
+       chroma.NameFunction:           "#2C78BF",
+       chroma.NameClass:              "#FBB829",
+       chroma.NameNamespace:          "#E02C6D",
+       chroma.NameException:          "#EF2F27",
+       chroma.NameVariable:           "#68A8E4",
+       chroma.NameConstant:           "#E02C6D",
+       chroma.NameLabel:              "#2C78BF",
+       chroma.NameEntity:             "#FF8700",
+       chroma.NameAttribute:          "#98BC37",
+       chroma.NameTag:                "#2C78BF",
+       chroma.NameDecorator:          "#E02C6D",
+       chroma.String:                 "#519F50",
+       chroma.StringDoc:              "#918175",
+       chroma.StringInterpol:         "#98BC37",
+       chroma.StringEscape:           "#0AAEB3",
+       chroma.StringRegex:            "#0AAEB3",
+       chroma.StringSymbol:           "#519F50",
+       chroma.StringOther:            "#519F50",
+       chroma.Number:                 "#FF8700",
+       chroma.NumberInteger:          "#FF8700",
+       chroma.NumberFloat:            "#FF8700",
+       chroma.NumberHex:              "#FF8700",
+       chroma.NumberOct:              "#FF8700",
+       chroma.Literal:                "#FF8700",
+       chroma.LiteralDate:            "#519F50",
+       chroma.Generic:                "#BAA67F",
+       chroma.GenericDeleted:         "#EF2F27",
+       chroma.GenericEmph:            "italic",
+       chroma.GenericError:           "#EF2F27",
+       chroma.GenericHeading:         "bold #2C78BF",
+       chroma.GenericInserted:        "#519F50",
+       chroma.GenericOutput:          "#918175",
+       chroma.GenericPrompt:          "#BAA67F",
+       chroma.GenericStrong:          "bold",
+       chroma.GenericSubheading:      "bold #E02C6D",
+       chroma.GenericTraceback:       "#EF2F27",
+       chroma.GenericUnderline:       "underline",
+       chroma.Error:                  "#EF2F27",
+}))
+
 var LogLineTraceLevelStyle = lipgloss.NewStyle().
        SetString("TRAC").
        Bold(true).
@@ -146,6 +204,7 @@
 
        var extras []string
        for _, k := range keys {
+               buf.Reset()
                v := line.Extras[k]
                out := ""
 
@@ -155,11 +214,21 @@
                        float64,
                        string:
                        out = fmt.Sprintf("%#v", v)
+                       err := highlightJSONWithChroma(&buf, out)
+                       if err == nil {
+                               out = buf.String()
+                       }
                default:
-                       buf.Reset()
                        err := jsonEncoder.Encode(v)
                        if err == nil {
-                               out = buf.String()
+                               str := buf.String()
+                               buf.Reset()
+                               err = highlightJSONWithChroma(&buf, str)
+                               if err == nil {
+                                       out = buf.String()
+                               } else {
+                                       out = fmt.Sprintf("%#v", v)
+                               }
                        } else {
                                out = fmt.Sprintf("%#v", v)
                        }
@@ -178,3 +247,17 @@
 
        return ""
 }
+
+func highlightJSONWithChroma(w io.Writer, jsonText string) error {
+       lexer := lexers.Get("json")
+       if lexer == nil {
+               lexer = lexers.Fallback
+       }
+       iter, err := lexer.Tokenise(nil, jsonText)
+       if err != nil {
+               return err
+       }
+
+       formatter := formatters.Get("terminal256")
+       return formatter.Format(w, srceryStyle, iter)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/renovate.json 
new/renovate-pretty-log-0.5.2/renovate.json
--- old/renovate-pretty-log-0.1.2/renovate.json 1970-01-01 01:00:00.000000000 
+0100
+++ new/renovate-pretty-log-0.5.2/renovate.json 2026-03-06 16:03:10.000000000 
+0100
@@ -0,0 +1,6 @@
+{
+  "$schema": "https://docs.renovatebot.com/renovate-schema.json";,
+  "extends": [
+    "config:recommended"
+  ]
+}

++++++ renovate-pretty-log.obsinfo ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old  2026-03-23 17:14:38.960871431 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new  2026-03-23 17:14:38.964871597 +0100
@@ -1,5 +1,5 @@
 name: renovate-pretty-log
-version: 0.1.2
-mtime: 1748016527
-commit: c29241031096ff5d3f36a751167f88440a79eae5
+version: 0.5.2
+mtime: 1772809390
+commit: f67ff8d1e2034f80b0b5b2b8be58d15438388c2e
 

++++++ vendor.tar.gz ++++++
++++ 98004 lines of diff (skipped)

Reply via email to