Add 'In Progress' tab to history sidebar

Features:
- New "In Progress" tab shows running/pending jobs
- Displays active jobs without opening full queue
- Tab positioned first for quick visibility
- Shows "Running..." or "Pending" status
- No delete button on active jobs (only completed/failed)

Implementation:
- Updated BuildHistorySidebar to accept activeJobs parameter
- Converts queue.Job to ui.HistoryEntry for display
- Filters running/pending jobs from queue
- Conditional delete button (nil check)
- Dynamic status text based on job state

UX Improvements:
- Quick glance at current activity without queue view
- Three-tab layout: In Progress → Completed → Failed
- Consistent styling with existing history entries
- Tappable entries to view full job details

This allows users to monitor active conversions directly
from the history sidebar, reducing the need to constantly
check the full job queue view.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Stu Leak 2025-12-18 18:02:03 -05:00
parent 925334d8df
commit 12b2b221b9
2 changed files with 56 additions and 11 deletions

View File

@ -153,6 +153,7 @@ func sortedKeys(m map[string][]fyne.CanvasObject) []string {
// BuildHistorySidebar creates the history sidebar with tabs
func BuildHistorySidebar(
entries []HistoryEntry,
activeJobs []HistoryEntry,
onEntryClick func(HistoryEntry),
onEntryDelete func(HistoryEntry),
titleColor, bgColor, textColor color.Color,
@ -168,11 +169,13 @@ func BuildHistorySidebar(
}
// Build lists
inProgressList := buildHistoryList(activeJobs, onEntryClick, nil, bgColor, textColor) // No delete for active jobs
completedList := buildHistoryList(completedEntries, onEntryClick, onEntryDelete, bgColor, textColor)
failedList := buildHistoryList(failedEntries, onEntryClick, onEntryDelete, bgColor, textColor)
// Tabs
// Tabs - In Progress first for quick visibility
tabs := container.NewAppTabs(
container.NewTabItem("In Progress", container.NewVScroll(inProgressList)),
container.NewTabItem("Completed", container.NewVScroll(completedList)),
container.NewTabItem("Failed", container.NewVScroll(failedList)),
)
@ -220,20 +223,37 @@ func buildHistoryItem(
// Capture entry for closures
capturedEntry := entry
// Delete button - small "×" button
deleteBtn := widget.NewButton("×", func() {
onEntryDelete(capturedEntry)
})
deleteBtn.Importance = widget.LowImportance
// Build header row with badge and optional delete button
headerItems := []fyne.CanvasObject{badge, layout.NewSpacer()}
if onEntryDelete != nil {
// Delete button - small "×" button (only for completed/failed)
deleteBtn := widget.NewButton("×", func() {
onEntryDelete(capturedEntry)
})
deleteBtn.Importance = widget.LowImportance
headerItems = append(headerItems, deleteBtn)
}
// Title
titleLabel := widget.NewLabel(utils.ShortenMiddle(entry.Title, 25))
titleLabel.TextStyle = fyne.TextStyle{Bold: true}
// Timestamp
timeStr := "Unknown"
if entry.CompletedAt != nil {
timeStr = entry.CompletedAt.Format("Jan 2, 15:04")
// Timestamp or status info
var timeStr string
if entry.Status == queue.JobStatusRunning || entry.Status == queue.JobStatusPending {
// For in-progress jobs, show status
if entry.Status == queue.JobStatusRunning {
timeStr = "Running..."
} else {
timeStr = "Pending"
}
} else {
// For completed/failed jobs, show timestamp
if entry.CompletedAt != nil {
timeStr = entry.CompletedAt.Format("Jan 2, 15:04")
} else {
timeStr = "Unknown"
}
}
timeLabel := widget.NewLabel(timeStr)
timeLabel.TextStyle = fyne.TextStyle{Monospace: true}
@ -246,7 +266,7 @@ func buildHistoryItem(
content := container.NewBorder(
nil, nil, statusRect, nil,
container.NewVBox(
container.NewHBox(badge, layout.NewSpacer(), deleteBtn),
container.NewHBox(headerItems...),
titleLabel,
timeLabel,
),

25
main.go
View File

@ -1372,8 +1372,33 @@ func (s *appState) showMainMenu() {
// Build sidebar if visible
var sidebar fyne.CanvasObject
if s.sidebarVisible {
// Get active jobs from queue (running/pending)
var activeJobs []ui.HistoryEntry
if s.jobQueue != nil {
for _, job := range s.jobQueue.List() {
if job.Status == queue.JobStatusRunning || job.Status == queue.JobStatusPending {
// Convert queue.Job to ui.HistoryEntry
entry := ui.HistoryEntry{
ID: job.ID,
Type: job.Type,
Status: job.Status,
Title: job.Title,
InputFile: job.InputFile,
OutputFile: job.OutputFile,
LogPath: job.LogPath,
Config: job.Config,
CreatedAt: job.CreatedAt,
StartedAt: job.StartedAt,
Error: job.Error,
}
activeJobs = append(activeJobs, entry)
}
}
}
sidebar = ui.BuildHistorySidebar(
s.historyEntries,
activeJobs,
s.showHistoryDetails,
s.deleteHistoryEntry,
titleColor,