package ui import ( "context" "fmt" "github.com/bnema/dumber/internal/application/port" "github.com/bnema/dumber/internal/application/usecase" "github.com/bnema/dumber/internal/logging" "github.com/bnema/dumber/internal/ui/component" "github.com/bnema/dumber/internal/ui/window" ) // initHistorySidebar creates and mounts the history sidebar into the // browser window's sidebar container. The sidebar is hidden by default. func (bw *browserWindow) initHistorySidebar(ctx context.Context, a *App) { if bw == nil || a == nil || bw.mainWindow == nil || a.deps == nil || a.deps.HistoryUC == nil { return } log := logging.FromContext(ctx) cfg := a.buildHistorySidebarConfig(bw) sidebar := component.NewHistorySidebar(ctx, cfg) if sidebar == nil { log.Warn().Msg("failed to create history sidebar") return } bw.historySidebar = sidebar // Mount into the main window's sidebar box. bw.mainWindow.SetSidebarWidget(sidebar.Widget()) bw.historySidebar.Hide() bw.mainWindow.SetSidebarVisible(false) bw.sidebarVisible = false bw.activeSidebarKind = nativeSidebarNone // Apply sidebar width from config, falling back to the default 320px. // The width is clamped to [280, 380] by SetSidebarWidth internally. bw.applySidebarWidthConfig(a) log.Debug().Msg("history sidebar initialized") } // buildHistorySidebarConfig constructs the HistorySidebarConfig for the given // browser window. Extracted from initHistorySidebar for testability. // //nolint:dupl // History and favorites sidebar configs intentionally mirror each other for separate component types. func (a *App) buildHistorySidebarConfig(bw *browserWindow) component.HistorySidebarConfig { var historyUC port.HistorySidebarHistory if a.deps != nil { historyUC = a.deps.HistoryUC } return component.HistorySidebarConfig{ HistoryUC: historyUC, OnNavigate: func(navCtx context.Context, url string) error { return a.navigateHistorySidebarSelection(navCtx, bw, url) }, OnNavigateKeepOpen: func(navCtx context.Context, url string) error { return a.navigateHistorySidebarSelection(navCtx, bw, url) }, OnOpenInNewPane: func(splitCtx context.Context, url string) error { if a.wsCoord == nil || !a.hasBrowserWindow(bw) { return nil } a.activateBrowserWindow(bw) return a.wsCoord.SplitWithURL(splitCtx, usecase.SplitRight, url) }, OnClose: func() { a.hideAndRestoreFocusForBrowserWindow(bw) }, } } func (a *App) navigateHistorySidebarSelection(ctx context.Context, bw *browserWindow, url string) error { if a == nil || bw == nil || !a.hasBrowserWindow(bw) { return nil } return a.navigateFromBrowserWindow(ctx, bw, url) } // toggleHistorySidebar toggles sidebar visibility. func (bw *browserWindow) toggleHistorySidebar() { if bw == nil || bw.historySidebar == nil { return } if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarHistory { bw.hideHistorySidebar() } else { bw.showHistorySidebar() } } // showHistorySidebar makes the sidebar visible and grabs focus for the search // entry. func (bw *browserWindow) showHistorySidebar() { if bw == nil || bw.historySidebar == nil || bw.mainWindow == nil { return } bw.mountNativeSidebar(nativeSidebarHistory) bw.historySidebar.Show() bw.mainWindow.SetSidebarVisible(true) bw.sidebarVisible = true bw.activeSidebarKind = nativeSidebarHistory } // hideHistorySidebar hides the sidebar. Callers should also restore focus // to the active content pane after calling this. func (bw *browserWindow) hideHistorySidebar() { if bw == nil || bw.historySidebar == nil || bw.mainWindow == nil { return } bw.historySidebar.Hide() bw.mainWindow.SetSidebarVisible(false) bw.sidebarVisible = false if bw.activeSidebarKind == nativeSidebarHistory { bw.activeSidebarKind = nativeSidebarNone } } func (bw *browserWindow) mountNativeSidebar(kind nativeSidebarKind) { if bw == nil || bw.mainWindow == nil || bw.activeSidebarKind == kind { return } if bw.historySidebar != nil { bw.historySidebar.Hide() } if bw.favoritesSidebar != nil { bw.favoritesSidebar.Hide() } switch kind { case nativeSidebarHistory: if bw.historySidebar != nil { bw.mainWindow.SetSidebarWidget(bw.historySidebar.Widget()) } case nativeSidebarFavorites: if bw.favoritesSidebar != nil { bw.mainWindow.SetSidebarWidget(bw.favoritesSidebar.Widget()) } } } // historySidebarWidthConfig extracts the config-backed sidebar width and // applies it via the MainWindow.SetSidebarWidth path. It is called during // initialization and can be reused if config is reloaded at runtime. func historySidebarWidthConfig(widthPx int) window.SidebarWidthConfig { cfg := window.SidebarDefaultWidth() if widthPx > 0 { cfg.WidthPx = widthPx } return cfg } // applySidebarWidthConfig applies the config-backed sidebar width to the // main window's sidebar. func (bw *browserWindow) applySidebarWidthConfig(a *App) { if bw == nil || bw.mainWindow == nil || a == nil { return } bw.mainWindow.SetSidebarWidth(historySidebarWidthConfig(a.runtimeConfigSnapshot().UI.SidebarWidth)) } // toggleHistorySidebarAction is the keyboard-action handler for toggling the // history sidebar on the last focused browser window. func (a *App) toggleHistorySidebarAction(ctx context.Context) error { bw := a.lastFocusedBrowserWindow() if bw == nil { return fmt.Errorf("history sidebar unavailable: no focused browser window") } if bw.historySidebar == nil { return fmt.Errorf("history sidebar unavailable: native sidebar not initialized") } if bw.sidebarVisible && bw.activeSidebarKind == nativeSidebarHistory { a.hideAndRestoreFocusForBrowserWindow(bw) return nil } bw.toggleHistorySidebar() return nil }