Phase 5: Integrate sidebar into main menu
Integrated history sidebar into main menu with toggle button and split
view layout. Added history details dialog with FFmpeg command copy.
Changes:
- internal/ui/mainmenu.go:
* Updated BuildMainMenu() signature to accept sidebar parameters
* Added "☰ History" toggle button to header
* Implemented HSplit layout (20% sidebar, 80% main) when sidebar visible
- main.go:
* Added "sort" import for showHistoryDetails
* Added showHistoryDetails() method to display job details dialog
* Shows timestamps, config, error messages, FFmpeg command
* "Show in Folder" button (only if output file exists)
* "View Log" button (only if log file exists)
* Updated showMainMenu() to build and pass sidebar
* Implemented sidebar toggle that refreshes main menu
The sidebar can be toggled on/off from the main menu, shows history
entries with filtering by status (Completed vs Failed/Cancelled), and
clicking an entry opens a detailed view with all job information and
the ability to copy the FFmpeg command for manual execution.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
385c6f736d
commit
d7389a25bc
|
|
@ -43,13 +43,16 @@ type HistoryEntry struct {
|
|||
}
|
||||
|
||||
// BuildMainMenu creates the main menu view with module tiles grouped by category
|
||||
func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDrop func(string, []fyne.URI), onQueueClick func(), onLogsClick func(), onBenchmarkClick func(), onBenchmarkHistoryClick func(), titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject {
|
||||
func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDrop func(string, []fyne.URI), onQueueClick func(), onLogsClick func(), onBenchmarkClick func(), onBenchmarkHistoryClick func(), onToggleSidebar func(), sidebarVisible bool, sidebar fyne.CanvasObject, titleColor, queueColor, textColor color.Color, queueCompleted, queueTotal int) fyne.CanvasObject {
|
||||
title := canvas.NewText("VIDEOTOOLS", titleColor)
|
||||
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
|
||||
title.TextSize = 28
|
||||
|
||||
queueTile := buildQueueTile(queueCompleted, queueTotal, queueColor, textColor, onQueueClick)
|
||||
|
||||
sidebarToggleBtn := widget.NewButton("☰ History", onToggleSidebar)
|
||||
sidebarToggleBtn.Importance = widget.LowImportance
|
||||
|
||||
benchmarkBtn := widget.NewButton("Run Benchmark", onBenchmarkClick)
|
||||
benchmarkBtn.Importance = widget.LowImportance
|
||||
|
||||
|
|
@ -59,7 +62,7 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
|
|||
logsBtn := widget.NewButton("Logs", onLogsClick)
|
||||
logsBtn.Importance = widget.LowImportance
|
||||
|
||||
header := container.New(layout.NewHBoxLayout(), title, layout.NewSpacer(), benchmarkBtn, viewResultsBtn, logsBtn, queueTile)
|
||||
header := container.New(layout.NewHBoxLayout(), title, layout.NewSpacer(), sidebarToggleBtn, benchmarkBtn, viewResultsBtn, logsBtn, queueTile)
|
||||
|
||||
categorized := map[string][]fyne.CanvasObject{}
|
||||
for i := range modules {
|
||||
|
|
@ -103,6 +106,13 @@ func BuildMainMenu(modules []ModuleInfo, onModuleClick func(string), onModuleDro
|
|||
container.NewVBox(sections...),
|
||||
)
|
||||
|
||||
// Wrap with HSplit if sidebar is visible
|
||||
if sidebarVisible && sidebar != nil {
|
||||
split := container.NewHSplit(sidebar, body)
|
||||
split.Offset = 0.2
|
||||
return split
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
|
|
|
|||
116
main.go
116
main.go
|
|
@ -21,6 +21,7 @@ import (
|
|||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -815,6 +816,103 @@ func (s *appState) addToHistory(job *queue.Job) {
|
|||
}
|
||||
}
|
||||
|
||||
// showHistoryDetails displays detailed information about a history entry
|
||||
func (s *appState) showHistoryDetails(entry ui.HistoryEntry) {
|
||||
// Format config
|
||||
var configLines []string
|
||||
for key, value := range entry.Config {
|
||||
configLines = append(configLines, fmt.Sprintf("%s: %v", key, value))
|
||||
}
|
||||
sort.Strings(configLines)
|
||||
|
||||
// Format timestamps
|
||||
createdStr := entry.CreatedAt.Format("2006-01-02 15:04:05")
|
||||
startedStr := "N/A"
|
||||
if entry.StartedAt != nil {
|
||||
startedStr = entry.StartedAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
completedStr := "N/A"
|
||||
if entry.CompletedAt != nil {
|
||||
completedStr = entry.CompletedAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
details := fmt.Sprintf(`Type: %s
|
||||
Status: %s
|
||||
Input: %s
|
||||
Output: %s
|
||||
|
||||
Created: %s
|
||||
Started: %s
|
||||
Completed: %s
|
||||
|
||||
Config:
|
||||
%s`, entry.Type, entry.Status, entry.InputFile, entry.OutputFile,
|
||||
createdStr, startedStr, completedStr, strings.Join(configLines, "\n"))
|
||||
|
||||
if entry.Error != "" {
|
||||
details += fmt.Sprintf("\n\nError:\n%s", entry.Error)
|
||||
}
|
||||
|
||||
detailsLabel := widget.NewLabel(details)
|
||||
detailsLabel.Wrapping = fyne.TextWrapWord
|
||||
|
||||
// FFmpeg Command section
|
||||
var ffmpegSection fyne.CanvasObject
|
||||
if entry.FFmpegCmd != "" {
|
||||
cmdWidget := ui.NewFFmpegCommandWidget(entry.FFmpegCmd, s.window)
|
||||
ffmpegSection = container.NewVBox(
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("FFmpeg Command:"),
|
||||
cmdWidget,
|
||||
)
|
||||
}
|
||||
|
||||
// Buttons
|
||||
var buttons []fyne.CanvasObject
|
||||
|
||||
if entry.OutputFile != "" {
|
||||
if _, err := os.Stat(entry.OutputFile); err == nil {
|
||||
buttons = append(buttons, widget.NewButton("Show in Folder", func() {
|
||||
dir := filepath.Dir(entry.OutputFile)
|
||||
if err := openFolder(dir); err != nil {
|
||||
dialog.ShowError(err, s.window)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if entry.LogPath != "" {
|
||||
if _, err := os.Stat(entry.LogPath); err == nil {
|
||||
buttons = append(buttons, widget.NewButton("View Log", func() {
|
||||
s.openLogViewer(entry.Title, entry.LogPath, false)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
closeBtn := widget.NewButton("Close", nil)
|
||||
buttons = append(buttons, layout.NewSpacer(), closeBtn)
|
||||
|
||||
// Layout
|
||||
contentVBox := container.NewVBox(
|
||||
container.NewVScroll(detailsLabel),
|
||||
)
|
||||
if ffmpegSection != nil {
|
||||
contentVBox.Add(ffmpegSection)
|
||||
}
|
||||
|
||||
content := container.NewBorder(
|
||||
nil,
|
||||
container.NewHBox(buttons...),
|
||||
nil, nil,
|
||||
contentVBox,
|
||||
)
|
||||
|
||||
d := dialog.NewCustom("Job Details", "Close", content, s.window)
|
||||
d.Resize(fyne.NewSize(700, 600))
|
||||
closeBtn.OnTapped = func() { d.Hide() }
|
||||
d.Show()
|
||||
}
|
||||
|
||||
func (s *appState) stopPreview() {
|
||||
if s.anim != nil {
|
||||
s.anim.Stop()
|
||||
|
|
@ -1233,6 +1331,18 @@ func (s *appState) showMainMenu() {
|
|||
queueTotal = len(s.jobQueue.List())
|
||||
}
|
||||
|
||||
// Build sidebar if visible
|
||||
var sidebar fyne.CanvasObject
|
||||
if s.sidebarVisible {
|
||||
sidebar = ui.BuildHistorySidebar(
|
||||
s.historyEntries,
|
||||
s.showHistoryDetails,
|
||||
titleColor,
|
||||
utils.MustHex("#1A1F2E"),
|
||||
textColor,
|
||||
)
|
||||
}
|
||||
|
||||
menu := ui.BuildMainMenu(mods, s.showModule, s.handleModuleDrop, s.showQueue, func() {
|
||||
logDir := getLogsDir()
|
||||
_ = os.MkdirAll(logDir, 0o755)
|
||||
|
|
@ -1264,7 +1374,11 @@ func (s *appState) showMainMenu() {
|
|||
viewAppLogBtn,
|
||||
)
|
||||
dialog.ShowCustom("Logs", "Close", logOptions, s.window)
|
||||
}, s.showBenchmark, s.showBenchmarkHistory, titleColor, queueColor, textColor, queueCompleted, queueTotal)
|
||||
}, s.showBenchmark, s.showBenchmarkHistory, func() {
|
||||
// Toggle sidebar
|
||||
s.sidebarVisible = !s.sidebarVisible
|
||||
s.showMainMenu()
|
||||
}, s.sidebarVisible, sidebar, titleColor, queueColor, textColor, queueCompleted, queueTotal)
|
||||
|
||||
// Update stats bar
|
||||
s.updateStatsBar()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user