Optimize author log viewer performance with tail behavior
Problem: Author log was causing severe UI lag and memory issues - Unbounded string growth (entire log kept in RAM) - Full widget rebuild on every line append - O(n²) string concatenation performance Solutions implemented: 1. Tail behavior - Keep only last 100 lines in UI - Uses circular buffer (authorLogLines slice) - Prevents unbounded memory growth - Rebuilds text from buffer on each append 2. Copy Log button - Copies full log from file (accurate) - Falls back to in-memory log if file unavailable 3. View Full Log button - Opens full log in dedicated log viewer - No UI lag from large logs 4. Track log file path - authorLogFilePath stored when log created - Used by copy and view buttons Performance improvements: - Memory: O(1) instead of O(n) - fixed 100 line buffer - CPU: One SetText() per line instead of concatenation - UI responsiveness: Dramatically improved with tail behavior UI changes: - Label shows "Authoring Log (last 100 lines)" - Copy/View buttons for accessing full log
This commit is contained in:
parent
49e01f5817
commit
d781ce2d58
|
|
@ -750,6 +750,42 @@ func buildAuthorDiscTab(state *appState) fyne.CanvasObject {
|
|||
logScroll.SetMinSize(fyne.NewSize(0, 200))
|
||||
state.authorLogScroll = logScroll
|
||||
|
||||
// Log control buttons
|
||||
copyLogBtn := widget.NewButton("Copy Log", func() {
|
||||
if state.authorLogFilePath != "" {
|
||||
// Copy from file for accuracy
|
||||
if data, err := os.ReadFile(state.authorLogFilePath); err == nil {
|
||||
state.window.Clipboard().SetContent(string(data))
|
||||
dialog.ShowInformation("Copied", "Full authoring log copied to clipboard", state.window)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Fallback to in-memory log
|
||||
state.window.Clipboard().SetContent(state.authorLogText)
|
||||
dialog.ShowInformation("Copied", "Authoring log copied to clipboard", state.window)
|
||||
})
|
||||
copyLogBtn.Importance = widget.LowImportance
|
||||
|
||||
viewFullLogBtn := widget.NewButton("View Full Log", func() {
|
||||
if state.authorLogFilePath == "" || state.authorLogFilePath == "-" {
|
||||
dialog.ShowInformation("No Log File", "No log file available to view", state.window)
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(state.authorLogFilePath); err != nil {
|
||||
dialog.ShowError(fmt.Errorf("log file not found: %w", err), state.window)
|
||||
return
|
||||
}
|
||||
state.openLogViewer("Authoring Log", state.authorLogFilePath, false)
|
||||
})
|
||||
viewFullLogBtn.Importance = widget.LowImportance
|
||||
|
||||
logControls := container.NewHBox(
|
||||
widget.NewLabel("Authoring Log (last 100 lines):"),
|
||||
layout.NewSpacer(),
|
||||
copyLogBtn,
|
||||
viewFullLogBtn,
|
||||
)
|
||||
|
||||
controls := container.NewVBox(
|
||||
widget.NewLabel("Generate DVD/ISO:"),
|
||||
widget.NewSeparator(),
|
||||
|
|
@ -759,7 +795,7 @@ func buildAuthorDiscTab(state *appState) fyne.CanvasObject {
|
|||
statusLabel,
|
||||
progressBar,
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Authoring Log:"),
|
||||
logControls,
|
||||
logScroll,
|
||||
widget.NewSeparator(),
|
||||
generateBtn,
|
||||
|
|
@ -1162,6 +1198,8 @@ func concatDVDMpg(inputs []string, output string) error {
|
|||
|
||||
func (s *appState) resetAuthorLog() {
|
||||
s.authorLogText = ""
|
||||
s.authorLogLines = nil
|
||||
s.authorLogFilePath = ""
|
||||
if s.authorLogEntry != nil {
|
||||
s.authorLogEntry.SetText("")
|
||||
}
|
||||
|
|
@ -1174,7 +1212,17 @@ func (s *appState) appendAuthorLog(line string) {
|
|||
if strings.TrimSpace(line) == "" {
|
||||
return
|
||||
}
|
||||
s.authorLogText += line + "\n"
|
||||
|
||||
// Keep only last 100 lines for UI display (tail behavior)
|
||||
const maxLines = 100
|
||||
s.authorLogLines = append(s.authorLogLines, line)
|
||||
if len(s.authorLogLines) > maxLines {
|
||||
s.authorLogLines = s.authorLogLines[len(s.authorLogLines)-maxLines:]
|
||||
}
|
||||
|
||||
// Rebuild text from buffer
|
||||
s.authorLogText = strings.Join(s.authorLogLines, "\n")
|
||||
|
||||
if s.authorLogEntry != nil {
|
||||
s.authorLogEntry.SetText(s.authorLogText)
|
||||
}
|
||||
|
|
@ -1726,6 +1774,7 @@ func (s *appState) executeAuthorJob(ctx context.Context, job *queue.Job, progres
|
|||
logging.Debug(logging.CatSystem, "author log open failed: %v", logErr)
|
||||
} else {
|
||||
job.LogPath = logPath
|
||||
s.authorLogFilePath = logPath // Store for UI access
|
||||
defer logFile.Close()
|
||||
}
|
||||
|
||||
|
|
@ -1828,6 +1877,7 @@ func (s *appState) executeAuthorJob(ctx context.Context, job *queue.Job, progres
|
|||
logging.Debug(logging.CatSystem, "author log open failed: %v", logErr)
|
||||
} else {
|
||||
job.LogPath = logPath
|
||||
s.authorLogFilePath = logPath // Store for UI access
|
||||
defer logFile.Close()
|
||||
}
|
||||
|
||||
|
|
|
|||
2
main.go
2
main.go
|
|
@ -962,6 +962,8 @@ type appState struct {
|
|||
authorChaptersRefresh func() // Refresh hook for chapter list UI
|
||||
authorDiscSize string // "DVD5" or "DVD9"
|
||||
authorLogText string
|
||||
authorLogLines []string // Circular buffer for last N lines
|
||||
authorLogFilePath string // Path to log file for full viewing
|
||||
authorLogEntry *widget.Entry
|
||||
authorLogScroll *container.Scroll
|
||||
authorProgress float64
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user