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))
|
logScroll.SetMinSize(fyne.NewSize(0, 200))
|
||||||
state.authorLogScroll = logScroll
|
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(
|
controls := container.NewVBox(
|
||||||
widget.NewLabel("Generate DVD/ISO:"),
|
widget.NewLabel("Generate DVD/ISO:"),
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
|
|
@ -759,7 +795,7 @@ func buildAuthorDiscTab(state *appState) fyne.CanvasObject {
|
||||||
statusLabel,
|
statusLabel,
|
||||||
progressBar,
|
progressBar,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
widget.NewLabel("Authoring Log:"),
|
logControls,
|
||||||
logScroll,
|
logScroll,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
generateBtn,
|
generateBtn,
|
||||||
|
|
@ -1162,6 +1198,8 @@ func concatDVDMpg(inputs []string, output string) error {
|
||||||
|
|
||||||
func (s *appState) resetAuthorLog() {
|
func (s *appState) resetAuthorLog() {
|
||||||
s.authorLogText = ""
|
s.authorLogText = ""
|
||||||
|
s.authorLogLines = nil
|
||||||
|
s.authorLogFilePath = ""
|
||||||
if s.authorLogEntry != nil {
|
if s.authorLogEntry != nil {
|
||||||
s.authorLogEntry.SetText("")
|
s.authorLogEntry.SetText("")
|
||||||
}
|
}
|
||||||
|
|
@ -1174,7 +1212,17 @@ func (s *appState) appendAuthorLog(line string) {
|
||||||
if strings.TrimSpace(line) == "" {
|
if strings.TrimSpace(line) == "" {
|
||||||
return
|
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 {
|
if s.authorLogEntry != nil {
|
||||||
s.authorLogEntry.SetText(s.authorLogText)
|
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)
|
logging.Debug(logging.CatSystem, "author log open failed: %v", logErr)
|
||||||
} else {
|
} else {
|
||||||
job.LogPath = logPath
|
job.LogPath = logPath
|
||||||
|
s.authorLogFilePath = logPath // Store for UI access
|
||||||
defer logFile.Close()
|
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)
|
logging.Debug(logging.CatSystem, "author log open failed: %v", logErr)
|
||||||
} else {
|
} else {
|
||||||
job.LogPath = logPath
|
job.LogPath = logPath
|
||||||
|
s.authorLogFilePath = logPath // Store for UI access
|
||||||
defer logFile.Close()
|
defer logFile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -962,6 +962,8 @@ type appState struct {
|
||||||
authorChaptersRefresh func() // Refresh hook for chapter list UI
|
authorChaptersRefresh func() // Refresh hook for chapter list UI
|
||||||
authorDiscSize string // "DVD5" or "DVD9"
|
authorDiscSize string // "DVD5" or "DVD9"
|
||||||
authorLogText string
|
authorLogText string
|
||||||
|
authorLogLines []string // Circular buffer for last N lines
|
||||||
|
authorLogFilePath string // Path to log file for full viewing
|
||||||
authorLogEntry *widget.Entry
|
authorLogEntry *widget.Entry
|
||||||
authorLogScroll *container.Scroll
|
authorLogScroll *container.Scroll
|
||||||
authorProgress float64
|
authorProgress float64
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user