feat(favicon): preload cached favicons in stacked pane title bars
- commit
- f367b1694c73d319189fc9bcbbbdae894d5c347f
- author
- bnema · · 2025-12-16 22:03 UTC
- parent
- 2067abe
- signature
- unsigned
3 files · +53 -1
M internal/ui/cache/favicon_cache.go +24 -1
| @@ -124,12 +124,35 @@ func (fc *FaviconCache) Get(domain string) *gdk.Texture { | |||
| 124 | 124 | } | |
| 125 | 125 | ||
| 126 | 126 | // GetByURL retrieves a favicon texture by extracting the domain from the URL. | |
| 127 | - | // Returns nil if not found. | |
| 127 | + | // Returns nil if not found in memory cache. | |
| 128 | 128 | func (fc *FaviconCache) GetByURL(pageURL string) *gdk.Texture { | |
| 129 | 129 | domain := extractDomain(pageURL) | |
| 130 | 130 | return fc.Get(domain) | |
| 131 | 131 | } | |
| 132 | 132 | ||
| 133 | + | // GetFromCacheByURL checks both memory and disk cache for a favicon. | |
| 134 | + | // Returns nil if not found. Does not fetch from external sources. | |
| 135 | + | // If found on disk, also populates memory cache. | |
| 136 | + | func (fc *FaviconCache) GetFromCacheByURL(pageURL string) *gdk.Texture { | |
| 137 | + | domain := extractDomain(pageURL) | |
| 138 | + | if domain == "" { | |
| 139 | + | return nil | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // Check memory cache first | |
| 143 | + | if texture := fc.Get(domain); texture != nil { | |
| 144 | + | return texture | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | // Check disk cache | |
| 148 | + | if texture := fc.loadFromDisk(domain); texture != nil { | |
| 149 | + | fc.Set(domain, texture) | |
| 150 | + | return texture | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | return nil | |
| 154 | + | } | |
| 155 | + | ||
| 133 | 156 | // GetOrFetch checks the in-memory cache first, then queries the FaviconDatabase, | |
| 134 | 157 | // and falls back to Google's favicon API if not found. | |
| 135 | 158 | // The callback is invoked with the texture (or nil). | |
| @@ -124,12 +124,35 @@ func (fc *FaviconCache) Get(domain string) *gdk.Texture { | |||||
| 124 | } | 124 | } | ||
| 125 | 125 | ||||
| 126 | // GetByURL retrieves a favicon texture by extracting the domain from the URL. | 126 | // GetByURL retrieves a favicon texture by extracting the domain from the URL. | ||
| 127 | - | // Returns nil if not found. | 127 | + | // Returns nil if not found in memory cache. |
| 128 | func (fc *FaviconCache) GetByURL(pageURL string) *gdk.Texture { | 128 | func (fc *FaviconCache) GetByURL(pageURL string) *gdk.Texture { | ||
| 129 | domain := extractDomain(pageURL) | 129 | domain := extractDomain(pageURL) | ||
| 130 | return fc.Get(domain) | 130 | return fc.Get(domain) | ||
| 131 | } | 131 | } | ||
| 132 | 132 | ||||
| 133 | + | // GetFromCacheByURL checks both memory and disk cache for a favicon. | |||
| 134 | + | // Returns nil if not found. Does not fetch from external sources. | |||
| 135 | + | // If found on disk, also populates memory cache. | |||
| 136 | + | func (fc *FaviconCache) GetFromCacheByURL(pageURL string) *gdk.Texture { | |||
| 137 | + | domain := extractDomain(pageURL) | |||
| 138 | + | if domain == "" { | |||
| 139 | + | return nil | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | // Check memory cache first | |||
| 143 | + | if texture := fc.Get(domain); texture != nil { | |||
| 144 | + | return texture | |||
| 145 | + | } | |||
| 146 | + | ||||
| 147 | + | // Check disk cache | |||
| 148 | + | if texture := fc.loadFromDisk(domain); texture != nil { | |||
| 149 | + | fc.Set(domain, texture) | |||
| 150 | + | return texture | |||
| 151 | + | } | |||
| 152 | + | ||||
| 153 | + | return nil | |||
| 154 | + | } | |||
| 155 | + | ||||
| 133 | // GetOrFetch checks the in-memory cache first, then queries the FaviconDatabase, | 156 | // GetOrFetch checks the in-memory cache first, then queries the FaviconDatabase, | ||
| 134 | // and falls back to Google's favicon API if not found. | 157 | // and falls back to Google's favicon API if not found. | ||
| 135 | // The callback is invoked with the texture (or nil). | 158 | // The callback is invoked with the texture (or nil). | ||
M internal/ui/coordinator/content.go +27 -0
| @@ -364,3 +364,30 @@ func (c *ContentCoordinator) SetNavigationOrigin(paneID entity.PaneID, url strin | |||
| 364 | 364 | c.navOrigins[paneID] = url | |
| 365 | 365 | c.navOriginMu.Unlock() | |
| 366 | 366 | } | |
| 367 | + | ||
| 368 | + | // PreloadCachedFavicon checks the favicon cache and updates the stacked pane | |
| 369 | + | // title bar immediately if a cached favicon exists for the URL. | |
| 370 | + | // This provides instant favicon display without waiting for WebKit. | |
| 371 | + | func (c *ContentCoordinator) PreloadCachedFavicon(ctx context.Context, paneID entity.PaneID, url string) { | |
| 372 | + | if c.faviconCache == nil || url == "" { | |
| 373 | + | return | |
| 374 | + | } | |
| 375 | + | ||
| 376 | + | // Check memory and disk cache (no external fetch) | |
| 377 | + | texture := c.faviconCache.GetFromCacheByURL(url) | |
| 378 | + | if texture == nil { | |
| 379 | + | return | |
| 380 | + | } | |
| 381 | + | ||
| 382 | + | // Update stacked pane favicon if applicable | |
| 383 | + | ws, wsView := c.getActiveWS() | |
| 384 | + | if wsView != nil { | |
| 385 | + | tr := wsView.TreeRenderer() | |
| 386 | + | if tr != nil { | |
| 387 | + | stackedView := tr.GetStackedViewForPane(string(paneID)) | |
| 388 | + | if stackedView != nil { | |
| 389 | + | c.updateStackedPaneFavicon(ctx, ws, stackedView, paneID, texture) | |
| 390 | + | } | |
| 391 | + | } | |
| 392 | + | } | |
| 393 | + | } | |
| @@ -364,3 +364,30 @@ func (c *ContentCoordinator) SetNavigationOrigin(paneID entity.PaneID, url strin | |||||
| 364 | c.navOrigins[paneID] = url | 364 | c.navOrigins[paneID] = url | ||
| 365 | c.navOriginMu.Unlock() | 365 | c.navOriginMu.Unlock() | ||
| 366 | } | 366 | } | ||
| 367 | + | ||||
| 368 | + | // PreloadCachedFavicon checks the favicon cache and updates the stacked pane | |||
| 369 | + | // title bar immediately if a cached favicon exists for the URL. | |||
| 370 | + | // This provides instant favicon display without waiting for WebKit. | |||
| 371 | + | func (c *ContentCoordinator) PreloadCachedFavicon(ctx context.Context, paneID entity.PaneID, url string) { | |||
| 372 | + | if c.faviconCache == nil || url == "" { | |||
| 373 | + | return | |||
| 374 | + | } | |||
| 375 | + | ||||
| 376 | + | // Check memory and disk cache (no external fetch) | |||
| 377 | + | texture := c.faviconCache.GetFromCacheByURL(url) | |||
| 378 | + | if texture == nil { | |||
| 379 | + | return | |||
| 380 | + | } | |||
| 381 | + | ||||
| 382 | + | // Update stacked pane favicon if applicable | |||
| 383 | + | ws, wsView := c.getActiveWS() | |||
| 384 | + | if wsView != nil { | |||
| 385 | + | tr := wsView.TreeRenderer() | |||
| 386 | + | if tr != nil { | |||
| 387 | + | stackedView := tr.GetStackedViewForPane(string(paneID)) | |||
| 388 | + | if stackedView != nil { | |||
| 389 | + | c.updateStackedPaneFavicon(ctx, ws, stackedView, paneID, texture) | |||
| 390 | + | } | |||
| 391 | + | } | |||
| 392 | + | } | |||
| 393 | + | } | |||
M internal/ui/coordinator/navigation.go +2 -0
| @@ -57,6 +57,8 @@ func (c *NavigationCoordinator) Navigate(ctx context.Context, url string) error | |||
| 57 | 57 | paneID = string(pane.Pane.ID) | |
| 58 | 58 | // Track original URL for cross-domain redirect favicon caching | |
| 59 | 59 | c.contentCoord.SetNavigationOrigin(pane.Pane.ID, url) | |
| 60 | + | // Pre-load cached favicon for instant display in stacked pane title bar | |
| 61 | + | c.contentCoord.PreloadCachedFavicon(ctx, pane.Pane.ID, url) | |
| 60 | 62 | } | |
| 61 | 63 | } | |
| 62 | 64 | ||
| @@ -57,6 +57,8 @@ func (c *NavigationCoordinator) Navigate(ctx context.Context, url string) error | |||||
| 57 | paneID = string(pane.Pane.ID) | 57 | paneID = string(pane.Pane.ID) | ||
| 58 | // Track original URL for cross-domain redirect favicon caching | 58 | // Track original URL for cross-domain redirect favicon caching | ||
| 59 | c.contentCoord.SetNavigationOrigin(pane.Pane.ID, url) | 59 | c.contentCoord.SetNavigationOrigin(pane.Pane.ID, url) | ||
| 60 | + | // Pre-load cached favicon for instant display in stacked pane title bar | |||
| 61 | + | c.contentCoord.PreloadCachedFavicon(ctx, pane.Pane.ID, url) | |||
| 60 | } | 62 | } | ||
| 61 | } | 63 | } | ||
| 62 | 64 | ||||