refactor(logging): migrate log.Printf to logging package
- commit
- 826661c824505953081811775cf89b587fc1e071
- author
- bnema · · 2025-11-29 12:04 UTC
- parent
- 5e0ca2d
- signature
- GPG Unverified
2B82207D370127A0The key is unknown
50 files · +867 -843
M internal/app/api/scheme.go +11 -11
| @@ -3,7 +3,6 @@ package api | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "embed" | 4 | "embed" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "mime" | 6 | "mime" | ||
| 8 | neturl "net/url" | 7 | neturl "net/url" | ||
| 9 | "os" | 8 | "os" | ||
| @@ -12,6 +11,7 @@ import ( | |||||
| 12 | 11 | ||||
| 13 | "github.com/bnema/dumber/internal/app/constants" | 12 | "github.com/bnema/dumber/internal/app/constants" | ||
| 14 | "github.com/bnema/dumber/internal/config" | 13 | "github.com/bnema/dumber/internal/config" | ||
| 14 | + | "github.com/bnema/dumber/internal/logging" | |||
| 15 | "github.com/bnema/dumber/internal/services" | 15 | "github.com/bnema/dumber/internal/services" | ||
| 16 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 16 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 17 | "github.com/diamondburned/gotk4/pkg/gio/v2" | 17 | "github.com/diamondburned/gotk4/pkg/gio/v2" | ||
| @@ -47,7 +47,7 @@ func (s *SchemeHandler) SetConfig(cfg *config.Config) { | |||||
| 47 | // Handle processes dumb:// scheme requests using the new URISchemeRequest API | 47 | // Handle processes dumb:// scheme requests using the new URISchemeRequest API | ||
| 48 | func (s *SchemeHandler) Handle(req *webkit.URISchemeRequest) { | 48 | func (s *SchemeHandler) Handle(req *webkit.URISchemeRequest) { | ||
| 49 | uri := req.URI() | 49 | uri := req.URI() | ||
| 50 | - | log.Printf("[scheme] request: %s", uri) | 50 | + | logging.Debug(fmt.Sprintf("[scheme] request: %s", uri)) |
| 51 | 51 | ||||
| 52 | // Known forms: | 52 | // Known forms: | ||
| 53 | // - dumb://homepage or dumb:homepage → index.html | 53 | // - dumb://homepage or dumb:homepage → index.html | ||
| @@ -96,7 +96,7 @@ func (s *SchemeHandler) handleAsset(req *webkit.URISchemeRequest, u *neturl.URL) | |||||
| 96 | 96 | ||||
| 97 | // Special-case homepage favicon: map .ico request to embedded PNG file | 97 | // Special-case homepage favicon: map .ico request to embedded PNG file | ||
| 98 | if (u.Host == constants.HomepagePath || u.Opaque == constants.HomepagePath) && strings.EqualFold(rel, "favicon.ico") { | 98 | if (u.Host == constants.HomepagePath || u.Opaque == constants.HomepagePath) && strings.EqualFold(rel, "favicon.ico") { | ||
| 99 | - | log.Printf("[scheme] asset: rel=%s (host=%s path=%s) → mapping to favicon.png", rel, u.Host, u.Path) | 99 | + | logging.Debug(fmt.Sprintf("[scheme] asset: rel=%s (host=%s path=%s) → mapping to favicon.png", rel, u.Host, u.Path)) |
| 100 | data, rerr := s.assets.ReadFile(filepath.ToSlash(filepath.Join("assets", "gui", "favicon.png"))) | 100 | data, rerr := s.assets.ReadFile(filepath.ToSlash(filepath.Join("assets", "gui", "favicon.png"))) | ||
| 101 | if rerr == nil { | 101 | if rerr == nil { | ||
| 102 | s.finishRequest(req, "image/png", data, "favicon.png") | 102 | s.finishRequest(req, "image/png", data, "favicon.png") | ||
| @@ -104,12 +104,12 @@ func (s *SchemeHandler) handleAsset(req *webkit.URISchemeRequest, u *neturl.URL) | |||||
| 104 | } | 104 | } | ||
| 105 | } | 105 | } | ||
| 106 | 106 | ||||
| 107 | - | log.Printf("[scheme] asset: rel=%s (host=%s path=%s)", rel, u.Host, u.Path) | 107 | + | logging.Debug(fmt.Sprintf("[scheme] asset: rel=%s (host=%s path=%s)", rel, u.Host, u.Path)) |
| 108 | 108 | ||||
| 109 | // Try to read the requested asset | 109 | // Try to read the requested asset | ||
| 110 | data, rerr := s.assets.ReadFile(filepath.ToSlash(filepath.Join("assets", "gui", rel))) | 110 | data, rerr := s.assets.ReadFile(filepath.ToSlash(filepath.Join("assets", "gui", rel))) | ||
| 111 | if rerr != nil { | 111 | if rerr != nil { | ||
| 112 | - | log.Printf("[scheme] not found: %s", rel) | 112 | + | logging.Debug(fmt.Sprintf("[scheme] not found: %s", rel)) |
| 113 | req.FinishError(fmt.Errorf("asset not found: %s", rel)) | 113 | req.FinishError(fmt.Errorf("asset not found: %s", rel)) | ||
| 114 | return | 114 | return | ||
| 115 | } | 115 | } | ||
| @@ -118,7 +118,7 @@ func (s *SchemeHandler) handleAsset(req *webkit.URISchemeRequest, u *neturl.URL) | |||||
| 118 | mt := s.getMimeType(rel) | 118 | mt := s.getMimeType(rel) | ||
| 119 | 119 | ||||
| 120 | // Finish the request with the data | 120 | // Finish the request with the data | ||
| 121 | - | log.Printf("[scheme] serving %s with mime-type: %s", rel, mt) | 121 | + | logging.Debug(fmt.Sprintf("[scheme] serving %s with mime-type: %s", rel, mt)) |
| 122 | s.finishRequest(req, mt, data, rel) | 122 | s.finishRequest(req, mt, data, rel) | ||
| 123 | } | 123 | } | ||
| 124 | 124 | ||||
| @@ -131,7 +131,7 @@ func (s *SchemeHandler) finishRequest(req *webkit.URISchemeRequest, mimeType str | |||||
| 131 | stream := gio.NewMemoryInputStreamFromBytes(gbytes) | 131 | stream := gio.NewMemoryInputStreamFromBytes(gbytes) | ||
| 132 | if stream == nil { | 132 | if stream == nil { | ||
| 133 | req.FinishError(fmt.Errorf("failed to create input stream for: %s", filename)) | 133 | req.FinishError(fmt.Errorf("failed to create input stream for: %s", filename)) | ||
| 134 | - | log.Printf("[scheme] failed to create stream for: %s", filename) | 134 | + | logging.Error(fmt.Sprintf("[scheme] failed to create stream for: %s", filename)) |
| 135 | return | 135 | return | ||
| 136 | } | 136 | } | ||
| 137 | 137 | ||||
| @@ -204,7 +204,7 @@ func (s *SchemeHandler) handleFavicon(req *webkit.URISchemeRequest, u *neturl.UR | |||||
| 204 | } | 204 | } | ||
| 205 | 205 | ||||
| 206 | if filename == "" { | 206 | if filename == "" { | ||
| 207 | - | log.Printf("[scheme] favicon: empty filename") | 207 | + | logging.Debug(fmt.Sprintf("[scheme] favicon: empty filename")) |
| 208 | req.FinishError(fmt.Errorf("invalid favicon path")) | 208 | req.FinishError(fmt.Errorf("invalid favicon path")) | ||
| 209 | return | 209 | return | ||
| 210 | } | 210 | } | ||
| @@ -212,7 +212,7 @@ func (s *SchemeHandler) handleFavicon(req *webkit.URISchemeRequest, u *neturl.UR | |||||
| 212 | // Get the favicon cache directory path | 212 | // Get the favicon cache directory path | ||
| 213 | dataDir, err := config.GetDataDir() | 213 | dataDir, err := config.GetDataDir() | ||
| 214 | if err != nil { | 214 | if err != nil { | ||
| 215 | - | log.Printf("[scheme] favicon: failed to get data directory: %v", err) | 215 | + | logging.Error(fmt.Sprintf("[scheme] favicon: failed to get data directory: %v", err)) |
| 216 | req.FinishError(fmt.Errorf("failed to get data directory")) | 216 | req.FinishError(fmt.Errorf("failed to get data directory")) | ||
| 217 | return | 217 | return | ||
| 218 | } | 218 | } | ||
| @@ -222,11 +222,11 @@ func (s *SchemeHandler) handleFavicon(req *webkit.URISchemeRequest, u *neturl.UR | |||||
| 222 | // Read the favicon file | 222 | // Read the favicon file | ||
| 223 | data, err := os.ReadFile(faviconPath) | 223 | data, err := os.ReadFile(faviconPath) | ||
| 224 | if err != nil { | 224 | if err != nil { | ||
| 225 | - | log.Printf("[scheme] favicon: file not found: %s", faviconPath) | 225 | + | logging.Debug(fmt.Sprintf("[scheme] favicon: file not found: %s", faviconPath)) |
| 226 | req.FinishError(fmt.Errorf("favicon not found: %s", filename)) | 226 | req.FinishError(fmt.Errorf("favicon not found: %s", filename)) | ||
| 227 | return | 227 | return | ||
| 228 | } | 228 | } | ||
| 229 | 229 | ||||
| 230 | - | log.Printf("[scheme] favicon: serving %s (%d bytes)", filename, len(data)) | 230 | + | logging.Debug(fmt.Sprintf("[scheme] favicon: serving %s (%d bytes)", filename, len(data))) |
| 231 | s.finishRequest(req, "image/png", data, filename) | 231 | s.finishRequest(req, "image/png", data, filename) | ||
| 232 | } | 232 | } | ||
M internal/app/browser/browser.go +42 -43
| @@ -5,7 +5,6 @@ import ( | |||||
| 5 | "database/sql" | 5 | "database/sql" | ||
| 6 | "embed" | 6 | "embed" | ||
| 7 | "fmt" | 7 | "fmt" | ||
| 8 | - | "log" | |||
| 9 | "os" | 8 | "os" | ||
| 10 | "os/signal" | 9 | "os/signal" | ||
| 11 | "runtime" | 10 | "runtime" | ||
| @@ -58,8 +57,8 @@ type BrowserApp struct { | |||||
| 58 | workspace *WorkspaceManager | 57 | workspace *WorkspaceManager | ||
| 59 | 58 | ||||
| 60 | // Content filtering | 59 | // Content filtering | ||
| 61 | - | filterManager *filtering.FilterManager | 60 | + | filterManager *filtering.FilterManager |
| 62 | - | contentBlockingService *filtering.ContentBlockingService | 61 | + | contentBlockingService *filtering.ContentBlockingService |
| 63 | 62 | ||||
| 64 | // Handlers | 63 | // Handlers | ||
| 65 | schemeHandler *api.SchemeHandler | 64 | schemeHandler *api.SchemeHandler | ||
| @@ -71,7 +70,7 @@ type BrowserApp struct { | |||||
| 71 | // Run starts the browser application | 70 | // Run starts the browser application | ||
| 72 | func Run(assets embed.FS, version, commit, buildDate string) { | 71 | func Run(assets embed.FS, version, commit, buildDate string) { | ||
| 73 | startupStart := time.Now() | 72 | startupStart := time.Now() | ||
| 74 | - | log.Printf("Starting GUI mode (webkit_cgo=%v)", webkit.IsNativeAvailable()) | 73 | + | logging.Info(fmt.Sprintf("Starting GUI mode (webkit_cgo=%v)", webkit.IsNativeAvailable())) |
| 75 | 74 | ||||
| 76 | app := &BrowserApp{ | 75 | app := &BrowserApp{ | ||
| 77 | version: version, | 76 | version: version, | ||
| @@ -81,7 +80,7 @@ func Run(assets embed.FS, version, commit, buildDate string) { | |||||
| 81 | } | 80 | } | ||
| 82 | 81 | ||||
| 83 | if err := app.Initialize(); err != nil { | 82 | if err := app.Initialize(); err != nil { | ||
| 84 | - | log.Printf("Failed to initialize browser: %v", err) | 83 | + | logging.Error(fmt.Sprintf("Failed to initialize browser: %v", err)) |
| 85 | if webkit.IsNativeAvailable() { | 84 | if webkit.IsNativeAvailable() { | ||
| 86 | runtime.UnlockOSThread() | 85 | runtime.UnlockOSThread() | ||
| 87 | } | 86 | } | ||
| @@ -89,9 +88,9 @@ func Run(assets embed.FS, version, commit, buildDate string) { | |||||
| 89 | } | 88 | } | ||
| 90 | 89 | ||||
| 91 | startupElapsed := time.Since(startupStart) | 90 | startupElapsed := time.Since(startupStart) | ||
| 92 | - | log.Printf("[startup] Application initialized in %v", startupElapsed) | 91 | + | logging.Info(fmt.Sprintf("[startup] Application initialized in %v", startupElapsed)) |
| 93 | if startupElapsed > 500*time.Millisecond { | 92 | if startupElapsed > 500*time.Millisecond { | ||
| 94 | - | log.Printf("[startup] WARNING: Startup took %v (target: <500ms)", startupElapsed) | 93 | + | logging.Warn(fmt.Sprintf("[startup] WARNING: Startup took %v (target: <500ms)", startupElapsed)) |
| 95 | } | 94 | } | ||
| 96 | 95 | ||||
| 97 | app.Run() | 96 | app.Run() | ||
| @@ -116,20 +115,20 @@ func (app *BrowserApp) Initialize() error { | |||||
| 116 | 115 | ||||
| 117 | // Setup output capture if configured | 116 | // Setup output capture if configured | ||
| 118 | if err := app.setupOutputCapture(); err != nil { | 117 | if err := app.setupOutputCapture(); err != nil { | ||
| 119 | - | log.Printf("Warning: failed to setup output capture: %v", err) | 118 | + | logging.Warn(fmt.Sprintf("Warning: failed to setup output capture: %v", err)) |
| 120 | } | 119 | } | ||
| 121 | 120 | ||||
| 122 | // Initialize WebKit log capture if configured | 121 | // Initialize WebKit log capture if configured | ||
| 123 | if app.config.Logging.CaptureCOutput { | 122 | if app.config.Logging.CaptureCOutput { | ||
| 124 | if err := webkit.InitWebKitLogCapture(); err != nil { | 123 | if err := webkit.InitWebKitLogCapture(); err != nil { | ||
| 125 | - | log.Printf("Warning: failed to initialize WebKit log capture: %v", err) | 124 | + | logging.Warn(fmt.Sprintf("Warning: failed to initialize WebKit log capture: %v", err)) |
| 126 | } else { | 125 | } else { | ||
| 127 | defer webkit.StopWebKitLogCapture() | 126 | defer webkit.StopWebKitLogCapture() | ||
| 128 | webkit.StartWebKitOutputCapture() | 127 | webkit.StartWebKitOutputCapture() | ||
| 129 | } | 128 | } | ||
| 130 | } | 129 | } | ||
| 131 | 130 | ||||
| 132 | - | log.Printf("Config initialized") | 131 | + | logging.Info(fmt.Sprintf("Config initialized")) |
| 133 | 132 | ||||
| 134 | // Detect keyboard layout | 133 | // Detect keyboard layout | ||
| 135 | environment.DetectAndSetKeyboardLocale() | 134 | environment.DetectAndSetKeyboardLocale() | ||
| @@ -141,11 +140,11 @@ func (app *BrowserApp) Initialize() error { | |||||
| 141 | } | 140 | } | ||
| 142 | app.database = database | 141 | app.database = database | ||
| 143 | app.queries = db.New(database) | 142 | app.queries = db.New(database) | ||
| 144 | - | log.Printf("Database opened at %s", app.config.Database.Path) | 143 | + | logging.Info(fmt.Sprintf("Database opened at %s", app.config.Database.Path)) |
| 145 | 144 | ||||
| 146 | // Cleanup expired certificate validations | 145 | // Cleanup expired certificate validations | ||
| 147 | if err := webkit.CleanupExpiredCertificateValidations(); err != nil { | 146 | if err := webkit.CleanupExpiredCertificateValidations(); err != nil { | ||
| 148 | - | log.Printf("Warning: failed to cleanup expired certificate validations: %v", err) | 147 | + | logging.Warn(fmt.Sprintf("Warning: failed to cleanup expired certificate validations: %v", err)) |
| 149 | } | 148 | } | ||
| 150 | 149 | ||||
| 151 | // Initialize services | 150 | // Initialize services | ||
| @@ -154,7 +153,7 @@ func (app *BrowserApp) Initialize() error { | |||||
| 154 | 153 | ||||
| 155 | // Load all caches in parallel for fast startup (target: <100ms) | 154 | // Load all caches in parallel for fast startup (target: <100ms) | ||
| 156 | if err := app.loadCachesParallel(context.Background()); err != nil { | 155 | if err := app.loadCachesParallel(context.Background()); err != nil { | ||
| 157 | - | log.Printf("Warning: failed to load caches: %v", err) | 156 | + | logging.Warn(fmt.Sprintf("Warning: failed to load caches: %v", err)) |
| 158 | // Non-fatal - caches will fall back to defaults or DB queries | 157 | // Non-fatal - caches will fall back to defaults or DB queries | ||
| 159 | } | 158 | } | ||
| 160 | 159 | ||||
| @@ -176,7 +175,7 @@ func (app *BrowserApp) loadCachesParallel(ctx context.Context) error { | |||||
| 176 | // Load zoom cache in parallel | 175 | // Load zoom cache in parallel | ||
| 177 | g.Go(func() error { | 176 | g.Go(func() error { | ||
| 178 | if err := app.browserService.LoadZoomCacheFromDB(ctx); err != nil { | 177 | if err := app.browserService.LoadZoomCacheFromDB(ctx); err != nil { | ||
| 179 | - | log.Printf("[cache] Failed to load zoom cache: %v", err) | 178 | + | logging.Error(fmt.Sprintf("[cache] Failed to load zoom cache: %v", err)) |
| 180 | return err | 179 | return err | ||
| 181 | } | 180 | } | ||
| 182 | return nil | 181 | return nil | ||
| @@ -185,7 +184,7 @@ func (app *BrowserApp) loadCachesParallel(ctx context.Context) error { | |||||
| 185 | // Load certificate validation cache in parallel | 184 | // Load certificate validation cache in parallel | ||
| 186 | g.Go(func() error { | 185 | g.Go(func() error { | ||
| 187 | if err := app.browserService.LoadCertCacheFromDB(ctx); err != nil { | 186 | if err := app.browserService.LoadCertCacheFromDB(ctx); err != nil { | ||
| 188 | - | log.Printf("[cache] Failed to load cert cache: %v", err) | 187 | + | logging.Error(fmt.Sprintf("[cache] Failed to load cert cache: %v", err)) |
| 189 | return err | 188 | return err | ||
| 190 | } | 189 | } | ||
| 191 | return nil | 190 | return nil | ||
| @@ -194,7 +193,7 @@ func (app *BrowserApp) loadCachesParallel(ctx context.Context) error { | |||||
| 194 | // Load favorites cache in parallel | 193 | // Load favorites cache in parallel | ||
| 195 | g.Go(func() error { | 194 | g.Go(func() error { | ||
| 196 | if err := app.browserService.LoadFavoritesCacheFromDB(ctx); err != nil { | 195 | if err := app.browserService.LoadFavoritesCacheFromDB(ctx); err != nil { | ||
| 197 | - | log.Printf("[cache] Failed to load favorites cache: %v", err) | 196 | + | logging.Error(fmt.Sprintf("[cache] Failed to load favorites cache: %v", err)) |
| 198 | return err | 197 | return err | ||
| 199 | } | 198 | } | ||
| 200 | return nil | 199 | return nil | ||
| @@ -203,7 +202,7 @@ func (app *BrowserApp) loadCachesParallel(ctx context.Context) error { | |||||
| 203 | // Load fuzzy search cache in parallel for instant dmenu access | 202 | // Load fuzzy search cache in parallel for instant dmenu access | ||
| 204 | g.Go(func() error { | 203 | g.Go(func() error { | ||
| 205 | if err := app.browserService.LoadFuzzyCacheFromDB(ctx); err != nil { | 204 | if err := app.browserService.LoadFuzzyCacheFromDB(ctx); err != nil { | ||
| 206 | - | log.Printf("[cache] Failed to load fuzzy cache: %v", err) | 205 | + | logging.Error(fmt.Sprintf("[cache] Failed to load fuzzy cache: %v", err)) |
| 207 | return err | 206 | return err | ||
| 208 | } | 207 | } | ||
| 209 | return nil | 208 | return nil | ||
| @@ -215,11 +214,11 @@ func (app *BrowserApp) loadCachesParallel(ctx context.Context) error { | |||||
| 215 | } | 214 | } | ||
| 216 | 215 | ||||
| 217 | elapsed := time.Since(startTime) | 216 | elapsed := time.Since(startTime) | ||
| 218 | - | log.Printf("[cache] All caches loaded in %v", elapsed) | 217 | + | logging.Info(fmt.Sprintf("[cache] All caches loaded in %v", elapsed)) |
| 219 | 218 | ||||
| 220 | // Warn if startup is slower than target | 219 | // Warn if startup is slower than target | ||
| 221 | if elapsed > 100*time.Millisecond { | 220 | if elapsed > 100*time.Millisecond { | ||
| 222 | - | log.Printf("[cache] Warning: Cache loading took %v (target: <100ms)", elapsed) | 221 | + | logging.Warn(fmt.Sprintf("[cache] Warning: Cache loading took %v (target: <100ms)", elapsed)) |
| 223 | } | 222 | } | ||
| 224 | 223 | ||||
| 225 | return nil | 224 | return nil | ||
| @@ -239,19 +238,19 @@ func (app *BrowserApp) Run() { | |||||
| 239 | // Initialize content blocking BEFORE creating WebViews so the service is ready | 238 | // Initialize content blocking BEFORE creating WebViews so the service is ready | ||
| 240 | // to register WebViews as they are created (tabs, workspaces, popups) | 239 | // to register WebViews as they are created (tabs, workspaces, popups) | ||
| 241 | if err := app.setupContentBlocking(); err != nil { | 240 | if err := app.setupContentBlocking(); err != nil { | ||
| 242 | - | log.Printf("Warning: failed to setup content blocking: %v", err) | 241 | + | logging.Warn(fmt.Sprintf("Warning: failed to setup content blocking: %v", err)) |
| 243 | // Continue without content blocking | 242 | // Continue without content blocking | ||
| 244 | } | 243 | } | ||
| 245 | 244 | ||||
| 246 | // Create and setup WebView (this also creates TabManager which creates more WebViews) | 245 | // Create and setup WebView (this also creates TabManager which creates more WebViews) | ||
| 247 | if err := app.createWebView(); err != nil { | 246 | if err := app.createWebView(); err != nil { | ||
| 248 | - | log.Printf("Warning: failed to create WebView: %v", err) | 247 | + | logging.Warn(fmt.Sprintf("Warning: failed to create WebView: %v", err)) |
| 249 | return | 248 | return | ||
| 250 | } | 249 | } | ||
| 251 | 250 | ||||
| 252 | // Apply URI scheme handlers after WebView creation | 251 | // Apply URI scheme handlers after WebView creation | ||
| 253 | if err := webkit.ApplyURISchemeHandlers(app.webView.GetWebView()); err != nil { | 252 | if err := webkit.ApplyURISchemeHandlers(app.webView.GetWebView()); err != nil { | ||
| 254 | - | log.Printf("Warning: failed to register URI scheme handlers: %v", err) | 253 | + | logging.Warn(fmt.Sprintf("Warning: failed to register URI scheme handlers: %v", err)) |
| 255 | } | 254 | } | ||
| 256 | 255 | ||||
| 257 | // Handle browse command if present (must use active tab's navigation controller) | 256 | // Handle browse command if present (must use active tab's navigation controller) | ||
| @@ -277,52 +276,52 @@ func (app *BrowserApp) Run() { | |||||
| 277 | 276 | ||||
| 278 | // cleanup handles cleanup on shutdown | 277 | // cleanup handles cleanup on shutdown | ||
| 279 | func (app *BrowserApp) cleanup() { | 278 | func (app *BrowserApp) cleanup() { | ||
| 280 | - | log.Printf("Starting browser cleanup...") | 279 | + | logging.Info(fmt.Sprintf("Starting browser cleanup...")) |
| 281 | 280 | ||||
| 282 | // Cleanup window shortcuts first | 281 | // Cleanup window shortcuts first | ||
| 283 | if app.windowShortcutHandler != nil { | 282 | if app.windowShortcutHandler != nil { | ||
| 284 | - | log.Printf("Cleaning up window shortcuts") | 283 | + | logging.Info(fmt.Sprintf("Cleaning up window shortcuts")) |
| 285 | app.windowShortcutHandler.Cleanup() | 284 | app.windowShortcutHandler.Cleanup() | ||
| 286 | app.windowShortcutHandler = nil | 285 | app.windowShortcutHandler = nil | ||
| 287 | } | 286 | } | ||
| 288 | 287 | ||||
| 289 | // Cleanup tab manager (which cleans up all tabs and their workspaces) | 288 | // Cleanup tab manager (which cleans up all tabs and their workspaces) | ||
| 290 | if app.tabManager != nil { | 289 | if app.tabManager != nil { | ||
| 291 | - | log.Printf("Cleaning up tab manager") | 290 | + | logging.Info(fmt.Sprintf("Cleaning up tab manager")) |
| 292 | app.tabManager.Cleanup() | 291 | app.tabManager.Cleanup() | ||
| 293 | app.tabManager = nil | 292 | app.tabManager = nil | ||
| 294 | } | 293 | } | ||
| 295 | 294 | ||||
| 296 | // Flush all caches to ensure pending writes complete before database closes | 295 | // Flush all caches to ensure pending writes complete before database closes | ||
| 297 | if app.browserService != nil { | 296 | if app.browserService != nil { | ||
| 298 | - | log.Printf("Flushing all caches...") | 297 | + | logging.Info(fmt.Sprintf("Flushing all caches...")) |
| 299 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | 298 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| 300 | defer cancel() | 299 | defer cancel() | ||
| 301 | if err := app.browserService.FlushAllCaches(ctx); err != nil { | 300 | if err := app.browserService.FlushAllCaches(ctx); err != nil { | ||
| 302 | - | log.Printf("Warning: failed to flush caches: %v", err) | 301 | + | logging.Warn(fmt.Sprintf("Warning: failed to flush caches: %v", err)) |
| 303 | } | 302 | } | ||
| 304 | } | 303 | } | ||
| 305 | 304 | ||||
| 306 | // Close database with WAL checkpoint | 305 | // Close database with WAL checkpoint | ||
| 307 | if app.database != nil { | 306 | if app.database != nil { | ||
| 308 | - | log.Printf("Performing WAL checkpoint and closing database...") | 307 | + | logging.Info(fmt.Sprintf("Performing WAL checkpoint and closing database...")) |
| 309 | 308 | ||||
| 310 | // Run WAL checkpoint to commit all pending writes and truncate WAL file | 309 | // Run WAL checkpoint to commit all pending writes and truncate WAL file | ||
| 311 | if _, err := app.database.Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil { | 310 | if _, err := app.database.Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil { | ||
| 312 | - | log.Printf("Warning: WAL checkpoint failed: %v", err) | 311 | + | logging.Warn(fmt.Sprintf("Warning: WAL checkpoint failed: %v", err)) |
| 313 | } else { | 312 | } else { | ||
| 314 | - | log.Printf("WAL checkpoint completed successfully") | 313 | + | logging.Info(fmt.Sprintf("WAL checkpoint completed successfully")) |
| 315 | } | 314 | } | ||
| 316 | 315 | ||||
| 317 | // Close database connection | 316 | // Close database connection | ||
| 318 | if closeErr := app.database.Close(); closeErr != nil { | 317 | if closeErr := app.database.Close(); closeErr != nil { | ||
| 319 | - | log.Printf("Warning: failed to close database: %v", closeErr) | 318 | + | logging.Warn(fmt.Sprintf("Warning: failed to close database: %v", closeErr)) |
| 320 | } else { | 319 | } else { | ||
| 321 | - | log.Printf("Database closed successfully") | 320 | + | logging.Info(fmt.Sprintf("Database closed successfully")) |
| 322 | } | 321 | } | ||
| 323 | } | 322 | } | ||
| 324 | 323 | ||||
| 325 | - | log.Printf("Browser cleanup completed") | 324 | + | logging.Info(fmt.Sprintf("Browser cleanup completed")) |
| 326 | } | 325 | } | ||
| 327 | 326 | ||||
| 328 | // setupOutputCapture initializes stdout/stderr capture if configured | 327 | // setupOutputCapture initializes stdout/stderr capture if configured | ||
| @@ -331,11 +330,11 @@ func (app *BrowserApp) setupOutputCapture() error { | |||||
| 331 | // Capturing stdout/stderr works for non-GTK builds, but in native GTK mode | 330 | // Capturing stdout/stderr works for non-GTK builds, but in native GTK mode | ||
| 332 | // it interferes with WebKit's own pipe management and crashes immediately. | 331 | // it interferes with WebKit's own pipe management and crashes immediately. | ||
| 333 | if webkit.IsNativeAvailable() { | 332 | if webkit.IsNativeAvailable() { | ||
| 334 | - | log.Printf("Warning: stdout/stderr capture is not supported in native GTK mode; skipping") | 333 | + | logging.Warn(fmt.Sprintf("Warning: stdout/stderr capture is not supported in native GTK mode; skipping")) |
| 335 | return nil | 334 | return nil | ||
| 336 | } | 335 | } | ||
| 337 | 336 | ||||
| 338 | - | log.Printf("Warning: stdout/stderr capture is experimental and may interfere with normal operations") | 337 | + | logging.Warn(fmt.Sprintf("Warning: stdout/stderr capture is experimental and may interfere with normal operations")) |
| 339 | if logger := logging.GetLogger(); logger != nil { | 338 | if logger := logging.GetLogger(); logger != nil { | ||
| 340 | outputCapture := logging.NewOutputCapture(logger) | 339 | outputCapture := logging.NewOutputCapture(logger) | ||
| 341 | if err := outputCapture.Start(); err != nil { | 340 | if err := outputCapture.Start(); err != nil { | ||
| @@ -355,7 +354,7 @@ func (app *BrowserApp) setupSignalHandling() { | |||||
| 355 | // Handle signals in a goroutine to quit the main loop | 354 | // Handle signals in a goroutine to quit the main loop | ||
| 356 | go func() { | 355 | go func() { | ||
| 357 | sig := <-sigChan | 356 | sig := <-sigChan | ||
| 358 | - | log.Printf("Received signal %v - shutting down gracefully", sig) | 357 | + | logging.Info(fmt.Sprintf("Received signal %v - shutting down gracefully", sig)) |
| 359 | webkit.QuitMainLoop() | 358 | webkit.QuitMainLoop() | ||
| 360 | }() | 359 | }() | ||
| 361 | } | 360 | } | ||
| @@ -363,29 +362,29 @@ func (app *BrowserApp) setupSignalHandling() { | |||||
| 363 | // runMainLoop starts the appropriate main loop based on WebKit availability | 362 | // runMainLoop starts the appropriate main loop based on WebKit availability | ||
| 364 | func (app *BrowserApp) runMainLoop() { | 363 | func (app *BrowserApp) runMainLoop() { | ||
| 365 | if webkit.IsNativeAvailable() { | 364 | if webkit.IsNativeAvailable() { | ||
| 366 | - | log.Printf("Entering GTK main loop…") | 365 | + | logging.Info(fmt.Sprintf("Entering GTK main loop…")) |
| 367 | webkit.RunMainLoop() | 366 | webkit.RunMainLoop() | ||
| 368 | - | log.Printf("GTK main loop exited") | 367 | + | logging.Info(fmt.Sprintf("GTK main loop exited")) |
| 369 | 368 | ||||
| 370 | // Flush pending history writes immediately after main loop exit | 369 | // Flush pending history writes immediately after main loop exit | ||
| 371 | // This ensures database operations complete while GTK is still in a valid state | 370 | // This ensures database operations complete while GTK is still in a valid state | ||
| 372 | // MUST happen before cleanup() deferred call, which happens after this function returns | 371 | // MUST happen before cleanup() deferred call, which happens after this function returns | ||
| 373 | if app.browserService != nil { | 372 | if app.browserService != nil { | ||
| 374 | - | log.Printf("Flushing pending history writes...") | 373 | + | logging.Info(fmt.Sprintf("Flushing pending history writes...")) |
| 375 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | 374 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| 376 | defer cancel() | 375 | defer cancel() | ||
| 377 | if err := app.browserService.FlushHistoryQueue(ctx); err != nil { | 376 | if err := app.browserService.FlushHistoryQueue(ctx); err != nil { | ||
| 378 | - | log.Printf("Warning: history queue flush incomplete: %v", err) | 377 | + | logging.Warn(fmt.Sprintf("Warning: history queue flush incomplete: %v", err)) |
| 379 | } else { | 378 | } else { | ||
| 380 | - | log.Printf("History queue flushed successfully") | 379 | + | logging.Info(fmt.Sprintf("History queue flushed successfully")) |
| 381 | } | 380 | } | ||
| 382 | } | 381 | } | ||
| 383 | } else { | 382 | } else { | ||
| 384 | - | log.Printf("Not entering GUI loop (non-CGO build)") | 383 | + | logging.Info(fmt.Sprintf("Not entering GUI loop (non-CGO build)")) |
| 385 | // In non-CGO mode, just wait for signals | 384 | // In non-CGO mode, just wait for signals | ||
| 386 | sigChan := make(chan os.Signal, 1) | 385 | sigChan := make(chan os.Signal, 1) | ||
| 387 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | 386 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||
| 388 | sig := <-sigChan | 387 | sig := <-sigChan | ||
| 389 | - | log.Printf("Received signal %v - exiting", sig) | 388 | + | logging.Info(fmt.Sprintf("Received signal %v - exiting", sig)) |
| 390 | } | 389 | } | ||
| 391 | } | 390 | } | ||
M internal/app/browser/focus_state_machine.go +40 -40
| @@ -4,11 +4,11 @@ package browser | |||||
| 4 | import ( | 4 | import ( | ||
| 5 | "container/heap" | 5 | "container/heap" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "runtime/debug" | 7 | "runtime/debug" | ||
| 9 | "sync" | 8 | "sync" | ||
| 10 | "time" | 9 | "time" | ||
| 11 | 10 | ||||
| 11 | + | "github.com/bnema/dumber/internal/logging" | |||
| 12 | "github.com/bnema/dumber/pkg/webkit" | 12 | "github.com/bnema/dumber/pkg/webkit" | ||
| 13 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 13 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 14 | ) | 14 | ) | ||
| @@ -274,10 +274,10 @@ func (fsm *FocusStateMachine) ConfigureDebug(focusDebug, _ bool, metricsEnabled | |||||
| 274 | fsm.metricsEnabled = metricsEnabled | 274 | fsm.metricsEnabled = metricsEnabled | ||
| 275 | 275 | ||||
| 276 | if focusDebug { | 276 | if focusDebug { | ||
| 277 | - | log.Printf("[FSM] Focus debug logging enabled") | 277 | + | logging.Info(fmt.Sprintf("[FSM] Focus debug logging enabled")) |
| 278 | } | 278 | } | ||
| 279 | if metricsEnabled { | 279 | if metricsEnabled { | ||
| 280 | - | log.Printf("[FSM] Focus metrics tracking enabled") | 280 | + | logging.Info(fmt.Sprintf("[FSM] Focus metrics tracking enabled")) |
| 281 | } | 281 | } | ||
| 282 | } | 282 | } | ||
| 283 | 283 | ||||
| @@ -286,27 +286,27 @@ func (fsm *FocusStateMachine) Initialize() error { | |||||
| 286 | fsm.mu.Lock() | 286 | fsm.mu.Lock() | ||
| 287 | defer fsm.mu.Unlock() | 287 | defer fsm.mu.Unlock() | ||
| 288 | 288 | ||||
| 289 | - | log.Printf("[FSM] Initializing focus state machine") | 289 | + | logging.Info(fmt.Sprintf("[FSM] Initializing focus state machine")) |
| 290 | fsm.currentState = StateInitializing | 290 | fsm.currentState = StateInitializing | ||
| 291 | 291 | ||||
| 292 | // Determine initial focus | 292 | // Determine initial focus | ||
| 293 | leaves := fsm.wm.collectLeaves() | 293 | leaves := fsm.wm.collectLeaves() | ||
| 294 | if len(leaves) == 0 { | 294 | if len(leaves) == 0 { | ||
| 295 | - | log.Printf("[FSM] No panes found during initialization") | 295 | + | logging.Debug(fmt.Sprintf("[FSM] No panes found during initialization")) |
| 296 | fsm.currentState = StateIdle | 296 | fsm.currentState = StateIdle | ||
| 297 | return nil | 297 | return nil | ||
| 298 | } | 298 | } | ||
| 299 | 299 | ||||
| 300 | initialPane := fsm.determineInitialFocus(leaves) | 300 | initialPane := fsm.determineInitialFocus(leaves) | ||
| 301 | if initialPane == nil { | 301 | if initialPane == nil { | ||
| 302 | - | log.Printf("[FSM] Could not determine initial focus target") | 302 | + | logging.Warn(fmt.Sprintf("[FSM] Could not determine initial focus target")) |
| 303 | fsm.currentState = StateIdle | 303 | fsm.currentState = StateIdle | ||
| 304 | return nil | 304 | return nil | ||
| 305 | } | 305 | } | ||
| 306 | 306 | ||||
| 307 | // Phase 3: Apply initial focus and CSS | 307 | // Phase 3: Apply initial focus and CSS | ||
| 308 | if err := fsm.applyInitialFocus(initialPane); err != nil { | 308 | if err := fsm.applyInitialFocus(initialPane); err != nil { | ||
| 309 | - | log.Printf("[FSM] Failed to apply initial focus: %v", err) | 309 | + | logging.Error(fmt.Sprintf("[FSM] Failed to apply initial focus: %v", err)) |
| 310 | fsm.currentState = StateIdle | 310 | fsm.currentState = StateIdle | ||
| 311 | return err | 311 | return err | ||
| 312 | } | 312 | } | ||
| @@ -324,7 +324,7 @@ func (fsm *FocusStateMachine) Initialize() error { | |||||
| 324 | // Phase 7: Transition to idle state | 324 | // Phase 7: Transition to idle state | ||
| 325 | fsm.currentState = StateIdle | 325 | fsm.currentState = StateIdle | ||
| 326 | 326 | ||||
| 327 | - | log.Printf("[FSM] Focus state machine initialized successfully with pane %p", initialPane) | 327 | + | logging.Info(fmt.Sprintf("[FSM] Focus state machine initialized successfully with pane %p", initialPane)) |
| 328 | return nil | 328 | return nil | ||
| 329 | } | 329 | } | ||
| 330 | 330 | ||||
| @@ -333,7 +333,7 @@ func (fsm *FocusStateMachine) Shutdown() { | |||||
| 333 | fsm.mu.Lock() | 333 | fsm.mu.Lock() | ||
| 334 | defer fsm.mu.Unlock() | 334 | defer fsm.mu.Unlock() | ||
| 335 | 335 | ||||
| 336 | - | log.Printf("[FSM] Shutting down focus state machine") | 336 | + | logging.Info(fmt.Sprintf("[FSM] Shutting down focus state machine")) |
| 337 | 337 | ||||
| 338 | // Signal shutdown | 338 | // Signal shutdown | ||
| 339 | close(fsm.shutdownChan) | 339 | close(fsm.shutdownChan) | ||
| @@ -346,7 +346,7 @@ func (fsm *FocusStateMachine) Shutdown() { | |||||
| 346 | fsm.settlingTimer.Stop() | 346 | fsm.settlingTimer.Stop() | ||
| 347 | } | 347 | } | ||
| 348 | 348 | ||||
| 349 | - | log.Printf("[FSM] Focus state machine shutdown complete") | 349 | + | logging.Info(fmt.Sprintf("[FSM] Focus state machine shutdown complete")) |
| 350 | } | 350 | } | ||
| 351 | 351 | ||||
| 352 | // RequestFocus queues a focus change request with priority and deduplication | 352 | // RequestFocus queues a focus change request with priority and deduplication | ||
| @@ -382,7 +382,7 @@ func (fsm *FocusStateMachine) RequestFocus(node *paneNode, source FocusSource) e | |||||
| 382 | fsm.mu.Unlock() | 382 | fsm.mu.Unlock() | ||
| 383 | } | 383 | } | ||
| 384 | if fsm.debugEnabled { | 384 | if fsm.debugEnabled { | ||
| 385 | - | log.Printf("[FSM] Duplicate request ignored: %s -> %p", request.Source, request.TargetNode) | 385 | + | logging.Debug(fmt.Sprintf("[FSM] Duplicate request ignored: %s -> %p", request.Source, request.TargetNode)) |
| 386 | } | 386 | } | ||
| 387 | return nil | 387 | return nil | ||
| 388 | } | 388 | } | ||
| @@ -395,7 +395,7 @@ func (fsm *FocusStateMachine) RequestFocus(node *paneNode, source FocusSource) e | |||||
| 395 | fsm.mu.Unlock() | 395 | fsm.mu.Unlock() | ||
| 396 | } | 396 | } | ||
| 397 | if fsm.debugEnabled { | 397 | if fsm.debugEnabled { | ||
| 398 | - | log.Printf("[FSM] Request coalesced: %s -> %p", request.Source, request.TargetNode) | 398 | + | logging.Debug(fmt.Sprintf("[FSM] Request coalesced: %s -> %p", request.Source, request.TargetNode)) |
| 399 | } | 399 | } | ||
| 400 | return nil | 400 | return nil | ||
| 401 | } | 401 | } | ||
| @@ -443,7 +443,7 @@ func (fsm *FocusStateMachine) InvalidateActivePane(node *paneNode) { | |||||
| 443 | return | 443 | return | ||
| 444 | } | 444 | } | ||
| 445 | 445 | ||||
| 446 | - | log.Printf("[FSM] Active pane invalidated: %p", node) | 446 | + | logging.Debug(fmt.Sprintf("[FSM] Active pane invalidated: %p", node)) |
| 447 | fsm.activePane = nil | 447 | fsm.activePane = nil | ||
| 448 | 448 | ||||
| 449 | if fsm.wm != nil && fsm.wm.app != nil { | 449 | if fsm.wm != nil && fsm.wm.app != nil { | ||
| @@ -541,7 +541,7 @@ func (fsm *FocusStateMachine) determineInitialFocus(leaves []*paneNode) *paneNod | |||||
| 541 | if leaf.pane != nil && leaf.pane.webView != nil { | 541 | if leaf.pane != nil && leaf.pane.webView != nil { | ||
| 542 | url := leaf.pane.webView.GetCurrentURL() | 542 | url := leaf.pane.webView.GetCurrentURL() | ||
| 543 | if url != "" && url != "about:blank" { | 543 | if url != "" && url != "about:blank" { | ||
| 544 | - | log.Printf("[FSM] Selected pane with content as initial focus: %s", url) | 544 | + | logging.Debug(fmt.Sprintf("[FSM] Selected pane with content as initial focus: %s", url)) |
| 545 | return leaf | 545 | return leaf | ||
| 546 | } | 546 | } | ||
| 547 | } | 547 | } | ||
| @@ -549,12 +549,12 @@ func (fsm *FocusStateMachine) determineInitialFocus(leaves []*paneNode) *paneNod | |||||
| 549 | 549 | ||||
| 550 | // Priority 2: Top-left pane (geometrically) | 550 | // Priority 2: Top-left pane (geometrically) | ||
| 551 | if topLeft := fsm.findTopLeftPane(leaves); topLeft != nil { | 551 | if topLeft := fsm.findTopLeftPane(leaves); topLeft != nil { | ||
| 552 | - | log.Printf("[FSM] Selected top-left pane as initial focus") | 552 | + | logging.Debug(fmt.Sprintf("[FSM] Selected top-left pane as initial focus")) |
| 553 | return topLeft | 553 | return topLeft | ||
| 554 | } | 554 | } | ||
| 555 | 555 | ||||
| 556 | // Priority 3: First pane in tree order | 556 | // Priority 3: First pane in tree order | ||
| 557 | - | log.Printf("[FSM] Selected first pane as initial focus") | 557 | + | logging.Debug(fmt.Sprintf("[FSM] Selected first pane as initial focus")) |
| 558 | return leaves[0] | 558 | return leaves[0] | ||
| 559 | } | 559 | } | ||
| 560 | 560 | ||||
| @@ -587,7 +587,7 @@ func (fsm *FocusStateMachine) applyInitialFocus(node *paneNode) error { | |||||
| 587 | return ErrInvalidTarget | 587 | return ErrInvalidTarget | ||
| 588 | } | 588 | } | ||
| 589 | 589 | ||||
| 590 | - | log.Printf("[FSM] Applying initial focus to pane %p", node) | 590 | + | logging.Debug(fmt.Sprintf("[FSM] Applying initial focus to pane %p", node)) |
| 591 | 591 | ||||
| 592 | // Apply GTK focus | 592 | // Apply GTK focus | ||
| 593 | if err := fsm.applyGTKFocus(node); err != nil { | 593 | if err := fsm.applyGTKFocus(node); err != nil { | ||
| @@ -646,7 +646,7 @@ func (fsm *FocusStateMachine) applyGTKFocus(node *paneNode) error { | |||||
| 646 | func (fsm *FocusStateMachine) processQueue() { | 646 | func (fsm *FocusStateMachine) processQueue() { | ||
| 647 | defer fsm.queueProcessor.Done() | 647 | defer fsm.queueProcessor.Done() | ||
| 648 | 648 | ||||
| 649 | - | log.Printf("[FSM] Focus request queue processor started") | 649 | + | logging.Debug(fmt.Sprintf("[FSM] Focus request queue processor started")) |
| 650 | 650 | ||||
| 651 | for { | 651 | for { | ||
| 652 | select { | 652 | select { | ||
| @@ -657,7 +657,7 @@ func (fsm *FocusStateMachine) processQueue() { | |||||
| 657 | // Legacy channel for backward compatibility | 657 | // Legacy channel for backward compatibility | ||
| 658 | fsm.handleFocusRequest(request) | 658 | fsm.handleFocusRequest(request) | ||
| 659 | case <-fsm.shutdownChan: | 659 | case <-fsm.shutdownChan: | ||
| 660 | - | log.Printf("[FSM] Focus request queue processor shutting down") | 660 | + | logging.Debug(fmt.Sprintf("[FSM] Focus request queue processor shutting down")) |
| 661 | return | 661 | return | ||
| 662 | } | 662 | } | ||
| 663 | } | 663 | } | ||
| @@ -696,7 +696,7 @@ func (fsm *FocusStateMachine) handleFocusRequest(request FocusRequest) { | |||||
| 696 | fsm.mu.Lock() | 696 | fsm.mu.Lock() | ||
| 697 | fsm.metrics.FailedRequests++ | 697 | fsm.metrics.FailedRequests++ | ||
| 698 | fsm.mu.Unlock() | 698 | fsm.mu.Unlock() | ||
| 699 | - | log.Printf("[FSM] Invalid focus request %s: %v", request.ID, err) | 699 | + | logging.Error(fmt.Sprintf("[FSM] Invalid focus request %s: %v", request.ID, err)) |
| 700 | return | 700 | return | ||
| 701 | } | 701 | } | ||
| 702 | 702 | ||||
| @@ -706,13 +706,13 @@ func (fsm *FocusStateMachine) handleFocusRequest(request FocusRequest) { | |||||
| 706 | fsm.mu.RUnlock() | 706 | fsm.mu.RUnlock() | ||
| 707 | 707 | ||||
| 708 | if currentActive == request.TargetNode { | 708 | if currentActive == request.TargetNode { | ||
| 709 | - | log.Printf("[FSM] Focus request %s: already focused on target", request.ID) | 709 | + | logging.Debug(fmt.Sprintf("[FSM] Focus request %s: already focused on target", request.ID)) |
| 710 | fsm.recordProcessTime(time.Since(startTime)) | 710 | fsm.recordProcessTime(time.Since(startTime)) | ||
| 711 | return | 711 | return | ||
| 712 | } | 712 | } | ||
| 713 | 713 | ||||
| 714 | - | log.Printf("[FSM] Processing focus request %s: %s -> %p", | 714 | + | logging.Debug(fmt.Sprintf("[FSM] Processing focus request %s: %s -> %p", |
| 715 | - | request.ID, request.Source, request.TargetNode) | 715 | + | request.ID, request.Source, request.TargetNode)) |
| 716 | 716 | ||||
| 717 | // GTK operations must run on main thread - always use IdleAdd for simplicity | 717 | // GTK operations must run on main thread - always use IdleAdd for simplicity | ||
| 718 | var err error | 718 | var err error | ||
| @@ -728,7 +728,7 @@ func (fsm *FocusStateMachine) handleFocusRequest(request FocusRequest) { | |||||
| 728 | fsm.mu.Lock() | 728 | fsm.mu.Lock() | ||
| 729 | fsm.metrics.FailedRequests++ | 729 | fsm.metrics.FailedRequests++ | ||
| 730 | fsm.mu.Unlock() | 730 | fsm.mu.Unlock() | ||
| 731 | - | log.Printf("[FSM] Focus request %s failed: %v", request.ID, err) | 731 | + | logging.Error(fmt.Sprintf("[FSM] Focus request %s failed: %v", request.ID, err)) |
| 732 | } else { | 732 | } else { | ||
| 733 | fsm.mu.Lock() | 733 | fsm.mu.Lock() | ||
| 734 | fsm.metrics.SuccessfulRequests++ | 734 | fsm.metrics.SuccessfulRequests++ | ||
| @@ -805,7 +805,7 @@ func (fsm *FocusStateMachine) executeFocusChange(request FocusRequest) error { | |||||
| 805 | // Start settling timer | 805 | // Start settling timer | ||
| 806 | fsm.startSettlingTimer() | 806 | fsm.startSettlingTimer() | ||
| 807 | 807 | ||||
| 808 | - | log.Printf("[FSM] Focus change completed: %p -> %p", oldPane, newPane) | 808 | + | logging.Debug(fmt.Sprintf("[FSM] Focus change completed: %p -> %p", oldPane, newPane)) |
| 809 | return nil | 809 | return nil | ||
| 810 | } | 810 | } | ||
| 811 | 811 | ||||
| @@ -866,14 +866,14 @@ func (fsm *FocusStateMachine) verifyCSSState() { | |||||
| 866 | } | 866 | } | ||
| 867 | 867 | ||||
| 868 | if fsm.reconcileCount > fsm.maxReconcileAttempts { | 868 | if fsm.reconcileCount > fsm.maxReconcileAttempts { | ||
| 869 | - | log.Printf("[FSM] WARNING: Reconciliation loop detected, skipping (attempt %d)", fsm.reconcileCount) | 869 | + | logging.Warn(fmt.Sprintf("[FSM] WARNING: Reconciliation loop detected, skipping (attempt %d)", fsm.reconcileCount)) |
| 870 | fsm.mu.Unlock() | 870 | fsm.mu.Unlock() | ||
| 871 | return | 871 | return | ||
| 872 | } | 872 | } | ||
| 873 | fsm.mu.Unlock() | 873 | fsm.mu.Unlock() | ||
| 874 | 874 | ||||
| 875 | - | log.Printf("[FSM] CSS inconsistencies found: %v", issues) | 875 | + | logging.Debug(fmt.Sprintf("[FSM] CSS inconsistencies found: %v", issues)) |
| 876 | - | log.Printf("[FSM] Triggering CSS reconciliation (attempt %d)", fsm.reconcileCount) | 876 | + | logging.Debug(fmt.Sprintf("[FSM] Triggering CSS reconciliation (attempt %d)", fsm.reconcileCount)) |
| 877 | 877 | ||||
| 878 | // Track reconciliation in metrics | 878 | // Track reconciliation in metrics | ||
| 879 | fsm.mu.Lock() | 879 | fsm.mu.Lock() | ||
| @@ -896,7 +896,7 @@ func (fsm *FocusStateMachine) shouldCoalesce(request FocusRequest) bool { | |||||
| 896 | for i := 0; i < fsm.priorityQueue.Len(); i++ { | 896 | for i := 0; i < fsm.priorityQueue.Len(); i++ { | ||
| 897 | pending := (*fsm.priorityQueue)[i] | 897 | pending := (*fsm.priorityQueue)[i] | ||
| 898 | if pending.TargetNode == request.TargetNode { | 898 | if pending.TargetNode == request.TargetNode { | ||
| 899 | - | log.Printf("[FSM] Coalescing request to same target: %p", request.TargetNode) | 899 | + | logging.Debug(fmt.Sprintf("[FSM] Coalescing request to same target: %p", request.TargetNode)) |
| 900 | return true | 900 | return true | ||
| 901 | } | 901 | } | ||
| 902 | } | 902 | } | ||
| @@ -924,7 +924,7 @@ func (fsm *FocusStateMachine) requeue(request FocusRequest) { | |||||
| 924 | defer fsm.mu.Unlock() | 924 | defer fsm.mu.Unlock() | ||
| 925 | 925 | ||||
| 926 | heap.Push(fsm.priorityQueue, &request) | 926 | heap.Push(fsm.priorityQueue, &request) | ||
| 927 | - | log.Printf("[FSM] Requeued request due to higher priority: %s", request.ID) | 927 | + | logging.Debug(fmt.Sprintf("[FSM] Requeued request due to higher priority: %s", request.ID)) |
| 928 | 928 | ||||
| 929 | // Signal that a request is pending | 929 | // Signal that a request is pending | ||
| 930 | select { | 930 | select { | ||
| @@ -940,7 +940,7 @@ func (fsm *FocusStateMachine) attachGTKController(node *paneNode) { | |||||
| 940 | } | 940 | } | ||
| 941 | 941 | ||||
| 942 | if node.focusControllerToken != 0 { | 942 | if node.focusControllerToken != 0 { | ||
| 943 | - | log.Printf("[FSM] Controller already attached to pane %p", node) | 943 | + | logging.Debug(fmt.Sprintf("[FSM] Controller already attached to pane %p", node)) |
| 944 | return | 944 | return | ||
| 945 | } | 945 | } | ||
| 946 | 946 | ||||
| @@ -965,7 +965,7 @@ func (fsm *FocusStateMachine) attachGTKController(node *paneNode) { | |||||
| 965 | } | 965 | } | ||
| 966 | node.lastFocusEnterTime = now | 966 | node.lastFocusEnterTime = now | ||
| 967 | 967 | ||||
| 968 | - | log.Printf("[FSM] GTK focus enter: %p", node) | 968 | + | logging.Debug(fmt.Sprintf("[FSM] GTK focus enter: %p", node)) |
| 969 | // Don't automatically change focus on GTK enter - let user interactions drive this | 969 | // Don't automatically change focus on GTK enter - let user interactions drive this | ||
| 970 | // This prevents infinite loops with our own focus changes | 970 | // This prevents infinite loops with our own focus changes | ||
| 971 | }) | 971 | }) | ||
| @@ -978,7 +978,7 @@ func (fsm *FocusStateMachine) attachGTKController(node *paneNode) { | |||||
| 978 | } | 978 | } | ||
| 979 | node.lastFocusLeaveTime = now | 979 | node.lastFocusLeaveTime = now | ||
| 980 | 980 | ||||
| 981 | - | log.Printf("[FSM] GTK focus leave: %p", node) | 981 | + | logging.Debug(fmt.Sprintf("[FSM] GTK focus leave: %p", node)) |
| 982 | // Similarly, don't react to GTK leave events automatically | 982 | // Similarly, don't react to GTK leave events automatically | ||
| 983 | }) | 983 | }) | ||
| 984 | 984 | ||||
| @@ -988,7 +988,7 @@ func (fsm *FocusStateMachine) attachGTKController(node *paneNode) { | |||||
| 988 | // Store controller pointer as token for later removal | 988 | // Store controller pointer as token for later removal | ||
| 989 | node.focusControllerToken = uintptr(controller.Native()) | 989 | node.focusControllerToken = uintptr(controller.Native()) | ||
| 990 | if node.focusControllerToken != 0 { | 990 | if node.focusControllerToken != 0 { | ||
| 991 | - | log.Printf("[FSM] Attached GTK focus controller to pane %p with token %d", node, node.focusControllerToken) | 991 | + | logging.Debug(fmt.Sprintf("[FSM] Attached GTK focus controller to pane %p with token %d", node, node.focusControllerToken)) |
| 992 | } | 992 | } | ||
| 993 | } | 993 | } | ||
| 994 | 994 | ||||
| @@ -999,7 +999,7 @@ func (fsm *FocusStateMachine) detachGTKController(node *paneNode, token uintptr) | |||||
| 999 | } | 999 | } | ||
| 1000 | 1000 | ||||
| 1001 | if node.focusControllerToken != token { | 1001 | if node.focusControllerToken != token { | ||
| 1002 | - | log.Printf("[FSM] Token mismatch, skipping detach for pane %p", node) | 1002 | + | logging.Debug(fmt.Sprintf("[FSM] Token mismatch, skipping detach for pane %p", node)) |
| 1003 | return | 1003 | return | ||
| 1004 | } | 1004 | } | ||
| 1005 | 1005 | ||||
| @@ -1007,7 +1007,7 @@ func (fsm *FocusStateMachine) detachGTKController(node *paneNode, token uintptr) | |||||
| 1007 | 1007 | ||||
| 1008 | // Note: In GTK4, controllers are automatically removed when widget is destroyed | 1008 | // Note: In GTK4, controllers are automatically removed when widget is destroyed | ||
| 1009 | // We just need to clear our token reference | 1009 | // We just need to clear our token reference | ||
| 1010 | - | log.Printf("[FSM] Detached GTK focus controller from pane %p", node) | 1010 | + | logging.Debug(fmt.Sprintf("[FSM] Detached GTK focus controller from pane %p", node)) |
| 1011 | } | 1011 | } | ||
| 1012 | 1012 | ||||
| 1013 | // attachGTKControllersToAllPanes attaches focus controllers to all existing panes | 1013 | // attachGTKControllersToAllPanes attaches focus controllers to all existing panes | ||
| @@ -1021,7 +1021,7 @@ func (fsm *FocusStateMachine) attachGTKControllersToAllPanes() { | |||||
| 1021 | fsm.attachGTKController(leaf) | 1021 | fsm.attachGTKController(leaf) | ||
| 1022 | } | 1022 | } | ||
| 1023 | 1023 | ||||
| 1024 | - | log.Printf("[FSM] Attached GTK focus controllers to %d panes", len(leaves)) | 1024 | + | logging.Debug(fmt.Sprintf("[FSM] Attached GTK focus controllers to %d panes", len(leaves))) |
| 1025 | } | 1025 | } | ||
| 1026 | 1026 | ||||
| 1027 | // FocusDebugInfo contains comprehensive debug information about the focus system | 1027 | // FocusDebugInfo contains comprehensive debug information about the focus system | ||
| @@ -1070,7 +1070,7 @@ func (fsm *FocusStateMachine) DumpState() FocusDebugInfo { | |||||
| 1070 | 1070 | ||||
| 1071 | // ForceValidation manually triggers CSS consistency check and validation | 1071 | // ForceValidation manually triggers CSS consistency check and validation | ||
| 1072 | func (fsm *FocusStateMachine) ForceValidation() []string { | 1072 | func (fsm *FocusStateMachine) ForceValidation() []string { | ||
| 1073 | - | log.Printf("[FSM] Force validation requested") | 1073 | + | logging.Debug(fmt.Sprintf("[FSM] Force validation requested")) |
| 1074 | 1074 | ||||
| 1075 | // Trigger CSS verification | 1075 | // Trigger CSS verification | ||
| 1076 | fsm.verifyCSSState() | 1076 | fsm.verifyCSSState() | ||
| @@ -1096,7 +1096,7 @@ func (fsm *FocusStateMachine) ForceValidation() []string { | |||||
| 1096 | } | 1096 | } | ||
| 1097 | } | 1097 | } | ||
| 1098 | 1098 | ||||
| 1099 | - | log.Printf("[FSM] Force validation completed: %d issues found", len(issues)) | 1099 | + | logging.Debug(fmt.Sprintf("[FSM] Force validation completed: %d issues found", len(issues))) |
| 1100 | return issues | 1100 | return issues | ||
| 1101 | } | 1101 | } | ||
| 1102 | 1102 | ||||
| @@ -1138,7 +1138,7 @@ func (fsm *FocusStateMachine) ClearHistory() { | |||||
| 1138 | defer fsm.mu.Unlock() | 1138 | defer fsm.mu.Unlock() | ||
| 1139 | 1139 | ||||
| 1140 | fsm.historyRing = NewRingBuffer[FocusTransition](50) | 1140 | fsm.historyRing = NewRingBuffer[FocusTransition](50) | ||
| 1141 | - | log.Printf("[FSM] Focus transition history cleared") | 1141 | + | logging.Debug(fmt.Sprintf("[FSM] Focus transition history cleared")) |
| 1142 | } | 1142 | } | ||
| 1143 | 1143 | ||||
| 1144 | // GetMetrics returns performance metrics for the focus system | 1144 | // GetMetrics returns performance metrics for the focus system | ||
M internal/app/browser/pane.go +6 -5
| @@ -1,11 +1,12 @@ | |||||
| 1 | package browser | 1 | package browser | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "time" | 5 | "time" | ||
| 6 | 6 | ||||
| 7 | "github.com/bnema/dumber/internal/app/control" | 7 | "github.com/bnema/dumber/internal/app/control" | ||
| 8 | "github.com/bnema/dumber/internal/app/messaging" | 8 | "github.com/bnema/dumber/internal/app/messaging" | ||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 9 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| @@ -85,7 +86,7 @@ func (p *BrowserPane) Cleanup() { | |||||
| 85 | 86 | ||||
| 86 | if p.shortcutHandler != nil { | 87 | if p.shortcutHandler != nil { | ||
| 87 | p.shortcutHandler.Detach() | 88 | p.shortcutHandler.Detach() | ||
| 88 | - | log.Printf("[pane-%s] Cleaned up shortcuts", p.id) | 89 | + | logging.Debug(fmt.Sprintf("[pane-%s] Cleaned up shortcuts", p.id)) |
| 89 | } | 90 | } | ||
| 90 | 91 | ||||
| 91 | if p.navigationController != nil { | 92 | if p.navigationController != nil { | ||
| @@ -107,9 +108,9 @@ func (p *BrowserPane) Cleanup() { | |||||
| 107 | // Destroy WebView to unregister from global registry and release resources | 108 | // Destroy WebView to unregister from global registry and release resources | ||
| 108 | if p.webView != nil && !p.webView.IsDestroyed() { | 109 | if p.webView != nil && !p.webView.IsDestroyed() { | ||
| 109 | if err := p.webView.Destroy(); err != nil { | 110 | if err := p.webView.Destroy(); err != nil { | ||
| 110 | - | log.Printf("[pane-%s] failed to destroy webview: %v", p.id, err) | 111 | + | logging.Error(fmt.Sprintf("[pane-%s] failed to destroy webview: %v", p.id, err)) |
| 111 | } else { | 112 | } else { | ||
| 112 | - | log.Printf("[pane-%s] successfully destroyed webview", p.id) | 113 | + | logging.Debug(fmt.Sprintf("[pane-%s] successfully destroyed webview", p.id)) |
| 113 | } | 114 | } | ||
| 114 | } | 115 | } | ||
| 115 | 116 | ||||
| @@ -132,6 +133,6 @@ func (p *BrowserPane) CleanupFromWorkspace(wm interface{}) { | |||||
| 132 | }); ok { | 133 | }); ok { | ||
| 133 | workspaceManager.removeFromMaps(p.webView) | 134 | workspaceManager.removeFromMaps(p.webView) | ||
| 134 | workspaceManager.removeFromAppPanes(p) | 135 | workspaceManager.removeFromAppPanes(p) | ||
| 135 | - | log.Printf("[pane-%s] Cleaned up from workspace tracking", p.id) | 136 | + | logging.Debug(fmt.Sprintf("[pane-%s] Cleaned up from workspace tracking", p.id)) |
| 136 | } | 137 | } | ||
| 137 | } | 138 | } | ||
M internal/app/browser/shortcuts.go +6 -6
| @@ -3,10 +3,10 @@ package browser | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | 6 | ||||
| 8 | "github.com/bnema/dumber/internal/app/control" | 7 | "github.com/bnema/dumber/internal/app/control" | ||
| 9 | "github.com/bnema/dumber/internal/config" | 8 | "github.com/bnema/dumber/internal/config" | ||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| @@ -71,7 +71,7 @@ func (s *ShortcutHandler) RegisterShortcuts() { | |||||
| 71 | if !s.isActivePane() { | 71 | if !s.isActivePane() { | ||
| 72 | return | 72 | return | ||
| 73 | } | 73 | } | ||
| 74 | - | log.Printf("Shortcut: Reload page") | 74 | + | logging.Debug("Shortcut: Reload page") |
| 75 | _ = s.webView.Reload() | 75 | _ = s.webView.Reload() | ||
| 76 | }) | 76 | }) | ||
| 77 | 77 | ||||
| @@ -79,7 +79,7 @@ func (s *ShortcutHandler) RegisterShortcuts() { | |||||
| 79 | if !s.isActivePane() { | 79 | if !s.isActivePane() { | ||
| 80 | return | 80 | return | ||
| 81 | } | 81 | } | ||
| 82 | - | log.Printf("Shortcut: Hard reload page") | 82 | + | logging.Debug("Shortcut: Hard reload page") |
| 83 | _ = s.webView.ReloadBypassCache() | 83 | _ = s.webView.ReloadBypassCache() | ||
| 84 | }) | 84 | }) | ||
| 85 | 85 | ||||
| @@ -87,7 +87,7 @@ func (s *ShortcutHandler) RegisterShortcuts() { | |||||
| 87 | if !s.isActivePane() { | 87 | if !s.isActivePane() { | ||
| 88 | return | 88 | return | ||
| 89 | } | 89 | } | ||
| 90 | - | log.Printf("Shortcut: F5 reload") | 90 | + | logging.Debug("Shortcut: F5 reload") |
| 91 | _ = s.webView.Reload() | 91 | _ = s.webView.Reload() | ||
| 92 | }) | 92 | }) | ||
| 93 | 93 | ||||
| @@ -117,7 +117,7 @@ func (s *ShortcutHandler) exposeWorkspaceConfig() { | |||||
| 117 | 117 | ||||
| 118 | payload, err := json.Marshal(s.config.Workspace) | 118 | payload, err := json.Marshal(s.config.Workspace) | ||
| 119 | if err != nil { | 119 | if err != nil { | ||
| 120 | - | log.Printf("Shortcut: failed to marshal workspace config: %v", err) | 120 | + | logging.Error(fmt.Sprintf("Shortcut: failed to marshal workspace config: %v", err)) |
| 121 | return | 121 | return | ||
| 122 | } | 122 | } | ||
| 123 | 123 | ||||
| @@ -128,7 +128,7 @@ func (s *ShortcutHandler) exposeWorkspaceConfig() { | |||||
| 128 | })();`, string(payload)) | 128 | })();`, string(payload)) | ||
| 129 | 129 | ||||
| 130 | if err := s.webView.InjectScript(script); err != nil { | 130 | if err := s.webView.InjectScript(script); err != nil { | ||
| 131 | - | log.Printf("Shortcut: failed to expose workspace config: %v", err) | 131 | + | logging.Error(fmt.Sprintf("Shortcut: failed to expose workspace config: %v", err)) |
| 132 | } | 132 | } | ||
| 133 | } | 133 | } | ||
| 134 | 134 | ||||
M internal/app/browser/stacked_panes.go +34 -35
| @@ -3,14 +3,13 @@ package browser | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "errors" | 4 | "errors" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "sync/atomic" | 6 | "sync/atomic" | ||
| 8 | "time" | 7 | "time" | ||
| 9 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | + | "github.com/bnema/dumber/pkg/webkit" | |||
| 10 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 11 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 11 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 12 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 12 | - | ||||
| 13 | - | "github.com/bnema/dumber/pkg/webkit" | |||
| 14 | ) | 13 | ) | ||
| 15 | 14 | ||||
| 16 | const ( | 15 | const ( | ||
| @@ -139,8 +138,8 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 139 | 138 | ||||
| 140 | // Insert the new pane right after the current pane | 139 | // Insert the new pane right after the current pane | ||
| 141 | insertIndex := currentIndex + 1 | 140 | insertIndex := currentIndex + 1 | ||
| 142 | - | log.Printf("[workspace] adding to existing stack: currentIndex=%d insertIndex=%d stackSize=%d", | 141 | + | logging.Debug(fmt.Sprintf("[workspace] adding to existing stack: currentIndex=%d insertIndex=%d stackSize=%d", |
| 143 | - | currentIndex, insertIndex, len(stackNode.stackedPanes)) | 142 | + | currentIndex, insertIndex, len(stackNode.stackedPanes))) |
| 144 | 143 | ||||
| 145 | // Set parent relationship | 144 | // Set parent relationship | ||
| 146 | newLeaf.parent = stackNode | 145 | newLeaf.parent = stackNode | ||
| @@ -179,7 +178,7 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 179 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil && newLeaf.titleBar != nil { | 178 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil && newLeaf.titleBar != nil { | ||
| 180 | box.InsertChildAfter(newLeaf.container, newLeaf.titleBar) | 179 | box.InsertChildAfter(newLeaf.container, newLeaf.titleBar) | ||
| 181 | } | 180 | } | ||
| 182 | - | log.Printf("[workspace] inserted widgets at position %d (after widget %p)", insertIndex, insertAfterWidget) | 181 | + | logging.Debug(fmt.Sprintf("[workspace] inserted widgets at position %d (after widget %p)", insertIndex, insertAfterWidget)) |
| 183 | } else { | 182 | } else { | ||
| 184 | // Insert at the beginning (insertIndex = 0) | 183 | // Insert at the beginning (insertIndex = 0) | ||
| 185 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { | 184 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.container != nil { | ||
| @@ -188,7 +187,7 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 188 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { | 187 | if box, ok := stackNode.stackWrapper.(*gtk.Box); ok && newLeaf.titleBar != nil { | ||
| 189 | box.Prepend(newLeaf.titleBar) | 188 | box.Prepend(newLeaf.titleBar) | ||
| 190 | } | 189 | } | ||
| 191 | - | log.Printf("[workspace] prepended widgets at position 0") | 190 | + | logging.Debug("[workspace] prepended widgets at position 0") |
| 192 | } | 191 | } | ||
| 193 | 192 | ||||
| 194 | return stackNode, insertIndex, nil | 193 | return stackNode, insertIndex, nil | ||
| @@ -196,7 +195,7 @@ func (spm *StackedPaneManager) addPaneToExistingStack(target, newLeaf *paneNode) | |||||
| 196 | 195 | ||||
| 197 | // convertToStackedContainer converts a simple pane to a stacked container | 196 | // convertToStackedContainer converts a simple pane to a stacked container | ||
| 198 | func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNode) (*paneNode, int, error) { | 197 | func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNode) (*paneNode, int, error) { | ||
| 199 | - | log.Printf("[workspace] converting pane to stacked: %p", target) | 198 | + | logging.Debug(fmt.Sprintf("[workspace] converting pane to stacked: %p", target)) |
| 200 | 199 | ||||
| 201 | // Create the wrapper container - this is what will be used by splitNode | 200 | // Create the wrapper container - this is what will be used by splitNode | ||
| 202 | stackWrapperContainer := gtk.NewBox(gtk.OrientationVertical, 0) | 201 | stackWrapperContainer := gtk.NewBox(gtk.OrientationVertical, 0) | ||
| @@ -292,11 +291,11 @@ func (spm *StackedPaneManager) convertToStackedContainer(target, newLeaf *paneNo | |||||
| 292 | if newLeaf.container != nil { | 291 | if newLeaf.container != nil { | ||
| 293 | parentWidget := webkit.WidgetGetParent(newLeaf.container) | 292 | parentWidget := webkit.WidgetGetParent(newLeaf.container) | ||
| 294 | if parentWidget != nil { | 293 | if parentWidget != nil { | ||
| 295 | - | log.Printf("[workspace] unparenting new pane container %p from parent %p before stack append", newLeaf.container, parentWidget) | 294 | + | logging.Debug(fmt.Sprintf("[workspace] unparenting new pane container %p from parent %p before stack append", newLeaf.container, parentWidget)) |
| 296 | webkit.WidgetUnparent(newLeaf.container) | 295 | webkit.WidgetUnparent(newLeaf.container) | ||
| 297 | // Verify unparent succeeded | 296 | // Verify unparent succeeded | ||
| 298 | if finalParent := webkit.WidgetGetParent(newLeaf.container); finalParent != nil { | 297 | if finalParent := webkit.WidgetGetParent(newLeaf.container); finalParent != nil { | ||
| 299 | - | log.Printf("[workspace] WARNING: container %p still has parent %p after unparent", newLeaf.container, finalParent) | 298 | + | logging.Warn(fmt.Sprintf("[workspace] container %p still has parent %p after unparent", newLeaf.container, finalParent)) |
| 300 | } | 299 | } | ||
| 301 | } | 300 | } | ||
| 302 | } | 301 | } | ||
| @@ -369,7 +368,7 @@ func (spm *StackedPaneManager) finalizeStackCreation(stackNode, newLeaf *paneNod | |||||
| 369 | currentTitle := currentlyActivePane.pane.webView.GetTitle() | 368 | currentTitle := currentlyActivePane.pane.webView.GetTitle() | ||
| 370 | if currentTitle != "" { | 369 | if currentTitle != "" { | ||
| 371 | spm.updateTitleBarLabel(currentlyActivePane, currentTitle) | 370 | spm.updateTitleBarLabel(currentlyActivePane, currentTitle) | ||
| 372 | - | log.Printf("[workspace] updated title bar for pane becoming inactive during stack creation: %s", currentTitle) | 371 | + | logging.Debug(fmt.Sprintf("[workspace] updated title bar for pane becoming inactive during stack creation: %s", currentTitle)) |
| 373 | } | 372 | } | ||
| 374 | } | 373 | } | ||
| 375 | } | 374 | } | ||
| @@ -381,38 +380,38 @@ func (spm *StackedPaneManager) finalizeStackCreation(stackNode, newLeaf *paneNod | |||||
| 381 | // Set focus on the new pane synchronously | 380 | // Set focus on the new pane synchronously | ||
| 382 | spm.wm.SetActivePane(newLeaf, SourceSplit) | 381 | spm.wm.SetActivePane(newLeaf, SourceSplit) | ||
| 383 | 382 | ||||
| 384 | - | log.Printf("[workspace] stacked new pane: stackNode=%p newLeaf=%p stackSize=%d activeIndex=%d insertIndex=%d", | 383 | + | logging.Debug(fmt.Sprintf("[workspace] stacked new pane: stackNode=%p newLeaf=%p stackSize=%d activeIndex=%d insertIndex=%d", |
| 385 | - | stackNode, newLeaf, len(stackNode.stackedPanes), stackNode.activeStackIndex, insertIndex) | 384 | + | stackNode, newLeaf, len(stackNode.stackedPanes), stackNode.activeStackIndex, insertIndex)) |
| 386 | return newLeaf, nil | 385 | return newLeaf, nil | ||
| 387 | } | 386 | } | ||
| 388 | 387 | ||||
| 389 | // UpdateStackVisibility updates the visibility of panes in a stack | 388 | // UpdateStackVisibility updates the visibility of panes in a stack | ||
| 390 | func (spm *StackedPaneManager) UpdateStackVisibility(stackNode *paneNode) { | 389 | func (spm *StackedPaneManager) UpdateStackVisibility(stackNode *paneNode) { | ||
| 391 | - | log.Printf("[workspace] updateStackVisibility called: stackNode=%p", stackNode) | 390 | + | logging.Debug(fmt.Sprintf("[workspace] updateStackVisibility called: stackNode=%p", stackNode)) |
| 392 | 391 | ||||
| 393 | if stackNode == nil { | 392 | if stackNode == nil { | ||
| 394 | - | log.Printf("[workspace] updateStackVisibility aborted: stackNode is nil") | 393 | + | logging.Debug("[workspace] updateStackVisibility aborted: stackNode is nil") |
| 395 | return | 394 | return | ||
| 396 | } | 395 | } | ||
| 397 | 396 | ||||
| 398 | if !stackNode.isStacked { | 397 | if !stackNode.isStacked { | ||
| 399 | - | log.Printf("[workspace] updateStackVisibility aborted: stackNode.isStacked=%v", stackNode.isStacked) | 398 | + | logging.Debug(fmt.Sprintf("[workspace] updateStackVisibility aborted: stackNode.isStacked=%v", stackNode.isStacked)) |
| 400 | return | 399 | return | ||
| 401 | } | 400 | } | ||
| 402 | 401 | ||||
| 403 | if len(stackNode.stackedPanes) == 0 { | 402 | if len(stackNode.stackedPanes) == 0 { | ||
| 404 | - | log.Printf("[workspace] updateStackVisibility aborted: stackedPanes empty, len=%d", len(stackNode.stackedPanes)) | 403 | + | logging.Debug(fmt.Sprintf("[workspace] updateStackVisibility aborted: stackedPanes empty, len=%d", len(stackNode.stackedPanes))) |
| 405 | return | 404 | return | ||
| 406 | } | 405 | } | ||
| 407 | 406 | ||||
| 408 | activeIndex := stackNode.activeStackIndex | 407 | activeIndex := stackNode.activeStackIndex | ||
| 409 | if activeIndex < 0 || activeIndex >= len(stackNode.stackedPanes) { | 408 | if activeIndex < 0 || activeIndex >= len(stackNode.stackedPanes) { | ||
| 410 | - | log.Printf("[workspace] updateStackVisibility: correcting activeIndex from %d to 0", activeIndex) | 409 | + | logging.Debug(fmt.Sprintf("[workspace] updateStackVisibility: correcting activeIndex from %d to 0", activeIndex)) |
| 411 | activeIndex = 0 | 410 | activeIndex = 0 | ||
| 412 | stackNode.activeStackIndex = activeIndex | 411 | stackNode.activeStackIndex = activeIndex | ||
| 413 | } | 412 | } | ||
| 414 | 413 | ||||
| 415 | - | log.Printf("[workspace] updating stack visibility: activeIndex=%d stackSize=%d", activeIndex, len(stackNode.stackedPanes)) | 414 | + | logging.Debug(fmt.Sprintf("[workspace] updating stack visibility: activeIndex=%d stackSize=%d", activeIndex, len(stackNode.stackedPanes))) |
| 416 | 415 | ||||
| 417 | // CRITICAL: Process ALL panes in a single pass to prevent flickering | 416 | // CRITICAL: Process ALL panes in a single pass to prevent flickering | ||
| 418 | for i, pane := range stackNode.stackedPanes { | 417 | for i, pane := range stackNode.stackedPanes { | ||
| @@ -424,7 +423,7 @@ func (spm *StackedPaneManager) UpdateStackVisibility(stackNode *paneNode) { | |||||
| 424 | if pane.titleBar != nil { | 423 | if pane.titleBar != nil { | ||
| 425 | webkit.WidgetSetVisible(pane.titleBar, false) // ABSOLUTE RULE: never visible for active pane | 424 | webkit.WidgetSetVisible(pane.titleBar, false) // ABSOLUTE RULE: never visible for active pane | ||
| 426 | } | 425 | } | ||
| 427 | - | log.Printf("[workspace] active pane %d: container=visible, titleBar=HIDDEN", i) | 426 | + | logging.Debug(fmt.Sprintf("[workspace] active pane %d: container=visible, titleBar=HIDDEN", i)) |
| 428 | } else { | 427 | } else { | ||
| 429 | // Inactive panes: hide container, show title bar | 428 | // Inactive panes: hide container, show title bar | ||
| 430 | if pane.container != nil { | 429 | if pane.container != nil { | ||
| @@ -474,7 +473,7 @@ func (spm *StackedPaneManager) NavigateStack(direction string) bool { | |||||
| 474 | // This is more reliable than trying to match panes | 473 | // This is more reliable than trying to match panes | ||
| 475 | currentIndex := stackNode.activeStackIndex | 474 | currentIndex := stackNode.activeStackIndex | ||
| 476 | if currentIndex < 0 || currentIndex >= len(stackNode.stackedPanes) { | 475 | if currentIndex < 0 || currentIndex >= len(stackNode.stackedPanes) { | ||
| 477 | - | log.Printf("[workspace] navigateStack: invalid activeStackIndex=%d, resetting to 0", currentIndex) | 476 | + | logging.Debug(fmt.Sprintf("[workspace] navigateStack: invalid activeStackIndex=%d, resetting to 0", currentIndex)) |
| 478 | currentIndex = 0 | 477 | currentIndex = 0 | ||
| 479 | stackNode.activeStackIndex = 0 | 478 | stackNode.activeStackIndex = 0 | ||
| 480 | } | 479 | } | ||
| @@ -509,7 +508,7 @@ func (spm *StackedPaneManager) NavigateStack(direction string) bool { | |||||
| 509 | currentTitle := currentActivePane.pane.webView.GetTitle() | 508 | currentTitle := currentActivePane.pane.webView.GetTitle() | ||
| 510 | if currentTitle != "" { | 509 | if currentTitle != "" { | ||
| 511 | spm.updateTitleBarLabel(currentActivePane, currentTitle) | 510 | spm.updateTitleBarLabel(currentActivePane, currentTitle) | ||
| 512 | - | log.Printf("[workspace] updated title bar for pane transitioning to INACTIVE: %s", currentTitle) | 511 | + | logging.Debug(fmt.Sprintf("[workspace] updated title bar for pane transitioning to INACTIVE: %s", currentTitle)) |
| 513 | } | 512 | } | ||
| 514 | } | 513 | } | ||
| 515 | 514 | ||||
| @@ -521,8 +520,8 @@ func (spm *StackedPaneManager) NavigateStack(direction string) bool { | |||||
| 521 | newActivePane := stackNode.stackedPanes[newIndex] | 520 | newActivePane := stackNode.stackedPanes[newIndex] | ||
| 522 | spm.wm.SetActivePane(newActivePane, SourceStackNav) | 521 | spm.wm.SetActivePane(newActivePane, SourceStackNav) | ||
| 523 | 522 | ||||
| 524 | - | log.Printf("[workspace] navigated stack: direction=%s from=%d to=%d stackSize=%d", | 523 | + | logging.Debug(fmt.Sprintf("[workspace] navigated stack: direction=%s from=%d to=%d stackSize=%d", |
| 525 | - | direction, currentIndex, newIndex, len(stackNode.stackedPanes)) | 524 | + | direction, currentIndex, newIndex, len(stackNode.stackedPanes))) |
| 526 | return true | 525 | return true | ||
| 527 | } | 526 | } | ||
| 528 | 527 | ||||
| @@ -586,10 +585,10 @@ func (spm *StackedPaneManager) UpdateTitleBar(webView *webkit.WebView, title str | |||||
| 586 | if paneIndex != -1 && paneIndex != activeIndex { | 585 | if paneIndex != -1 && paneIndex != activeIndex { | ||
| 587 | // This is an INACTIVE pane - safe to update title bar | 586 | // This is an INACTIVE pane - safe to update title bar | ||
| 588 | spm.updateTitleBarLabel(node, title) | 587 | spm.updateTitleBarLabel(node, title) | ||
| 589 | - | log.Printf("[workspace] updated title bar for INACTIVE WebView %d: %s", webView.ID(), title) | 588 | + | logging.Debug(fmt.Sprintf("[workspace] updated title bar for INACTIVE WebView %d: %s", webView.ID(), title)) |
| 590 | } else { | 589 | } else { | ||
| 591 | // This is the ACTIVE pane - title bar should remain hidden | 590 | // This is the ACTIVE pane - title bar should remain hidden | ||
| 592 | - | log.Printf("[workspace] skipped title bar update for ACTIVE WebView %d: %s", webView.ID(), title) | 591 | + | logging.Debug(fmt.Sprintf("[workspace] skipped title bar update for ACTIVE WebView %d: %s", webView.ID(), title)) |
| 593 | } | 592 | } | ||
| 594 | } | 593 | } | ||
| 595 | } | 594 | } | ||
| @@ -609,7 +608,7 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||||
| 609 | // Create new title bar with updated title and favicon | 608 | // Create new title bar with updated title and favicon | ||
| 610 | newTitleBar := spm.createTitleBarWithTitle(title, pageURL) | 609 | newTitleBar := spm.createTitleBarWithTitle(title, pageURL) | ||
| 611 | if newTitleBar == nil { | 610 | if newTitleBar == nil { | ||
| 612 | - | log.Printf("[workspace] failed to create new title bar") | 611 | + | logging.Error("[workspace] failed to create new title bar") |
| 613 | return | 612 | return | ||
| 614 | } | 613 | } | ||
| 615 | 614 | ||||
| @@ -655,7 +654,7 @@ func (spm *StackedPaneManager) updateTitleBarLabel(node *paneNode, title string) | |||||
| 655 | } | 654 | } | ||
| 656 | } | 655 | } | ||
| 657 | 656 | ||||
| 658 | - | log.Printf("[workspace] replaced title bar for pane %d: %p", paneIndex, node) | 657 | + | logging.Debug(fmt.Sprintf("[workspace] replaced title bar for pane %d: %p", paneIndex, node)) |
| 659 | } | 658 | } | ||
| 660 | } | 659 | } | ||
| 661 | 660 | ||||
| @@ -680,7 +679,7 @@ func (spm *StackedPaneManager) createTitleBarWithTitle(title string, pageURL str | |||||
| 680 | if pageURL != "" && spm.wm != nil && spm.wm.app != nil && spm.wm.app.faviconService != nil { | 679 | if pageURL != "" && spm.wm != nil && spm.wm.app != nil && spm.wm.app.faviconService != nil { | ||
| 681 | spm.wm.app.faviconService.GetFaviconTexture(pageURL, func(texture *gdk.Texture, err error) { | 680 | spm.wm.app.faviconService.GetFaviconTexture(pageURL, func(texture *gdk.Texture, err error) { | ||
| 682 | if err != nil { | 681 | if err != nil { | ||
| 683 | - | log.Printf("[favicon] Failed to load favicon for title bar: %s - %v", pageURL, err) | 682 | + | logging.Warn(fmt.Sprintf("[favicon] Failed to load favicon for title bar: %s - %v", pageURL, err)) |
| 684 | return | 683 | return | ||
| 685 | } | 684 | } | ||
| 686 | 685 | ||||
| @@ -753,7 +752,7 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 753 | return errors.New("node not found in stack") | 752 | return errors.New("node not found in stack") | ||
| 754 | } | 753 | } | ||
| 755 | 754 | ||||
| 756 | - | log.Printf("[workspace] closing stacked pane: index=%d stackSize=%d", nodeIndex, len(stackNode.stackedPanes)) | 755 | + | logging.Debug(fmt.Sprintf("[workspace] closing stacked pane: index=%d stackSize=%d", nodeIndex, len(stackNode.stackedPanes))) |
| 757 | 756 | ||||
| 758 | // Detach hover/focus controllers before tearing down widgets to avoid GTK | 757 | // Detach hover/focus controllers before tearing down widgets to avoid GTK | ||
| 759 | // callbacks referencing freed memory during destruction. | 758 | // callbacks referencing freed memory during destruction. | ||
| @@ -901,7 +900,7 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 901 | stackNode.stackWrapper = nil | 900 | stackNode.stackWrapper = nil | ||
| 902 | stackNode.stackedPanes = nil | 901 | stackNode.stackedPanes = nil | ||
| 903 | 902 | ||||
| 904 | - | log.Printf("[workspace] converted stack back to regular pane") | 903 | + | logging.Debug("[workspace] converted stack back to regular pane") |
| 905 | return nil | 904 | return nil | ||
| 906 | 905 | ||||
| 907 | default: | 906 | default: | ||
| @@ -922,8 +921,8 @@ func (spm *StackedPaneManager) CloseStackedPane(node *paneNode) error { | |||||
| 922 | generation := spm.wm.nextCleanupGeneration() | 921 | generation := spm.wm.nextCleanupGeneration() | ||
| 923 | spm.wm.cleanupPane(node, generation) | 922 | spm.wm.cleanupPane(node, generation) | ||
| 924 | 923 | ||||
| 925 | - | log.Printf("[workspace] closed pane from stack: remaining=%d activeIndex=%d", | 924 | + | logging.Debug(fmt.Sprintf("[workspace] closed pane from stack: remaining=%d activeIndex=%d", |
| 926 | - | len(stackNode.stackedPanes), stackNode.activeStackIndex) | 925 | + | len(stackNode.stackedPanes), stackNode.activeStackIndex)) |
| 927 | return nil | 926 | return nil | ||
| 928 | } | 927 | } | ||
| 929 | } | 928 | } | ||
| @@ -942,8 +941,8 @@ func (spm *StackedPaneManager) handleTitleBarClick(titleBarID uint64) { | |||||
| 942 | if p == pane { | 941 | if p == pane { | ||
| 943 | // Only switch if it's not already active | 942 | // Only switch if it's not already active | ||
| 944 | if i != stackNode.activeStackIndex { | 943 | if i != stackNode.activeStackIndex { | ||
| 945 | - | log.Printf("[workspace] title bar clicked: switching from pane %d to pane %d", | 944 | + | logging.Debug(fmt.Sprintf("[workspace] title bar clicked: switching from pane %d to pane %d", |
| 946 | - | stackNode.activeStackIndex, i) | 945 | + | stackNode.activeStackIndex, i)) |
| 947 | 946 | ||||
| 948 | // Update title bar for the pane transitioning from ACTIVE to INACTIVE | 947 | // Update title bar for the pane transitioning from ACTIVE to INACTIVE | ||
| 949 | currentActivePane := stackNode.stackedPanes[stackNode.activeStackIndex] | 948 | currentActivePane := stackNode.stackedPanes[stackNode.activeStackIndex] | ||
M internal/app/browser/webview.go +41 -41
| @@ -4,7 +4,6 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "encoding/json" | 5 | "encoding/json" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "os" | 7 | "os" | ||
| 9 | "path/filepath" | 8 | "path/filepath" | ||
| 10 | "time" | 9 | "time" | ||
| @@ -14,6 +13,7 @@ import ( | |||||
| 14 | "github.com/bnema/dumber/internal/app/messaging" | 13 | "github.com/bnema/dumber/internal/app/messaging" | ||
| 15 | "github.com/bnema/dumber/internal/config" | 14 | "github.com/bnema/dumber/internal/config" | ||
| 16 | "github.com/bnema/dumber/internal/filtering" | 15 | "github.com/bnema/dumber/internal/filtering" | ||
| 16 | + | "github.com/bnema/dumber/internal/logging" | |||
| 17 | "github.com/bnema/dumber/internal/services" | 17 | "github.com/bnema/dumber/internal/services" | ||
| 18 | "github.com/bnema/dumber/pkg/webkit" | 18 | "github.com/bnema/dumber/pkg/webkit" | ||
| 19 | ) | 19 | ) | ||
| @@ -43,19 +43,19 @@ func (app *BrowserApp) buildWebkitConfig() (*webkit.Config, error) { | |||||
| 43 | 43 | ||||
| 44 | // Build WebKit configuration with data directories for persistence | 44 | // Build WebKit configuration with data directories for persistence | ||
| 45 | cfg := &webkit.Config{ | 45 | cfg := &webkit.Config{ | ||
| 46 | - | UserAgent: buildUserAgent(app.config), | 46 | + | UserAgent: buildUserAgent(app.config), |
| 47 | - | EnableJavaScript: true, | 47 | + | EnableJavaScript: true, |
| 48 | - | EnableWebGL: true, | 48 | + | EnableWebGL: true, |
| 49 | - | EnableMediaStream: true, | 49 | + | EnableMediaStream: true, |
| 50 | - | HardwareAcceleration: true, | 50 | + | HardwareAcceleration: true, |
| 51 | - | DefaultFontSize: app.config.Appearance.DefaultFontSize, | 51 | + | DefaultFontSize: app.config.Appearance.DefaultFontSize, |
| 52 | - | MinimumFontSize: 8, | 52 | + | MinimumFontSize: 8, |
| 53 | - | EnablePageCache: true, // Instant back/forward navigation (bfcache) | 53 | + | EnablePageCache: true, // Instant back/forward navigation (bfcache) |
| 54 | - | EnableSmoothScrolling: true, // Smooth scrolling animations | 54 | + | EnableSmoothScrolling: true, // Smooth scrolling animations |
| 55 | - | DataDir: webkitData, | 55 | + | DataDir: webkitData, |
| 56 | - | CacheDir: webkitCache, | 56 | + | CacheDir: webkitCache, |
| 57 | - | AppearanceConfigJSON: app.buildAppearanceConfigJSON(), | 57 | + | AppearanceConfigJSON: app.buildAppearanceConfigJSON(), |
| 58 | - | CreateWindow: true, // Default to creating a window for standalone WebViews | 58 | + | CreateWindow: true, // Default to creating a window for standalone WebViews |
| 59 | } | 59 | } | ||
| 60 | 60 | ||||
| 61 | return cfg, nil | 61 | return cfg, nil | ||
| @@ -102,7 +102,7 @@ func (app *BrowserApp) buildAppearanceConfigJSON() string { | |||||
| 102 | 102 | ||||
| 103 | payload, err := json.Marshal(paletteConfig) | 103 | payload, err := json.Marshal(paletteConfig) | ||
| 104 | if err != nil { | 104 | if err != nil { | ||
| 105 | - | log.Printf("[webview] Failed to marshal palette config: %v", err) | 105 | + | logging.Error(fmt.Sprintf("[webview] Failed to marshal palette config: %v", err)) |
| 106 | return "" | 106 | return "" | ||
| 107 | } | 107 | } | ||
| 108 | 108 | ||||
| @@ -149,7 +149,7 @@ func (app *BrowserApp) createPaneForView(view *webkit.WebView) (*BrowserPane, er | |||||
| 149 | pane.initializeGUITracking() | 149 | pane.initializeGUITracking() | ||
| 150 | 150 | ||||
| 151 | // GUI manager will be loaded on-demand when needed | 151 | // GUI manager will be loaded on-demand when needed | ||
| 152 | - | log.Printf("[webview] Created pane for webview: %d", view.ID()) | 152 | + | logging.Debug(fmt.Sprintf("[webview] Created pane for webview: %d", view.ID())) |
| 153 | 153 | ||||
| 154 | app.attachPaneHandlers(pane) | 154 | app.attachPaneHandlers(pane) | ||
| 155 | return pane, nil | 155 | return pane, nil | ||
| @@ -173,7 +173,7 @@ func (app *BrowserApp) attachPaneHandlers(pane *BrowserPane) { | |||||
| 173 | go func(url, title string) { | 173 | go func(url, title string) { | ||
| 174 | ctx := context.Background() | 174 | ctx := context.Background() | ||
| 175 | if err := app.browserService.UpdatePageTitle(ctx, url, title); err != nil { | 175 | if err := app.browserService.UpdatePageTitle(ctx, url, title); err != nil { | ||
| 176 | - | log.Printf("Warning: failed to update page title: %v", err) | 176 | + | logging.Warn(fmt.Sprintf("Warning: failed to update page title: %v", err)) |
| 177 | } | 177 | } | ||
| 178 | }(url, title) | 178 | }(url, title) | ||
| 179 | 179 | ||||
| @@ -210,7 +210,7 @@ func (app *BrowserApp) attachPaneHandlers(pane *BrowserPane) { | |||||
| 210 | node := app.workspace.GetNodeForWebView(pane.webView) | 210 | node := app.workspace.GetNodeForWebView(pane.webView) | ||
| 211 | if node != nil { | 211 | if node != nil { | ||
| 212 | app.workspace.setupPopupHandling(pane.webView, node) | 212 | app.workspace.setupPopupHandling(pane.webView, node) | ||
| 213 | - | log.Printf("[webview] Setup native popup handling for WebView ID: %d", pane.webView.ID()) | 213 | + | logging.Debug(fmt.Sprintf("[webview] Setup native popup handling for WebView ID: %d", pane.webView.ID())) |
| 214 | } | 214 | } | ||
| 215 | } | 215 | } | ||
| 216 | } | 216 | } | ||
| @@ -245,7 +245,7 @@ func shouldFocusForScriptMessage(payload string) bool { | |||||
| 245 | 245 | ||||
| 246 | // createWebView creates and configures the WebView | 246 | // createWebView creates and configures the WebView | ||
| 247 | func (app *BrowserApp) createWebView() error { | 247 | func (app *BrowserApp) createWebView() error { | ||
| 248 | - | log.Printf("Creating WebView (native backend expected: %v)", webkit.IsNativeAvailable()) | 248 | + | logging.Info(fmt.Sprintf("Creating WebView (native backend expected: %v)", webkit.IsNativeAvailable())) |
| 249 | 249 | ||||
| 250 | cfg, err := app.buildWebkitConfig() | 250 | cfg, err := app.buildWebkitConfig() | ||
| 251 | if err != nil { | 251 | if err != nil { | ||
| @@ -281,7 +281,7 @@ func (app *BrowserApp) createWebView() error { | |||||
| 281 | 281 | ||||
| 282 | // Initialize tab system (creates root container with tab bar and first tab) | 282 | // Initialize tab system (creates root container with tab bar and first tab) | ||
| 283 | if err := app.tabManager.Initialize(initialURL); err != nil { | 283 | if err := app.tabManager.Initialize(initialURL); err != nil { | ||
| 284 | - | log.Printf("ERROR: failed to initialize tab manager: %v", err) | 284 | + | logging.Error(fmt.Sprintf("ERROR: failed to initialize tab manager: %v", err)) |
| 285 | return err | 285 | return err | ||
| 286 | } | 286 | } | ||
| 287 | 287 | ||||
| @@ -289,9 +289,9 @@ func (app *BrowserApp) createWebView() error { | |||||
| 289 | rootContainer := app.tabManager.GetRootContainer() | 289 | rootContainer := app.tabManager.GetRootContainer() | ||
| 290 | if rootContainer != nil { | 290 | if rootContainer != nil { | ||
| 291 | window.SetChild(rootContainer) | 291 | window.SetChild(rootContainer) | ||
| 292 | - | log.Printf("[tabs] Tab system initialized and set as window content") | 292 | + | logging.Info(fmt.Sprintf("[tabs] Tab system initialized and set as window content")) |
| 293 | } else { | 293 | } else { | ||
| 294 | - | log.Printf("ERROR: tab manager root container is nil") | 294 | + | logging.Error(fmt.Sprintf("ERROR: tab manager root container is nil")) |
| 295 | return fmt.Errorf("failed to get tab manager root container") | 295 | return fmt.Errorf("failed to get tab manager root container") | ||
| 296 | } | 296 | } | ||
| 297 | } | 297 | } | ||
| @@ -300,9 +300,9 @@ func (app *BrowserApp) createWebView() error { | |||||
| 300 | if window != nil { | 300 | if window != nil { | ||
| 301 | app.windowShortcutHandler = NewWindowShortcutHandler(window, app) | 301 | app.windowShortcutHandler = NewWindowShortcutHandler(window, app) | ||
| 302 | if app.windowShortcutHandler != nil { | 302 | if app.windowShortcutHandler != nil { | ||
| 303 | - | log.Printf("Window-level global shortcuts initialized") | 303 | + | logging.Info(fmt.Sprintf("Window-level global shortcuts initialized")) |
| 304 | } else { | 304 | } else { | ||
| 305 | - | log.Printf("Warning: failed to initialize window-level shortcuts") | 305 | + | logging.Warn(fmt.Sprintf("Warning: failed to initialize window-level shortcuts")) |
| 306 | } | 306 | } | ||
| 307 | } | 307 | } | ||
| 308 | 308 | ||||
| @@ -310,18 +310,18 @@ func (app *BrowserApp) createWebView() error { | |||||
| 310 | 310 | ||||
| 311 | // Initialize FaviconService after WebView is created | 311 | // Initialize FaviconService after WebView is created | ||
| 312 | if err := app.initializeFaviconService(); err != nil { | 312 | if err := app.initializeFaviconService(); err != nil { | ||
| 313 | - | log.Printf("Warning: failed to initialize favicon service: %v", err) | 313 | + | logging.Warn(fmt.Sprintf("Warning: failed to initialize favicon service: %v", err)) |
| 314 | // Continue without favicon service - not critical | 314 | // Continue without favicon service - not critical | ||
| 315 | } | 315 | } | ||
| 316 | 316 | ||||
| 317 | // Apply initial zoom and show window | 317 | // Apply initial zoom and show window | ||
| 318 | app.zoomController.ApplyInitialZoom() | 318 | app.zoomController.ApplyInitialZoom() | ||
| 319 | 319 | ||||
| 320 | - | log.Printf("Showing WebView window…") | 320 | + | logging.Info(fmt.Sprintf("Showing WebView window…")) |
| 321 | if err := view.Show(); err != nil { | 321 | if err := view.Show(); err != nil { | ||
| 322 | - | log.Printf("Warning: failed to show WebView: %v", err) | 322 | + | logging.Warn(fmt.Sprintf("Warning: failed to show WebView: %v", err)) |
| 323 | } else if !webkit.IsNativeAvailable() { | 323 | } else if !webkit.IsNativeAvailable() { | ||
| 324 | - | log.Printf("Notice: running without webkit_cgo tag — no native window will be displayed.") | 324 | + | logging.Info(fmt.Sprintf("Notice: running without webkit_cgo tag — no native window will be displayed.")) |
| 325 | } | 325 | } | ||
| 326 | 326 | ||||
| 327 | return nil | 327 | return nil | ||
| @@ -362,27 +362,27 @@ func (app *BrowserApp) initializeFaviconService() error { | |||||
| 362 | } | 362 | } | ||
| 363 | 363 | ||||
| 364 | app.faviconService = faviconService | 364 | app.faviconService = faviconService | ||
| 365 | - | log.Printf("[favicon] FaviconService initialized successfully") | 365 | + | logging.Info(fmt.Sprintf("[favicon] FaviconService initialized successfully")) |
| 366 | return nil | 366 | return nil | ||
| 367 | } | 367 | } | ||
| 368 | 368 | ||||
| 369 | // setupContentBlocking initializes the content blocking system with proper timing | 369 | // setupContentBlocking initializes the content blocking system with proper timing | ||
| 370 | func (app *BrowserApp) setupContentBlocking() error { | 370 | func (app *BrowserApp) setupContentBlocking() error { | ||
| 371 | - | log.Printf("Initializing content blocking system...") | 371 | + | logging.Info(fmt.Sprintf("Initializing content blocking system...")) |
| 372 | 372 | ||||
| 373 | // Enable WebKit debug logging if requested | 373 | // Enable WebKit debug logging if requested | ||
| 374 | webkit.SetupWebKitDebugLogging() | 374 | webkit.SetupWebKitDebugLogging() | ||
| 375 | 375 | ||||
| 376 | defer func() { | 376 | defer func() { | ||
| 377 | if r := recover(); r != nil { | 377 | if r := recover(); r != nil { | ||
| 378 | - | log.Printf("Content blocking setup panic recovered: %v", r) | 378 | + | logging.Error(fmt.Sprintf("Content blocking setup panic recovered: %v", r)) |
| 379 | } | 379 | } | ||
| 380 | }() | 380 | }() | ||
| 381 | 381 | ||||
| 382 | // Setup filter system | 382 | // Setup filter system | ||
| 383 | filterManager, err := filtering.SetupFilterSystem() | 383 | filterManager, err := filtering.SetupFilterSystem() | ||
| 384 | if err != nil { | 384 | if err != nil { | ||
| 385 | - | log.Printf("Warning: Failed to setup filter system: %v", err) | 385 | + | logging.Warn(fmt.Sprintf("Warning: Failed to setup filter system: %v", err)) |
| 386 | return nil // Don't fail browser startup, continue without filters | 386 | return nil // Don't fail browser startup, continue without filters | ||
| 387 | } | 387 | } | ||
| 388 | 388 | ||||
| @@ -392,14 +392,14 @@ func (app *BrowserApp) setupContentBlocking() error { | |||||
| 392 | // Get data directory for content blocking service | 392 | // Get data directory for content blocking service | ||
| 393 | dataDir, err := config.GetDataDir() | 393 | dataDir, err := config.GetDataDir() | ||
| 394 | if err != nil { | 394 | if err != nil { | ||
| 395 | - | log.Printf("Warning: Failed to get data dir for content blocking: %v", err) | 395 | + | logging.Warn(fmt.Sprintf("Warning: Failed to get data dir for content blocking: %v", err)) |
| 396 | return nil | 396 | return nil | ||
| 397 | } | 397 | } | ||
| 398 | 398 | ||||
| 399 | // Create content blocking service | 399 | // Create content blocking service | ||
| 400 | cbService, err := filtering.NewContentBlockingService(dataDir+"/webkit", filterManager) | 400 | cbService, err := filtering.NewContentBlockingService(dataDir+"/webkit", filterManager) | ||
| 401 | if err != nil { | 401 | if err != nil { | ||
| 402 | - | log.Printf("Warning: Failed to create content blocking service: %v", err) | 402 | + | logging.Warn(fmt.Sprintf("Warning: Failed to create content blocking service: %v", err)) |
| 403 | return nil | 403 | return nil | ||
| 404 | } | 404 | } | ||
| 405 | app.contentBlockingService = cbService | 405 | app.contentBlockingService = cbService | ||
| @@ -407,36 +407,36 @@ func (app *BrowserApp) setupContentBlocking() error { | |||||
| 407 | // Set up callback to apply network filters when they become ready | 407 | // Set up callback to apply network filters when they become ready | ||
| 408 | // This callback will be called from the async filter loading process | 408 | // This callback will be called from the async filter loading process | ||
| 409 | filterManager.SetFiltersReadyCallback(func() { | 409 | filterManager.SetFiltersReadyCallback(func() { | ||
| 410 | - | log.Printf("[filtering] Filters ready, distributing to all WebViews...") | 410 | + | logging.Info(fmt.Sprintf("[filtering] Filters ready, distributing to all WebViews...")) |
| 411 | 411 | ||||
| 412 | // Get the WebKit JSON rules from filter manager | 412 | // Get the WebKit JSON rules from filter manager | ||
| 413 | filterJSON, err := filterManager.GetNetworkFilters() | 413 | filterJSON, err := filterManager.GetNetworkFilters() | ||
| 414 | if err != nil { | 414 | if err != nil { | ||
| 415 | - | log.Printf("[filtering] Failed to get network filters: %v", err) | 415 | + | logging.Error(fmt.Sprintf("[filtering] Failed to get network filters: %v", err)) |
| 416 | return | 416 | return | ||
| 417 | } | 417 | } | ||
| 418 | 418 | ||||
| 419 | if len(filterJSON) == 0 { | 419 | if len(filterJSON) == 0 { | ||
| 420 | - | log.Printf("[filtering] No network filters to apply") | 420 | + | logging.Info(fmt.Sprintf("[filtering] No network filters to apply")) |
| 421 | return | 421 | return | ||
| 422 | } | 422 | } | ||
| 423 | 423 | ||||
| 424 | - | log.Printf("[filtering] Got %d bytes of WebKit JSON rules", len(filterJSON)) | 424 | + | logging.Debug(fmt.Sprintf("[filtering] Got %d bytes of WebKit JSON rules", len(filterJSON))) |
| 425 | 425 | ||||
| 426 | // Signal the service that filters are ready - it will apply to all registered WebViews | 426 | // Signal the service that filters are ready - it will apply to all registered WebViews | ||
| 427 | cbService.SetFiltersReady(filterJSON) | 427 | cbService.SetFiltersReady(filterJSON) | ||
| 428 | - | log.Printf("[filtering] Content blocking enabled for all WebViews") | 428 | + | logging.Info(fmt.Sprintf("[filtering] Content blocking enabled for all WebViews")) |
| 429 | }) | 429 | }) | ||
| 430 | 430 | ||||
| 431 | // Start async filter loading | 431 | // Start async filter loading | ||
| 432 | // This allows filters to compile in the background while the browser starts | 432 | // This allows filters to compile in the background while the browser starts | ||
| 433 | go func() { | 433 | go func() { | ||
| 434 | if err := filtering.InitializeFiltersAsync(filterManager); err != nil { | 434 | if err := filtering.InitializeFiltersAsync(filterManager); err != nil { | ||
| 435 | - | log.Printf("Warning: failed to initialize filters asynchronously: %v", err) | 435 | + | logging.Warn(fmt.Sprintf("Warning: failed to initialize filters asynchronously: %v", err)) |
| 436 | } | 436 | } | ||
| 437 | }() | 437 | }() | ||
| 438 | 438 | ||||
| 439 | - | log.Printf("Content blocking system initialization started") | 439 | + | logging.Info(fmt.Sprintf("Content blocking system initialization started")) |
| 440 | return nil | 440 | return nil | ||
| 441 | } | 441 | } | ||
| 442 | 442 | ||||
M internal/app/browser/window_shortcuts.go +61 -61
| @@ -2,11 +2,11 @@ package browser | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "log" | |||
| 6 | "sync" | 5 | "sync" | ||
| 7 | "time" | 6 | "time" | ||
| 8 | 7 | ||||
| 9 | "github.com/bnema/dumber/internal/app/messaging" | 8 | "github.com/bnema/dumber/internal/app/messaging" | ||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 11 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 11 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 12 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 12 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| @@ -35,7 +35,7 @@ func NewWindowShortcutHandler(window *webkit.Window, app *BrowserApp) *WindowSho | |||||
| 35 | } | 35 | } | ||
| 36 | 36 | ||||
| 37 | if err := h.initialize(); err != nil { | 37 | if err := h.initialize(); err != nil { | ||
| 38 | - | log.Printf("[window-shortcuts] Failed to initialize: %v", err) | 38 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to initialize: %v", err)) |
| 39 | return nil | 39 | return nil | ||
| 40 | } | 40 | } | ||
| 41 | 41 | ||||
| @@ -51,7 +51,7 @@ func (h *WindowShortcutHandler) initialize() error { | |||||
| 51 | 51 | ||||
| 52 | // Register hardware keycode-based shortcuts (for AZERTY/international keyboards) | 52 | // Register hardware keycode-based shortcuts (for AZERTY/international keyboards) | ||
| 53 | if err := h.registerKeycodeShortcuts(); err != nil { | 53 | if err := h.registerKeycodeShortcuts(); err != nil { | ||
| 54 | - | log.Printf("[window-shortcuts] Failed to register keycode shortcuts: %v", err) | 54 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to register keycode shortcuts: %v", err)) |
| 55 | } | 55 | } | ||
| 56 | 56 | ||||
| 57 | return h.registerGlobalShortcuts() | 57 | return h.registerGlobalShortcuts() | ||
| @@ -103,8 +103,8 @@ func (h *WindowShortcutHandler) registerGlobalShortcuts() error { | |||||
| 103 | 103 | ||||
| 104 | for _, shortcut := range shortcuts { | 104 | for _, shortcut := range shortcuts { | ||
| 105 | h.shortcuts.RegisterShortcut(shortcut.key, shortcut.handler) | 105 | h.shortcuts.RegisterShortcut(shortcut.key, shortcut.handler) | ||
| 106 | - | log.Printf("[window-shortcuts] Registered global shortcut: %s (%s)", | 106 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Registered global shortcut: %s (%s)", |
| 107 | - | shortcut.key, shortcut.desc) | 107 | + | shortcut.key, shortcut.desc)) |
| 108 | } | 108 | } | ||
| 109 | 109 | ||||
| 110 | // NOTE: Ctrl+P is NOT registered here - it's handled by WebView's keyboard bridge | 110 | // NOTE: Ctrl+P is NOT registered here - it's handled by WebView's keyboard bridge | ||
| @@ -164,14 +164,14 @@ func (h *WindowShortcutHandler) registerKeycodeShortcuts() error { | |||||
| 164 | 164 | ||||
| 165 | // Handle Ctrl+Tab for next tab | 165 | // Handle Ctrl+Tab for next tab | ||
| 166 | if keyval == gdkKeyTab && state.Has(gdk.ControlMask) && !state.Has(gdk.ShiftMask) { | 166 | if keyval == gdkKeyTab && state.Has(gdk.ControlMask) && !state.Has(gdk.ShiftMask) { | ||
| 167 | - | log.Printf("[window-shortcuts] Ctrl+Tab -> next tab") | 167 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Ctrl+Tab -> next tab")) |
| 168 | h.handleNextTab() | 168 | h.handleNextTab() | ||
| 169 | return true // Consume event to prevent focus cycling | 169 | return true // Consume event to prevent focus cycling | ||
| 170 | } | 170 | } | ||
| 171 | 171 | ||||
| 172 | // Handle Ctrl+Shift+Tab for previous tab | 172 | // Handle Ctrl+Shift+Tab for previous tab | ||
| 173 | if keyval == gdkKeyTab && state.Has(gdk.ControlMask) && state.Has(gdk.ShiftMask) { | 173 | if keyval == gdkKeyTab && state.Has(gdk.ControlMask) && state.Has(gdk.ShiftMask) { | ||
| 174 | - | log.Printf("[window-shortcuts] Ctrl+Shift+Tab -> previous tab") | 174 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Ctrl+Shift+Tab -> previous tab")) |
| 175 | h.handlePrevTab() | 175 | h.handlePrevTab() | ||
| 176 | return true // Consume event to prevent focus cycling | 176 | return true // Consume event to prevent focus cycling | ||
| 177 | } | 177 | } | ||
| @@ -180,7 +180,7 @@ func (h *WindowShortcutHandler) registerKeycodeShortcuts() error { | |||||
| 180 | if state.Has(gdk.AltMask) && !state.Has(gdk.ControlMask) && !state.Has(gdk.ShiftMask) { | 180 | if state.Has(gdk.AltMask) && !state.Has(gdk.ControlMask) && !state.Has(gdk.ShiftMask) { | ||
| 181 | // Check if this is a number key | 181 | // Check if this is a number key | ||
| 182 | if tabIndex, ok := keycodeToTab[keycode]; ok { | 182 | if tabIndex, ok := keycodeToTab[keycode]; ok { | ||
| 183 | - | log.Printf("[window-shortcuts] Hardware keycode shortcut: Alt+keycode(%d) -> tab %d", keycode, tabIndex) | 183 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Hardware keycode shortcut: Alt+keycode(%d) -> tab %d", keycode, tabIndex)) |
| 184 | h.handleDirectTabSwitch(tabIndex) | 184 | h.handleDirectTabSwitch(tabIndex) | ||
| 185 | return true // Handled | 185 | return true // Handled | ||
| 186 | } | 186 | } | ||
| @@ -236,7 +236,7 @@ func (h *WindowShortcutHandler) registerKeycodeShortcuts() error { | |||||
| 236 | } | 236 | } | ||
| 237 | 237 | ||||
| 238 | if handled { | 238 | if handled { | ||
| 239 | - | log.Printf("[window-shortcuts] Tab mode action: %s", action) | 239 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Tab mode action: %s", action)) |
| 240 | h.handleTabModeAction(action) | 240 | h.handleTabModeAction(action) | ||
| 241 | return true // Consume event | 241 | return true // Consume event | ||
| 242 | } | 242 | } | ||
| @@ -248,7 +248,7 @@ func (h *WindowShortcutHandler) registerKeycodeShortcuts() error { | |||||
| 248 | }) | 248 | }) | ||
| 249 | 249 | ||||
| 250 | gtkWindow.AddController(keyController) | 250 | gtkWindow.AddController(keyController) | ||
| 251 | - | log.Printf("[window-shortcuts] Registered capture phase shortcuts: Ctrl+Tab, Alt+number") | 251 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Registered capture phase shortcuts: Ctrl+Tab, Alt+number")) |
| 252 | 252 | ||||
| 253 | return nil | 253 | return nil | ||
| 254 | } | 254 | } | ||
| @@ -271,11 +271,11 @@ func (h *WindowShortcutHandler) handleCopyURL() { | |||||
| 271 | h.lastCopyURL = time.Now() | 271 | h.lastCopyURL = time.Now() | ||
| 272 | 272 | ||||
| 273 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 273 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 274 | - | log.Printf("[window-shortcuts] No active pane for copy URL") | 274 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for copy URL")) |
| 275 | return | 275 | return | ||
| 276 | } | 276 | } | ||
| 277 | 277 | ||||
| 278 | - | log.Printf("[window-shortcuts] Copy URL -> pane %p", h.app.activePane.webView) | 278 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Copy URL -> pane %p", h.app.activePane.webView)) |
| 279 | 279 | ||||
| 280 | // Execute copy URL script directly on active pane | 280 | // Execute copy URL script directly on active pane | ||
| 281 | script := ` | 281 | script := ` | ||
| @@ -299,7 +299,7 @@ func (h *WindowShortcutHandler) handleCopyURL() { | |||||
| 299 | ` | 299 | ` | ||
| 300 | 300 | ||||
| 301 | if err := h.app.activePane.webView.InjectScript(script); err != nil { | 301 | if err := h.app.activePane.webView.InjectScript(script); err != nil { | ||
| 302 | - | log.Printf("[window-shortcuts] Failed to inject copy URL script: %v", err) | 302 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to inject copy URL script: %v", err)) |
| 303 | } | 303 | } | ||
| 304 | } | 304 | } | ||
| 305 | 305 | ||||
| @@ -313,14 +313,14 @@ func (h *WindowShortcutHandler) handleDevTools() { | |||||
| 313 | h.lastDevTools = time.Now() | 313 | h.lastDevTools = time.Now() | ||
| 314 | 314 | ||||
| 315 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 315 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 316 | - | log.Printf("[window-shortcuts] No active pane for devtools") | 316 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for devtools")) |
| 317 | return | 317 | return | ||
| 318 | } | 318 | } | ||
| 319 | 319 | ||||
| 320 | - | log.Printf("[window-shortcuts] DevTools -> pane %p", h.app.activePane.webView) | 320 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] DevTools -> pane %p", h.app.activePane.webView)) |
| 321 | 321 | ||||
| 322 | if err := h.app.activePane.webView.ShowDevTools(); err != nil { | 322 | if err := h.app.activePane.webView.ShowDevTools(); err != nil { | ||
| 323 | - | log.Printf("[window-shortcuts] Failed to show devtools: %v", err) | 323 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to show devtools: %v", err)) |
| 324 | } | 324 | } | ||
| 325 | } | 325 | } | ||
| 326 | 326 | ||||
| @@ -334,14 +334,14 @@ func (h *WindowShortcutHandler) handlePrint() { | |||||
| 334 | h.lastPrint = time.Now() | 334 | h.lastPrint = time.Now() | ||
| 335 | 335 | ||||
| 336 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 336 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 337 | - | log.Printf("[window-shortcuts] No active pane for print") | 337 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for print")) |
| 338 | return | 338 | return | ||
| 339 | } | 339 | } | ||
| 340 | 340 | ||||
| 341 | - | log.Printf("[window-shortcuts] Print -> pane %p", h.app.activePane.webView) | 341 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Print -> pane %p", h.app.activePane.webView)) |
| 342 | 342 | ||||
| 343 | if err := h.app.activePane.webView.ShowPrintDialog(); err != nil { | 343 | if err := h.app.activePane.webView.ShowPrintDialog(); err != nil { | ||
| 344 | - | log.Printf("[window-shortcuts] Failed to show print dialog: %v", err) | 344 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to show print dialog: %v", err)) |
| 345 | } | 345 | } | ||
| 346 | } | 346 | } | ||
| 347 | 347 | ||||
| @@ -350,14 +350,14 @@ func (h *WindowShortcutHandler) handleReload() { | |||||
| 350 | defer h.mu.Unlock() | 350 | defer h.mu.Unlock() | ||
| 351 | 351 | ||||
| 352 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 352 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 353 | - | log.Printf("[window-shortcuts] No active pane for reload") | 353 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for reload")) |
| 354 | return | 354 | return | ||
| 355 | } | 355 | } | ||
| 356 | 356 | ||||
| 357 | - | log.Printf("[window-shortcuts] Reload page -> pane %p", h.app.activePane.webView) | 357 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Reload page -> pane %p", h.app.activePane.webView)) |
| 358 | 358 | ||||
| 359 | if err := h.app.activePane.webView.Reload(); err != nil { | 359 | if err := h.app.activePane.webView.Reload(); err != nil { | ||
| 360 | - | log.Printf("[window-shortcuts] Failed to reload page: %v", err) | 360 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to reload page: %v", err)) |
| 361 | } | 361 | } | ||
| 362 | } | 362 | } | ||
| 363 | 363 | ||||
| @@ -366,14 +366,14 @@ func (h *WindowShortcutHandler) handleHardReload() { | |||||
| 366 | defer h.mu.Unlock() | 366 | defer h.mu.Unlock() | ||
| 367 | 367 | ||||
| 368 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 368 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 369 | - | log.Printf("[window-shortcuts] No active pane for hard reload") | 369 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for hard reload")) |
| 370 | return | 370 | return | ||
| 371 | } | 371 | } | ||
| 372 | 372 | ||||
| 373 | - | log.Printf("[window-shortcuts] Hard reload (bypass cache) -> pane %p", h.app.activePane.webView) | 373 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Hard reload (bypass cache) -> pane %p", h.app.activePane.webView)) |
| 374 | 374 | ||||
| 375 | if err := h.app.activePane.webView.ReloadBypassCache(); err != nil { | 375 | if err := h.app.activePane.webView.ReloadBypassCache(); err != nil { | ||
| 376 | - | log.Printf("[window-shortcuts] Failed to hard reload page: %v", err) | 376 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to hard reload page: %v", err)) |
| 377 | } | 377 | } | ||
| 378 | } | 378 | } | ||
| 379 | 379 | ||||
| @@ -398,12 +398,12 @@ func (h *WindowShortcutHandler) handleZoom(action string, multiplier float64) { | |||||
| 398 | defer h.mu.Unlock() | 398 | defer h.mu.Unlock() | ||
| 399 | 399 | ||||
| 400 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 400 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 401 | - | log.Printf("[window-shortcuts] No active pane for zoom %s", action) | 401 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for zoom %s", action)) |
| 402 | return | 402 | return | ||
| 403 | } | 403 | } | ||
| 404 | 404 | ||||
| 405 | activeWebView := h.app.activePane.webView | 405 | activeWebView := h.app.activePane.webView | ||
| 406 | - | log.Printf("[window-shortcuts] Zoom %s -> pane %p", action, activeWebView) | 406 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Zoom %s -> pane %p", action, activeWebView)) |
| 407 | 407 | ||||
| 408 | // Get current zoom level | 408 | // Get current zoom level | ||
| 409 | currentZoom := activeWebView.GetZoom() | 409 | currentZoom := activeWebView.GetZoom() | ||
| @@ -423,11 +423,11 @@ func (h *WindowShortcutHandler) handleZoom(action string, multiplier float64) { | |||||
| 423 | } | 423 | } | ||
| 424 | } | 424 | } | ||
| 425 | 425 | ||||
| 426 | - | log.Printf("[window-shortcuts] Zoom %s: %.2f -> %.2f", action, currentZoom, newZoom) | 426 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Zoom %s: %.2f -> %.2f", action, currentZoom, newZoom)) |
| 427 | 427 | ||||
| 428 | // Apply zoom to active pane | 428 | // Apply zoom to active pane | ||
| 429 | if err := activeWebView.SetZoom(newZoom); err != nil { | 429 | if err := activeWebView.SetZoom(newZoom); err != nil { | ||
| 430 | - | log.Printf("[window-shortcuts] Failed to set zoom: %v", err) | 430 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to set zoom: %v", err)) |
| 431 | return | 431 | return | ||
| 432 | } | 432 | } | ||
| 433 | 433 | ||||
| @@ -448,7 +448,7 @@ func (h *WindowShortcutHandler) handleZoom(action string, multiplier float64) { | |||||
| 448 | `, newZoom) | 448 | `, newZoom) | ||
| 449 | 449 | ||||
| 450 | if err := activeWebView.InjectScript(toastScript); err != nil { | 450 | if err := activeWebView.InjectScript(toastScript); err != nil { | ||
| 451 | - | log.Printf("[window-shortcuts] Failed to show zoom toast: %v", err) | 451 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to show zoom toast: %v", err)) |
| 452 | } | 452 | } | ||
| 453 | } | 453 | } | ||
| 454 | 454 | ||||
| @@ -459,7 +459,7 @@ func (h *WindowShortcutHandler) ensureGUIInActivePane(component string) { | |||||
| 459 | 459 | ||||
| 460 | pane := h.app.activePane | 460 | pane := h.app.activePane | ||
| 461 | if !pane.HasGUI() { | 461 | if !pane.HasGUI() { | ||
| 462 | - | log.Printf("[window-shortcuts] Injecting GUI into pane %s for %s", pane.ID(), component) | 462 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Injecting GUI into pane %s for %s", pane.ID(), component)) |
| 463 | if h.app.workspace != nil { | 463 | if h.app.workspace != nil { | ||
| 464 | h.app.workspace.ensureGUIInPane(pane) | 464 | h.app.workspace.ensureGUIInPane(pane) | ||
| 465 | } | 465 | } | ||
| @@ -467,7 +467,7 @@ func (h *WindowShortcutHandler) ensureGUIInActivePane(component string) { | |||||
| 467 | 467 | ||||
| 468 | // Ensure specific component is available | 468 | // Ensure specific component is available | ||
| 469 | if !pane.HasGUIComponent(component) { | 469 | if !pane.HasGUIComponent(component) { | ||
| 470 | - | log.Printf("[window-shortcuts] Ensuring %s component in pane %s", component, pane.ID()) | 470 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Ensuring %s component in pane %s", component, pane.ID())) |
| 471 | pane.SetGUIComponent(component, true) | 471 | pane.SetGUIComponent(component, true) | ||
| 472 | } | 472 | } | ||
| 473 | } | 473 | } | ||
| @@ -484,17 +484,17 @@ func (h *WindowShortcutHandler) handleUIToggle(lastToggle *time.Time, featureNam | |||||
| 484 | defer h.mu.Unlock() | 484 | defer h.mu.Unlock() | ||
| 485 | 485 | ||||
| 486 | if time.Since(*lastToggle) < 50*time.Millisecond { | 486 | if time.Since(*lastToggle) < 50*time.Millisecond { | ||
| 487 | - | log.Printf("[window-shortcuts] %s toggle debounced", featureName) | 487 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] %s toggle debounced", featureName)) |
| 488 | return | 488 | return | ||
| 489 | } | 489 | } | ||
| 490 | *lastToggle = time.Now() | 490 | *lastToggle = time.Now() | ||
| 491 | 491 | ||||
| 492 | if h.app.activePane == nil || h.app.activePane.webView == nil { | 492 | if h.app.activePane == nil || h.app.activePane.webView == nil { | ||
| 493 | - | log.Printf("[window-shortcuts] No active pane for %s", featureName) | 493 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active pane for %s", featureName)) |
| 494 | return | 494 | return | ||
| 495 | } | 495 | } | ||
| 496 | 496 | ||||
| 497 | - | log.Printf("[window-shortcuts] %s toggle -> pane %p", featureName, h.app.activePane.webView) | 497 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] %s toggle -> pane %p", featureName, h.app.activePane.webView)) |
| 498 | 498 | ||||
| 499 | h.ensureGUIInActivePane("omnibox") | 499 | h.ensureGUIInActivePane("omnibox") | ||
| 500 | 500 | ||||
| @@ -504,48 +504,48 @@ func (h *WindowShortcutHandler) handleUIToggle(lastToggle *time.Time, featureNam | |||||
| 504 | "timestamp": time.Now().UnixMilli(), | 504 | "timestamp": time.Now().UnixMilli(), | ||
| 505 | "source": "window-global", | 505 | "source": "window-global", | ||
| 506 | }); err != nil { | 506 | }); err != nil { | ||
| 507 | - | log.Printf("[window-shortcuts] Failed to dispatch %s toggle: %v", featureName, err) | 507 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to dispatch %s toggle: %v", featureName, err)) |
| 508 | } | 508 | } | ||
| 509 | } | 509 | } | ||
| 510 | 510 | ||||
| 511 | func (h *WindowShortcutHandler) handleWorkspaceNavigation(direction string) { | 511 | func (h *WindowShortcutHandler) handleWorkspaceNavigation(direction string) { | ||
| 512 | if h.app == nil || h.app.workspace == nil { | 512 | if h.app == nil || h.app.workspace == nil { | ||
| 513 | - | log.Printf("[window-shortcuts] No workspace for navigation") | 513 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No workspace for navigation")) |
| 514 | return | 514 | return | ||
| 515 | } | 515 | } | ||
| 516 | 516 | ||||
| 517 | - | log.Printf("[window-shortcuts] Workspace navigation: %s", direction) | 517 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Workspace navigation: %s", direction)) |
| 518 | 518 | ||||
| 519 | if h.app.workspace.FocusNeighbor(direction) { | 519 | if h.app.workspace.FocusNeighbor(direction) { | ||
| 520 | - | log.Printf("[window-shortcuts] Workspace navigation %s successful", direction) | 520 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Workspace navigation %s successful", direction)) |
| 521 | } else { | 521 | } else { | ||
| 522 | - | log.Printf("[window-shortcuts] Workspace navigation %s failed", direction) | 522 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Workspace navigation %s failed", direction)) |
| 523 | } | 523 | } | ||
| 524 | } | 524 | } | ||
| 525 | 525 | ||||
| 526 | func (h *WindowShortcutHandler) handleClosePane() { | 526 | func (h *WindowShortcutHandler) handleClosePane() { | ||
| 527 | if h.app == nil || h.app.workspace == nil { | 527 | if h.app == nil || h.app.workspace == nil { | ||
| 528 | - | log.Printf("[window-shortcuts] No workspace for close pane") | 528 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No workspace for close pane")) |
| 529 | return | 529 | return | ||
| 530 | } | 530 | } | ||
| 531 | 531 | ||||
| 532 | // Find the currently active WebView | 532 | // Find the currently active WebView | ||
| 533 | var activeWebView *webkit.WebView | 533 | var activeWebView *webkit.WebView | ||
| 534 | - | log.Printf("[window-shortcuts] Searching for active WebView among %d WebViews", len(h.app.workspace.viewToNode)) | 534 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Searching for active WebView among %d WebViews", len(h.app.workspace.viewToNode))) |
| 535 | for webView := range h.app.workspace.viewToNode { | 535 | for webView := range h.app.workspace.viewToNode { | ||
| 536 | if webView != nil { | 536 | if webView != nil { | ||
| 537 | isActive := webView.IsActive() | 537 | isActive := webView.IsActive() | ||
| 538 | - | log.Printf("[window-shortcuts] WebView %d: IsActive=%t", webView.ID(), isActive) | 538 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] WebView %d: IsActive=%t", webView.ID(), isActive)) |
| 539 | if isActive { | 539 | if isActive { | ||
| 540 | activeWebView = webView | 540 | activeWebView = webView | ||
| 541 | - | log.Printf("[window-shortcuts] Found active WebView: %d", webView.ID()) | 541 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Found active WebView: %d", webView.ID())) |
| 542 | break | 542 | break | ||
| 543 | } | 543 | } | ||
| 544 | } | 544 | } | ||
| 545 | } | 545 | } | ||
| 546 | 546 | ||||
| 547 | if activeWebView == nil { | 547 | if activeWebView == nil { | ||
| 548 | - | log.Printf("[window-shortcuts] No active WebView found, using workspace closeCurrentPane") | 548 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] No active WebView found, using workspace closeCurrentPane")) |
| 549 | h.app.workspace.closeCurrentPane() | 549 | h.app.workspace.closeCurrentPane() | ||
| 550 | return | 550 | return | ||
| 551 | } | 551 | } | ||
| @@ -553,7 +553,7 @@ func (h *WindowShortcutHandler) handleClosePane() { | |||||
| 553 | // Check if the active WebView is a popup | 553 | // Check if the active WebView is a popup | ||
| 554 | node := h.app.workspace.viewToNode[activeWebView] | 554 | node := h.app.workspace.viewToNode[activeWebView] | ||
| 555 | if node != nil && node.isPopup { | 555 | if node != nil && node.isPopup { | ||
| 556 | - | log.Printf("[window-shortcuts] Closing popup via OnWorkspaceMessage") | 556 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Closing popup via OnWorkspaceMessage")) |
| 557 | // Use the proper close-popup message for popups | 557 | // Use the proper close-popup message for popups | ||
| 558 | msg := messaging.Message{ | 558 | msg := messaging.Message{ | ||
| 559 | Event: "close-popup", | 559 | Event: "close-popup", | ||
| @@ -562,7 +562,7 @@ func (h *WindowShortcutHandler) handleClosePane() { | |||||
| 562 | } | 562 | } | ||
| 563 | h.app.workspace.OnWorkspaceMessage(activeWebView, msg) | 563 | h.app.workspace.OnWorkspaceMessage(activeWebView, msg) | ||
| 564 | } else { | 564 | } else { | ||
| 565 | - | log.Printf("[window-shortcuts] Closing regular pane via workspace closeCurrentPane") | 565 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Closing regular pane via workspace closeCurrentPane")) |
| 566 | // Use regular close for non-popup panes | 566 | // Use regular close for non-popup panes | ||
| 567 | h.app.workspace.closeCurrentPane() | 567 | h.app.workspace.closeCurrentPane() | ||
| 568 | } | 568 | } | ||
| @@ -571,61 +571,61 @@ func (h *WindowShortcutHandler) handleClosePane() { | |||||
| 571 | // handlePaneMode enters pane mode for modal pane management. | 571 | // handlePaneMode enters pane mode for modal pane management. | ||
| 572 | func (h *WindowShortcutHandler) handlePaneMode() { | 572 | func (h *WindowShortcutHandler) handlePaneMode() { | ||
| 573 | if h.app == nil || h.app.workspace == nil { | 573 | if h.app == nil || h.app.workspace == nil { | ||
| 574 | - | log.Printf("[window-shortcuts] Cannot enter pane mode: workspace not available") | 574 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Cannot enter pane mode: workspace not available")) |
| 575 | return | 575 | return | ||
| 576 | } | 576 | } | ||
| 577 | 577 | ||||
| 578 | - | log.Printf("[window-shortcuts] Entering pane mode") | 578 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Entering pane mode")) |
| 579 | h.app.workspace.EnterPaneMode() | 579 | h.app.workspace.EnterPaneMode() | ||
| 580 | } | 580 | } | ||
| 581 | 581 | ||||
| 582 | // handleTabMode enters tab mode for modal tab management. | 582 | // handleTabMode enters tab mode for modal tab management. | ||
| 583 | func (h *WindowShortcutHandler) handleTabMode() { | 583 | func (h *WindowShortcutHandler) handleTabMode() { | ||
| 584 | if h.app == nil || h.app.tabManager == nil { | 584 | if h.app == nil || h.app.tabManager == nil { | ||
| 585 | - | log.Printf("[window-shortcuts] Cannot enter tab mode: tab manager not available") | 585 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Cannot enter tab mode: tab manager not available")) |
| 586 | return | 586 | return | ||
| 587 | } | 587 | } | ||
| 588 | 588 | ||||
| 589 | - | log.Printf("[window-shortcuts] Entering tab mode") | 589 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Entering tab mode")) |
| 590 | h.app.tabManager.EnterTabMode() | 590 | h.app.tabManager.EnterTabMode() | ||
| 591 | } | 591 | } | ||
| 592 | 592 | ||||
| 593 | // handleNextTab switches to the next tab. | 593 | // handleNextTab switches to the next tab. | ||
| 594 | func (h *WindowShortcutHandler) handleNextTab() { | 594 | func (h *WindowShortcutHandler) handleNextTab() { | ||
| 595 | if h.app == nil || h.app.tabManager == nil { | 595 | if h.app == nil || h.app.tabManager == nil { | ||
| 596 | - | log.Printf("[window-shortcuts] Cannot switch tab: tab manager not available") | 596 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Cannot switch tab: tab manager not available")) |
| 597 | return | 597 | return | ||
| 598 | } | 598 | } | ||
| 599 | 599 | ||||
| 600 | - | log.Printf("[window-shortcuts] Switching to next tab") | 600 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Switching to next tab")) |
| 601 | if err := h.app.tabManager.NextTab(); err != nil { | 601 | if err := h.app.tabManager.NextTab(); err != nil { | ||
| 602 | - | log.Printf("[window-shortcuts] Failed to switch to next tab: %v", err) | 602 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to switch to next tab: %v", err)) |
| 603 | } | 603 | } | ||
| 604 | } | 604 | } | ||
| 605 | 605 | ||||
| 606 | // handlePrevTab switches to the previous tab. | 606 | // handlePrevTab switches to the previous tab. | ||
| 607 | func (h *WindowShortcutHandler) handlePrevTab() { | 607 | func (h *WindowShortcutHandler) handlePrevTab() { | ||
| 608 | if h.app == nil || h.app.tabManager == nil { | 608 | if h.app == nil || h.app.tabManager == nil { | ||
| 609 | - | log.Printf("[window-shortcuts] Cannot switch tab: tab manager not available") | 609 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Cannot switch tab: tab manager not available")) |
| 610 | return | 610 | return | ||
| 611 | } | 611 | } | ||
| 612 | 612 | ||||
| 613 | - | log.Printf("[window-shortcuts] Switching to previous tab") | 613 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Switching to previous tab")) |
| 614 | if err := h.app.tabManager.PreviousTab(); err != nil { | 614 | if err := h.app.tabManager.PreviousTab(); err != nil { | ||
| 615 | - | log.Printf("[window-shortcuts] Failed to switch to previous tab: %v", err) | 615 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to switch to previous tab: %v", err)) |
| 616 | } | 616 | } | ||
| 617 | } | 617 | } | ||
| 618 | 618 | ||||
| 619 | // handleDirectTabSwitch switches to a specific tab by index (0-based). | 619 | // handleDirectTabSwitch switches to a specific tab by index (0-based). | ||
| 620 | func (h *WindowShortcutHandler) handleDirectTabSwitch(index int) { | 620 | func (h *WindowShortcutHandler) handleDirectTabSwitch(index int) { | ||
| 621 | if h.app == nil || h.app.tabManager == nil { | 621 | if h.app == nil || h.app.tabManager == nil { | ||
| 622 | - | log.Printf("[window-shortcuts] Cannot switch tab: tab manager not available") | 622 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Cannot switch tab: tab manager not available")) |
| 623 | return | 623 | return | ||
| 624 | } | 624 | } | ||
| 625 | 625 | ||||
| 626 | - | log.Printf("[window-shortcuts] Direct tab switch to index %d", index) | 626 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Direct tab switch to index %d", index)) |
| 627 | if err := h.app.tabManager.SwitchToTab(index); err != nil { | 627 | if err := h.app.tabManager.SwitchToTab(index); err != nil { | ||
| 628 | - | log.Printf("[window-shortcuts] Failed to switch to tab %d: %v", index, err) | 628 | + | logging.Error(fmt.Sprintf("[window-shortcuts] Failed to switch to tab %d: %v", index, err)) |
| 629 | } | 629 | } | ||
| 630 | } | 630 | } | ||
| 631 | 631 | ||||
| @@ -653,7 +653,7 @@ func (h *WindowShortcutHandler) handleTabModeAction(action string) { | |||||
| 653 | } | 653 | } | ||
| 654 | 654 | ||||
| 655 | // Tab mode is active, handle the action | 655 | // Tab mode is active, handle the action | ||
| 656 | - | log.Printf("[window-shortcuts] Tab mode action: %s", action) | 656 | + | logging.Debug(fmt.Sprintf("[window-shortcuts] Tab mode action: %s", action)) |
| 657 | h.app.tabManager.HandleTabAction(action) | 657 | h.app.tabManager.HandleTabAction(action) | ||
| 658 | } | 658 | } | ||
| 659 | 659 | ||||
M internal/app/browser/workspace_css.go +9 -4
| @@ -3,10 +3,10 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "strings" | 6 | "strings" | ||
| 8 | 7 | ||||
| 9 | "github.com/bnema/dumber/internal/config" | 8 | "github.com/bnema/dumber/internal/config" | ||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| @@ -393,12 +393,17 @@ func (wm *WorkspaceManager) ensureWorkspaceStyles() { | |||||
| 393 | // Only apply CSS if it hasn't been applied globally or if content changed | 393 | // Only apply CSS if it hasn't been applied globally or if content changed | ||
| 394 | if !globalCSSInitialized || globalCSSContent != workspaceCSS { | 394 | if !globalCSSInitialized || globalCSSContent != workspaceCSS { | ||
| 395 | if err := webkit.AddCSSProvider(workspaceCSS); err != nil { | 395 | if err := webkit.AddCSSProvider(workspaceCSS); err != nil { | ||
| 396 | - | log.Printf("[workspace] WARNING: Failed to add CSS provider: %v", err) | 396 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Failed to add CSS provider: %v", err)) |
| 397 | } | 397 | } | ||
| 398 | globalCSSInitialized = true | 398 | globalCSSInitialized = true | ||
| 399 | globalCSSContent = workspaceCSS | 399 | globalCSSContent = workspaceCSS | ||
| 400 | - | log.Printf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS)) | 400 | + | logging.Info(fmt.Sprintf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS))) |
| 401 | - | log.Printf("[workspace] CSS preview: %s...", workspaceCSS[:min(200, len(workspaceCSS))]) | 401 | + | |
| 402 | + | previewLen := 200 | |||
| 403 | + | if len(workspaceCSS) < previewLen { | |||
| 404 | + | previewLen = len(workspaceCSS) | |||
| 405 | + | } | |||
| 406 | + | logging.Info(fmt.Sprintf("[workspace] CSS preview: %s...", workspaceCSS[:previewLen])) | |||
| 402 | } | 407 | } | ||
| 403 | 408 | ||||
| 404 | wm.cssInitialized = true | 409 | wm.cssInitialized = true | ||
M internal/app/browser/workspace_debug.go +6 -2
| @@ -1,12 +1,16 @@ | |||||
| 1 | package browser | 1 | package browser | ||
| 2 | 2 | ||||
| 3 | - | import "log" | 3 | + | import ( |
| 4 | + | "fmt" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | + | ) | |||
| 4 | 8 | ||||
| 5 | func (wm *WorkspaceManager) paneCloseLogf(format string, args ...interface{}) { | 9 | func (wm *WorkspaceManager) paneCloseLogf(format string, args ...interface{}) { | ||
| 6 | if wm == nil || !wm.debugPaneClose { | 10 | if wm == nil || !wm.debugPaneClose { | ||
| 7 | return | 11 | return | ||
| 8 | } | 12 | } | ||
| 9 | - | log.Printf("[pane-close] "+format, args...) | 13 | + | logging.Info(fmt.Sprintf("[pane-close] "+format, args...)) |
| 10 | } | 14 | } | ||
| 11 | 15 | ||||
| 12 | func (wm *WorkspaceManager) dumpTreeState(label string) { | 16 | func (wm *WorkspaceManager) dumpTreeState(label string) { | ||
M internal/app/browser/workspace_diagnostics.go +3 -2
| @@ -2,9 +2,10 @@ package browser | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "log" | |||
| 6 | "strings" | 5 | "strings" | ||
| 7 | "sync" | 6 | "sync" | ||
| 7 | + | ||||
| 8 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| 10 | type TreeSnapshot struct { | 11 | type TreeSnapshot struct { | ||
| @@ -63,7 +64,7 @@ func (d *WorkspaceDiagnostics) Capture(label string, root *paneNode) TreeSnapsho | |||||
| 63 | d.snapshots = append(d.snapshots, snapshot) | 64 | d.snapshots = append(d.snapshots, snapshot) | ||
| 64 | d.mu.Unlock() | 65 | d.mu.Unlock() | ||
| 65 | 66 | ||||
| 66 | - | log.Printf("[pane-close] snapshot %s\n%s", snapshot.Label, strings.Join(snapshot.Lines, "\n")) | 67 | + | logging.Info(fmt.Sprintf("[pane-close] snapshot %s\n%s", snapshot.Label, strings.Join(snapshot.Lines, "\n"))) |
| 67 | return snapshot | 68 | return snapshot | ||
| 68 | } | 69 | } | ||
| 69 | 70 | ||||
M internal/app/browser/workspace_geometry_validator.go +4 -5
| @@ -3,8 +3,7 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | 6 | + | "github.com/bnema/dumber/internal/logging" |
| 7 | - | ||||
| 8 | "github.com/bnema/dumber/pkg/webkit" | 7 | "github.com/bnema/dumber/pkg/webkit" | ||
| 9 | ) | 8 | ) | ||
| 10 | 9 | ||||
| @@ -52,7 +51,7 @@ func (gv *GeometryValidator) GetPaneGeometry(node *paneNode) PaneGeometry { | |||||
| 52 | 51 | ||||
| 53 | if node == nil || node.container == nil { | 52 | if node == nil || node.container == nil { | ||
| 54 | if gv.debugMode { | 53 | if gv.debugMode { | ||
| 55 | - | log.Printf("[geometry] Invalid node or missing container") | 54 | + | logging.Error(fmt.Sprintf("[geometry] Invalid node or missing container")) |
| 56 | } | 55 | } | ||
| 57 | return geometry | 56 | return geometry | ||
| 58 | } | 57 | } | ||
| @@ -67,8 +66,8 @@ func (gv *GeometryValidator) GetPaneGeometry(node *paneNode) PaneGeometry { | |||||
| 67 | geometry.IsValid = true | 66 | geometry.IsValid = true | ||
| 68 | 67 | ||||
| 69 | if gv.debugMode { | 68 | if gv.debugMode { | ||
| 70 | - | log.Printf("[geometry] Pane geometry: %dx%d at (%d,%d)", | 69 | + | logging.Info(fmt.Sprintf("[geometry] Pane geometry: %dx%d at (%d,%d)", |
| 71 | - | geometry.Width, geometry.Height, geometry.X, geometry.Y) | 70 | + | geometry.Width, geometry.Height, geometry.X, geometry.Y)) |
| 72 | } | 71 | } | ||
| 73 | 72 | ||||
| 74 | return geometry | 73 | return geometry | ||
M internal/app/browser/workspace_hover.go +6 -5
| @@ -2,8 +2,9 @@ | |||||
| 2 | package browser | 2 | package browser | ||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | "github.com/bnema/dumber/pkg/webkit" | 8 | "github.com/bnema/dumber/pkg/webkit" | ||
| 8 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 9 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| 9 | ) | 10 | ) | ||
| @@ -43,9 +44,9 @@ func (wm *WorkspaceManager) ensureHover(node *paneNode) { | |||||
| 43 | 44 | ||||
| 44 | if controller != nil { | 45 | if controller != nil { | ||
| 45 | node.hoverToken = controller | 46 | node.hoverToken = controller | ||
| 46 | - | log.Printf("[workspace] Attached hover handler to pane %p (controller=%p)", node, controller) | 47 | + | logging.Debug(fmt.Sprintf("[workspace] Attached hover handler to pane %p (controller=%p)", node, controller)) |
| 47 | } else { | 48 | } else { | ||
| 48 | - | log.Printf("[workspace] Failed to attach hover handler to pane %p", node) | 49 | + | logging.Error(fmt.Sprintf("[workspace] Failed to attach hover handler to pane %p", node)) |
| 49 | } | 50 | } | ||
| 50 | } | 51 | } | ||
| 51 | 52 | ||||
| @@ -61,7 +62,7 @@ func (wm *WorkspaceManager) detachHover(node *paneNode) { | |||||
| 61 | } | 62 | } | ||
| 62 | 63 | ||||
| 63 | webkit.WidgetRemoveHoverHandler(node.container, node.hoverToken) | 64 | webkit.WidgetRemoveHoverHandler(node.container, node.hoverToken) | ||
| 64 | - | log.Printf("[workspace] Detached hover handler from pane %p (controller=%p)", node, node.hoverToken) | 65 | + | logging.Debug(fmt.Sprintf("[workspace] Detached hover handler from pane %p (controller=%p)", node, node.hoverToken)) |
| 65 | node.hoverToken = nil | 66 | node.hoverToken = nil | ||
| 66 | } | 67 | } | ||
| 67 | 68 | ||||
| @@ -76,5 +77,5 @@ func (wm *WorkspaceManager) attachHoverHandlersToAllPanes() { | |||||
| 76 | wm.ensureHover(leaf) | 77 | wm.ensureHover(leaf) | ||
| 77 | } | 78 | } | ||
| 78 | 79 | ||||
| 79 | - | log.Printf("[workspace] Attached hover handlers to %d panes", len(leaves)) | 80 | + | logging.Debug(fmt.Sprintf("[workspace] Attached hover handlers to %d panes", len(leaves))) |
| 80 | } | 81 | } | ||
M internal/app/browser/workspace_manager.go +39 -39
| @@ -4,13 +4,13 @@ package browser | |||||
| 4 | import ( | 4 | import ( | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "strings" | 7 | "strings" | ||
| 9 | "sync" | 8 | "sync" | ||
| 10 | "sync/atomic" | 9 | "sync/atomic" | ||
| 11 | "time" | 10 | "time" | ||
| 12 | 11 | ||||
| 13 | "github.com/bnema/dumber/internal/app/messaging" | 12 | "github.com/bnema/dumber/internal/app/messaging" | ||
| 13 | + | "github.com/bnema/dumber/internal/logging" | |||
| 14 | "github.com/bnema/dumber/pkg/webkit" | 14 | "github.com/bnema/dumber/pkg/webkit" | ||
| 15 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 15 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| 16 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 16 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| @@ -208,7 +208,7 @@ func NewWorkspaceManager(app *BrowserApp, rootPane *BrowserPane) *WorkspaceManag | |||||
| 208 | 208 | ||||
| 209 | // Initialize focus state machine after all setup is complete | 209 | // Initialize focus state machine after all setup is complete | ||
| 210 | if err := manager.focusStateMachine.Initialize(); err != nil { | 210 | if err := manager.focusStateMachine.Initialize(); err != nil { | ||
| 211 | - | log.Printf("[workspace] Failed to initialize focus state machine: %v", err) | 211 | + | logging.Error(fmt.Sprintf("[workspace] Failed to initialize focus state machine: %v", err)) |
| 212 | } | 212 | } | ||
| 213 | 213 | ||||
| 214 | // Ensure hover controllers are attached to all panes for mouse-driven focus changes | 214 | // Ensure hover controllers are attached to all panes for mouse-driven focus changes | ||
| @@ -225,7 +225,7 @@ func NewWorkspaceManager(app *BrowserApp, rootPane *BrowserPane) *WorkspaceManag | |||||
| 225 | func (wm *WorkspaceManager) shouldDebounce(source *webkit.WebView, threshold time.Duration) bool { | 225 | func (wm *WorkspaceManager) shouldDebounce(source *webkit.WebView, threshold time.Duration) bool { | ||
| 226 | if last, ok := wm.lastSplitMsg[source]; ok { | 226 | if last, ok := wm.lastSplitMsg[source]; ok { | ||
| 227 | if elapsed := time.Since(last); elapsed < threshold { | 227 | if elapsed := time.Since(last); elapsed < threshold { | ||
| 228 | - | log.Printf("[workspace] operation debounced: %.0fms", elapsed.Seconds()*1000) | 228 | + | logging.Debug(fmt.Sprintf("[workspace] operation debounced: %.0fms", elapsed.Seconds()*1000)) |
| 229 | return true | 229 | return true | ||
| 230 | } | 230 | } | ||
| 231 | } | 231 | } | ||
| @@ -236,14 +236,14 @@ func (wm *WorkspaceManager) shouldDebounce(source *webkit.WebView, threshold tim | |||||
| 236 | // withSplitLock executes an operation with atomic splitting lock protection | 236 | // withSplitLock executes an operation with atomic splitting lock protection | ||
| 237 | func (wm *WorkspaceManager) withSplitLock(operation string, fn func() error) error { | 237 | func (wm *WorkspaceManager) withSplitLock(operation string, fn func() error) error { | ||
| 238 | if !atomic.CompareAndSwapInt32(&wm.splitting, 0, 1) { | 238 | if !atomic.CompareAndSwapInt32(&wm.splitting, 0, 1) { | ||
| 239 | - | log.Printf("[workspace] %s ignored: operation in progress", operation) | 239 | + | logging.Debug(fmt.Sprintf("[workspace] %s ignored: operation in progress", operation)) |
| 240 | return fmt.Errorf("operation in progress") | 240 | return fmt.Errorf("operation in progress") | ||
| 241 | } | 241 | } | ||
| 242 | 242 | ||||
| 243 | defer func() { | 243 | defer func() { | ||
| 244 | atomic.StoreInt32(&wm.splitting, 0) | 244 | atomic.StoreInt32(&wm.splitting, 0) | ||
| 245 | if r := recover(); r != nil { | 245 | if r := recover(); r != nil { | ||
| 246 | - | log.Printf("[workspace] %s panicked: %v", operation, r) | 246 | + | logging.Error(fmt.Sprintf("[workspace] %s panicked: %v", operation, r)) |
| 247 | panic(r) | 247 | panic(r) | ||
| 248 | } | 248 | } | ||
| 249 | }() | 249 | }() | ||
| @@ -272,9 +272,9 @@ func (wm *WorkspaceManager) cleanupPopupStorage(parentNode, targetNode *paneNode | |||||
| 272 | `, parentWebViewID, requestID, requestID, requestID, requestID, webviewID) | 272 | `, parentWebViewID, requestID, requestID, requestID, requestID, webviewID) | ||
| 273 | 273 | ||||
| 274 | if err := parentNode.pane.WebView().InjectScript(script); err != nil { | 274 | if err := parentNode.pane.WebView().InjectScript(script); err != nil { | ||
| 275 | - | log.Printf("[workspace] localStorage cleanup failed: %v", err) | 275 | + | logging.Error(fmt.Sprintf("[workspace] localStorage cleanup failed: %v", err)) |
| 276 | } else { | 276 | } else { | ||
| 277 | - | log.Printf("[workspace] Cleaned localStorage for popup requestId=%s webviewId=%s", requestID, webviewID) | 277 | + | logging.Debug(fmt.Sprintf("[workspace] Cleaned localStorage for popup requestId=%s webviewId=%s", requestID, webviewID)) |
| 278 | } | 278 | } | ||
| 279 | } | 279 | } | ||
| 280 | 280 | ||||
| @@ -292,7 +292,7 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 292 | } | 292 | } | ||
| 293 | 293 | ||||
| 294 | if node == nil { | 294 | if node == nil { | ||
| 295 | - | log.Printf("[workspace] message from unknown webview: event=%s webviewId=%s", msg.Event, msg.WebViewID) | 295 | + | logging.Warn(fmt.Sprintf("[workspace] message from unknown webview: event=%s webviewId=%s", msg.Event, msg.WebViewID)) |
| 296 | return | 296 | return | ||
| 297 | } | 297 | } | ||
| 298 | 298 | ||||
| @@ -321,7 +321,7 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 321 | }) | 321 | }) | ||
| 322 | 322 | ||||
| 323 | if err != nil { | 323 | if err != nil { | ||
| 324 | - | log.Printf("[workspace] split failed: %v", err) | 324 | + | logging.Error(fmt.Sprintf("[workspace] split failed: %v", err)) |
| 325 | } | 325 | } | ||
| 326 | 326 | ||||
| 327 | case "pane-stack": | 327 | case "pane-stack": | ||
| @@ -344,12 +344,12 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 344 | }) | 344 | }) | ||
| 345 | 345 | ||||
| 346 | if err != nil { | 346 | if err != nil { | ||
| 347 | - | log.Printf("[workspace] stack failed: %v", err) | 347 | + | logging.Error(fmt.Sprintf("[workspace] stack failed: %v", err)) |
| 348 | } | 348 | } | ||
| 349 | 349 | ||||
| 350 | case "close-popup": | 350 | case "close-popup": | ||
| 351 | if msg.WebViewID == "" { | 351 | if msg.WebViewID == "" { | ||
| 352 | - | log.Printf("[workspace] close-popup: empty webviewId, ignoring") | 352 | + | logging.Warn(fmt.Sprintf("[workspace] close-popup: empty webviewId, ignoring")) |
| 353 | return | 353 | return | ||
| 354 | } | 354 | } | ||
| 355 | 355 | ||||
| @@ -363,16 +363,16 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 363 | } | 363 | } | ||
| 364 | 364 | ||||
| 365 | if targetNode == nil { | 365 | if targetNode == nil { | ||
| 366 | - | log.Printf("[workspace] close-popup: webview not found: %s", msg.WebViewID) | 366 | + | logging.Warn(fmt.Sprintf("[workspace] close-popup: webview not found: %s", msg.WebViewID)) |
| 367 | return | 367 | return | ||
| 368 | } | 368 | } | ||
| 369 | 369 | ||||
| 370 | if !targetNode.isPopup { | 370 | if !targetNode.isPopup { | ||
| 371 | - | log.Printf("[workspace] close-popup: target is not a popup: %s", msg.WebViewID) | 371 | + | logging.Warn(fmt.Sprintf("[workspace] close-popup: target is not a popup: %s", msg.WebViewID)) |
| 372 | return | 372 | return | ||
| 373 | } | 373 | } | ||
| 374 | 374 | ||||
| 375 | - | log.Printf("[workspace] Closing popup due to %s", msg.Reason) | 375 | + | logging.Info(fmt.Sprintf("[workspace] Closing popup due to %s", msg.Reason)) |
| 376 | 376 | ||||
| 377 | // Remove from parent's active popup list | 377 | // Remove from parent's active popup list | ||
| 378 | if targetNode.parentPane != nil { | 378 | if targetNode.parentPane != nil { | ||
| @@ -380,7 +380,7 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 380 | for i, childID := range parentNode.activePopupChildren { | 380 | for i, childID := range parentNode.activePopupChildren { | ||
| 381 | if childID == msg.WebViewID { | 381 | if childID == msg.WebViewID { | ||
| 382 | parentNode.activePopupChildren = append(parentNode.activePopupChildren[:i], parentNode.activePopupChildren[i+1:]...) | 382 | parentNode.activePopupChildren = append(parentNode.activePopupChildren[:i], parentNode.activePopupChildren[i+1:]...) | ||
| 383 | - | log.Printf("[workspace] Removed popup from parent (remaining: %d)", len(parentNode.activePopupChildren)) | 383 | + | logging.Debug(fmt.Sprintf("[workspace] Removed popup from parent (remaining: %d)", len(parentNode.activePopupChildren))) |
| 384 | break | 384 | break | ||
| 385 | } | 385 | } | ||
| 386 | } | 386 | } | ||
| @@ -389,11 +389,11 @@ func (wm *WorkspaceManager) OnWorkspaceMessage(source *webkit.WebView, msg messa | |||||
| 389 | } | 389 | } | ||
| 390 | 390 | ||||
| 391 | if err := wm.ClosePane(targetNode); err != nil { | 391 | if err := wm.ClosePane(targetNode); err != nil { | ||
| 392 | - | log.Printf("[workspace] Failed to close popup: %v", err) | 392 | + | logging.Error(fmt.Sprintf("[workspace] Failed to close popup: %v", err)) |
| 393 | } | 393 | } | ||
| 394 | 394 | ||||
| 395 | default: | 395 | default: | ||
| 396 | - | log.Printf("[workspace] unhandled workspace event: %s", msg.Event) | 396 | + | logging.Warn(fmt.Sprintf("[workspace] unhandled workspace event: %s", msg.Event)) |
| 397 | } | 397 | } | ||
| 398 | } | 398 | } | ||
| 399 | 399 | ||||
| @@ -433,7 +433,7 @@ func (wm *WorkspaceManager) SetActivePane(node *paneNode, source FocusSource) { | |||||
| 433 | wm.lastFocusTarget = node | 433 | wm.lastFocusTarget = node | ||
| 434 | 434 | ||||
| 435 | if err := wm.focusStateMachine.RequestFocus(node, source); err != nil { | 435 | if err := wm.focusStateMachine.RequestFocus(node, source); err != nil { | ||
| 436 | - | log.Printf("[workspace] Focus request failed: %v", err) | 436 | + | logging.Error(fmt.Sprintf("[workspace] Focus request failed: %v", err)) |
| 437 | } | 437 | } | ||
| 438 | } | 438 | } | ||
| 439 | 439 | ||||
| @@ -516,12 +516,12 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 516 | return false | 516 | return false | ||
| 517 | } | 517 | } | ||
| 518 | 518 | ||||
| 519 | - | log.Printf("[workspace] Middle-click/Ctrl+click on link, opening in new pane: %s", linkURL) | 519 | + | logging.Info(fmt.Sprintf("[workspace] Middle-click/Ctrl+click on link, opening in new pane: %s", linkURL)) |
| 520 | 520 | ||||
| 521 | // Get the node for this webview | 521 | // Get the node for this webview | ||
| 522 | node := wm.GetNodeForWebView(webView) | 522 | node := wm.GetNodeForWebView(webView) | ||
| 523 | if node == nil { | 523 | if node == nil { | ||
| 524 | - | log.Printf("[workspace] Cannot find node for middle-clicked link") | 524 | + | logging.Error(fmt.Sprintf("[workspace] Cannot find node for middle-clicked link")) |
| 525 | return false | 525 | return false | ||
| 526 | } | 526 | } | ||
| 527 | 527 | ||||
| @@ -537,18 +537,18 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 537 | switch behavior { | 537 | switch behavior { | ||
| 538 | case "stacked": | 538 | case "stacked": | ||
| 539 | // Stack the current pane - this also creates a new pane in the stack | 539 | // Stack the current pane - this also creates a new pane in the stack | ||
| 540 | - | log.Printf("[workspace] Using stacked behavior for gesture link") | 540 | + | logging.Debug(fmt.Sprintf("[workspace] Using stacked behavior for gesture link")) |
| 541 | newNode, err = wm.stackedPaneManager.StackPane(node) | 541 | newNode, err = wm.stackedPaneManager.StackPane(node) | ||
| 542 | if err != nil { | 542 | if err != nil { | ||
| 543 | - | log.Printf("[workspace] Failed to stack pane for gesture link: %v", err) | 543 | + | logging.Error(fmt.Sprintf("[workspace] Failed to stack pane for gesture link: %v", err)) |
| 544 | return false | 544 | return false | ||
| 545 | } | 545 | } | ||
| 546 | // newNode is the new pane that was added to the stack | 546 | // newNode is the new pane that was added to the stack | ||
| 547 | - | log.Printf("[workspace] StackPane created new pane in stack: %p", newNode) | 547 | + | logging.Debug(fmt.Sprintf("[workspace] StackPane created new pane in stack: %p", newNode)) |
| 548 | 548 | ||||
| 549 | case "split": | 549 | case "split": | ||
| 550 | // Regular split behavior | 550 | // Regular split behavior | ||
| 551 | - | log.Printf("[workspace] Using split behavior for gesture link") | 551 | + | logging.Debug(fmt.Sprintf("[workspace] Using split behavior for gesture link")) |
| 552 | direction := "right" | 552 | direction := "right" | ||
| 553 | if wm.app.config.Workspace.Popups.Placement != "" { | 553 | if wm.app.config.Workspace.Popups.Placement != "" { | ||
| 554 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 554 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| @@ -557,7 +557,7 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 557 | 557 | ||||
| 558 | case "tabbed": | 558 | case "tabbed": | ||
| 559 | // Tabbed not yet implemented, fall back to split | 559 | // Tabbed not yet implemented, fall back to split | ||
| 560 | - | log.Printf("[workspace] WARNING: Tabbed behavior not yet implemented for gesture links, falling back to split") | 560 | + | logging.Warn(fmt.Sprintf("[workspace] WARNING: Tabbed behavior not yet implemented for gesture links, falling back to split")) |
| 561 | direction := "right" | 561 | direction := "right" | ||
| 562 | if wm.app.config.Workspace.Popups.Placement != "" { | 562 | if wm.app.config.Workspace.Popups.Placement != "" { | ||
| 563 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 563 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| @@ -566,7 +566,7 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 566 | 566 | ||||
| 567 | default: | 567 | default: | ||
| 568 | // Unknown behavior, fall back to split | 568 | // Unknown behavior, fall back to split | ||
| 569 | - | log.Printf("[workspace] WARNING: Unknown behavior '%s' for gesture links, falling back to split", behavior) | 569 | + | logging.Warn(fmt.Sprintf("[workspace] WARNING: Unknown behavior '%s' for gesture links, falling back to split", behavior)) |
| 570 | direction := "right" | 570 | direction := "right" | ||
| 571 | if wm.app.config.Workspace.Popups.Placement != "" { | 571 | if wm.app.config.Workspace.Popups.Placement != "" { | ||
| 572 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 572 | direction = strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| @@ -575,14 +575,14 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 575 | } | 575 | } | ||
| 576 | 576 | ||||
| 577 | if err != nil { | 577 | if err != nil { | ||
| 578 | - | log.Printf("[workspace] Failed to handle gesture link: %v", err) | 578 | + | logging.Error(fmt.Sprintf("[workspace] Failed to handle gesture link: %v", err)) |
| 579 | return false | 579 | return false | ||
| 580 | } | 580 | } | ||
| 581 | 581 | ||||
| 582 | // Navigate the new pane to the URL | 582 | // Navigate the new pane to the URL | ||
| 583 | if newNode != nil && newNode.pane != nil && newNode.pane.WebView() != nil { | 583 | if newNode != nil && newNode.pane != nil && newNode.pane.WebView() != nil { | ||
| 584 | if err := newNode.pane.WebView().LoadURL(linkURL); err != nil { | 584 | if err := newNode.pane.WebView().LoadURL(linkURL); err != nil { | ||
| 585 | - | log.Printf("[workspace] Failed to load URL in new pane: %v", err) | 585 | + | logging.Error(fmt.Sprintf("[workspace] Failed to load URL in new pane: %v", err)) |
| 586 | return false | 586 | return false | ||
| 587 | } | 587 | } | ||
| 588 | 588 | ||||
| @@ -593,7 +593,7 @@ func (wm *WorkspaceManager) RegisterNavigationHandler(webView *webkit.WebView) { | |||||
| 593 | return true // Indicate we handled the click | 593 | return true // Indicate we handled the click | ||
| 594 | }) | 594 | }) | ||
| 595 | 595 | ||||
| 596 | - | log.Printf("[workspace] Registered navigation handler for webview: %d", webView.ID()) | 596 | + | logging.Debug(fmt.Sprintf("[workspace] Registered navigation handler for webview: %d", webView.ID())) |
| 597 | } | 597 | } | ||
| 598 | 598 | ||||
| 599 | // GetAllPanes returns all BrowserPanes in this workspace. | 599 | // GetAllPanes returns all BrowserPanes in this workspace. | ||
| @@ -621,7 +621,7 @@ func (wm *WorkspaceManager) GetActivePane() *BrowserPane { | |||||
| 621 | // RestoreFocus restores focus to the active pane in this workspace. | 621 | // RestoreFocus restores focus to the active pane in this workspace. | ||
| 622 | // Called when switching to a tab to ensure the correct pane has focus. | 622 | // Called when switching to a tab to ensure the correct pane has focus. | ||
| 623 | func (wm *WorkspaceManager) RestoreFocus() { | 623 | func (wm *WorkspaceManager) RestoreFocus() { | ||
| 624 | - | log.Printf("[workspace] RestoreFocus: workspace %p", wm) | 624 | + | logging.Debug(fmt.Sprintf("[workspace] RestoreFocus: workspace %p", wm)) |
| 625 | 625 | ||||
| 626 | // Get the active pane and explicitly grab GTK focus | 626 | // Get the active pane and explicitly grab GTK focus | ||
| 627 | activeNode := wm.GetActiveNode() | 627 | activeNode := wm.GetActiveNode() | ||
| @@ -630,7 +630,7 @@ func (wm *WorkspaceManager) RestoreFocus() { | |||||
| 630 | // This ensures keyboard events go to the active tab's WebView | 630 | // This ensures keyboard events go to the active tab's WebView | ||
| 631 | // Note: RestoreFocus is called from switchToTabInternal which is already on the main thread | 631 | // Note: RestoreFocus is called from switchToTabInternal which is already on the main thread | ||
| 632 | webkit.WidgetGrabFocus(activeNode.pane.webView.AsWidget()) | 632 | webkit.WidgetGrabFocus(activeNode.pane.webView.AsWidget()) | ||
| 633 | - | log.Printf("[workspace] Grabbed GTK focus for WebView in workspace %p", wm) | 633 | + | logging.Debug(fmt.Sprintf("[workspace] Grabbed GTK focus for WebView in workspace %p", wm)) |
| 634 | 634 | ||||
| 635 | // Also request focus via FSM | 635 | // Also request focus via FSM | ||
| 636 | if wm.focusStateMachine != nil { | 636 | if wm.focusStateMachine != nil { | ||
| @@ -647,14 +647,14 @@ func (wm *WorkspaceManager) IsInActiveTab() bool { | |||||
| 647 | } | 647 | } | ||
| 648 | // The active workspace is the one currently assigned to app.workspace | 648 | // The active workspace is the one currently assigned to app.workspace | ||
| 649 | isActive := wm.app.workspace == wm | 649 | isActive := wm.app.workspace == wm | ||
| 650 | - | log.Printf("[workspace] IsInActiveTab check: workspace %p, app.workspace %p, isActive=%v", wm, wm.app.workspace, isActive) | 650 | + | logging.Debug(fmt.Sprintf("[workspace] IsInActiveTab check: workspace %p, app.workspace %p, isActive=%v", wm, wm.app.workspace, isActive)) |
| 651 | return isActive | 651 | return isActive | ||
| 652 | } | 652 | } | ||
| 653 | 653 | ||||
| 654 | // ClearFocus removes GTK focus from all panes in this workspace. | 654 | // ClearFocus removes GTK focus from all panes in this workspace. | ||
| 655 | // This prevents inactive tab's WebViews from catching keyboard events. | 655 | // This prevents inactive tab's WebViews from catching keyboard events. | ||
| 656 | func (wm *WorkspaceManager) ClearFocus() { | 656 | func (wm *WorkspaceManager) ClearFocus() { | ||
| 657 | - | log.Printf("[workspace] ClearFocus: workspace %p", wm) | 657 | + | logging.Debug(fmt.Sprintf("[workspace] ClearFocus: workspace %p", wm)) |
| 658 | // No state to clear - IsInActiveTab() checks dynamically | 658 | // No state to clear - IsInActiveTab() checks dynamically | ||
| 659 | } | 659 | } | ||
| 660 | 660 | ||||
| @@ -675,19 +675,19 @@ func (wm *WorkspaceManager) setRootContainer(container gtk.Widgetter) { | |||||
| 675 | } | 675 | } | ||
| 676 | 676 | ||||
| 677 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | 677 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | ||
| 678 | - | log.Printf("[workspace] ERROR: Cannot set root - tab manager not initialized") | 678 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: Cannot set root - tab manager not initialized")) |
| 679 | return | 679 | return | ||
| 680 | } | 680 | } | ||
| 681 | 681 | ||||
| 682 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | 682 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | ||
| 683 | if !ok || contentBox == nil { | 683 | if !ok || contentBox == nil { | ||
| 684 | - | log.Printf("[workspace] ERROR: ContentArea is not a Box") | 684 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: ContentArea is not a Box")) |
| 685 | return | 685 | return | ||
| 686 | } | 686 | } | ||
| 687 | 687 | ||||
| 688 | contentBox.Append(container) | 688 | contentBox.Append(container) | ||
| 689 | webkit.WidgetSetVisible(container, true) | 689 | webkit.WidgetSetVisible(container, true) | ||
| 690 | - | log.Printf("[workspace] Attached root container %p to ContentArea", container) | 690 | + | logging.Debug(fmt.Sprintf("[workspace] Attached root container %p to ContentArea", container)) |
| 691 | } | 691 | } | ||
| 692 | 692 | ||||
| 693 | // clearRootContainer removes the current workspace root from TabManager.ContentArea. | 693 | // clearRootContainer removes the current workspace root from TabManager.ContentArea. | ||
| @@ -698,16 +698,16 @@ func (wm *WorkspaceManager) clearRootContainer() { | |||||
| 698 | } | 698 | } | ||
| 699 | 699 | ||||
| 700 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | 700 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | ||
| 701 | - | log.Printf("[workspace] ERROR: Cannot clear root - tab manager not initialized") | 701 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: Cannot clear root - tab manager not initialized")) |
| 702 | return | 702 | return | ||
| 703 | } | 703 | } | ||
| 704 | 704 | ||||
| 705 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | 705 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | ||
| 706 | if !ok || contentBox == nil { | 706 | if !ok || contentBox == nil { | ||
| 707 | - | log.Printf("[workspace] ERROR: ContentArea is not a Box") | 707 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: ContentArea is not a Box")) |
| 708 | return | 708 | return | ||
| 709 | } | 709 | } | ||
| 710 | 710 | ||||
| 711 | contentBox.Remove(wm.root.container) | 711 | contentBox.Remove(wm.root.container) | ||
| 712 | - | log.Printf("[workspace] Removed root container %p from ContentArea", wm.root.container) | 712 | + | logging.Debug(fmt.Sprintf("[workspace] Removed root container %p from ContentArea", wm.root.container)) |
| 713 | } | 713 | } | ||
M internal/app/browser/workspace_navigation.go +30 -30
| @@ -3,11 +3,11 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "math" | 6 | "math" | ||
| 8 | "strings" | 7 | "strings" | ||
| 9 | "time" | 8 | "time" | ||
| 10 | 9 | ||||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |||
| 11 | "github.com/bnema/dumber/pkg/webkit" | 11 | "github.com/bnema/dumber/pkg/webkit" | ||
| 12 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 12 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 13 | ) | 13 | ) | ||
| @@ -32,9 +32,9 @@ func (wm *WorkspaceManager) DispatchPaneFocusEvent(node *paneNode, active bool) | |||||
| 32 | } | 32 | } | ||
| 33 | 33 | ||||
| 34 | if err := node.pane.webView.DispatchCustomEvent("dumber:workspace-focus", detail); err != nil { | 34 | if err := node.pane.webView.DispatchCustomEvent("dumber:workspace-focus", detail); err != nil { | ||
| 35 | - | log.Printf("[workspace] failed to dispatch focus event: %v", err) | 35 | + | logging.Error(fmt.Sprintf("[workspace] failed to dispatch focus event: %v", err)) |
| 36 | } else if wm.app.config != nil && wm.app.config.Debug.EnableWorkspaceDebug { | 36 | } else if wm.app.config != nil && wm.app.config.Debug.EnableWorkspaceDebug { | ||
| 37 | - | log.Printf("[workspace] dispatched focus event for webview %d (active=%v)", node.pane.webView.ID(), active) | 37 | + | logging.Debug(fmt.Sprintf("[workspace] dispatched focus event for webview %d (active=%v)", node.pane.webView.ID(), active)) |
| 38 | } | 38 | } | ||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| @@ -56,7 +56,7 @@ func (wm *WorkspaceManager) focusRespectingStack(node *paneNode, reason string) | |||||
| 56 | activePane := stack.stackedPanes[activeIndex] | 56 | activePane := stack.stackedPanes[activeIndex] | ||
| 57 | if activePane != nil && activePane != node { | 57 | if activePane != nil && activePane != node { | ||
| 58 | if reason != "" { | 58 | if reason != "" { | ||
| 59 | - | log.Printf("[workspace] %s: preserving active stacked pane index=%d", reason, activeIndex) | 59 | + | logging.Info(fmt.Sprintf("[workspace] %s: preserving active stacked pane index=%d", reason, activeIndex)) |
| 60 | } | 60 | } | ||
| 61 | wm.SetActivePane(activePane, SourceKeyboard) | 61 | wm.SetActivePane(activePane, SourceKeyboard) | ||
| 62 | return | 62 | return | ||
| @@ -122,7 +122,7 @@ func (wm *WorkspaceManager) FocusNeighbor(direction string) bool { | |||||
| 122 | case DirectionLeft, DirectionRight: | 122 | case DirectionLeft, DirectionRight: | ||
| 123 | return wm.focusAdjacent(strings.ToLower(direction)) | 123 | return wm.focusAdjacent(strings.ToLower(direction)) | ||
| 124 | default: | 124 | default: | ||
| 125 | - | log.Printf("[workspace] invalid focus direction: %s (expected: up, down, left, right)", direction) | 125 | + | logging.Error(fmt.Sprintf("[workspace] invalid focus direction: %s (expected: up, down, left, right)", direction)) |
| 126 | return false | 126 | return false | ||
| 127 | } | 127 | } | ||
| 128 | } | 128 | } | ||
| @@ -162,7 +162,7 @@ func (wm *WorkspaceManager) navigateStack(direction string) bool { | |||||
| 162 | } | 162 | } | ||
| 163 | 163 | ||||
| 164 | if currentIndex == -1 { | 164 | if currentIndex == -1 { | ||
| 165 | - | log.Printf("[workspace] navigateStack: current pane not found in stack") | 165 | + | logging.Error(fmt.Sprintf("[workspace] navigateStack: current pane not found in stack")) |
| 166 | return false | 166 | return false | ||
| 167 | } | 167 | } | ||
| 168 | 168 | ||||
| @@ -195,8 +195,8 @@ func (wm *WorkspaceManager) navigateStack(direction string) bool { | |||||
| 195 | newActivePane := stackNode.stackedPanes[newIndex] | 195 | newActivePane := stackNode.stackedPanes[newIndex] | ||
| 196 | wm.SetActivePane(newActivePane, SourceStackNav) | 196 | wm.SetActivePane(newActivePane, SourceStackNav) | ||
| 197 | 197 | ||||
| 198 | - | log.Printf("[workspace] navigated stack: direction=%s from=%d to=%d stackSize=%d", | 198 | + | logging.Debug(fmt.Sprintf("[workspace] navigated stack: direction=%s from=%d to=%d stackSize=%d", |
| 199 | - | direction, currentIndex, newIndex, len(stackNode.stackedPanes)) | 199 | + | direction, currentIndex, newIndex, len(stackNode.stackedPanes))) |
| 200 | return true | 200 | return true | ||
| 201 | } | 201 | } | ||
| 202 | 202 | ||||
| @@ -208,7 +208,7 @@ func (wm *WorkspaceManager) focusAdjacent(direction string) bool { | |||||
| 208 | } | 208 | } | ||
| 209 | 209 | ||||
| 210 | if neighbor := wm.structuralNeighbor(currentFocused, direction); neighbor != nil { | 210 | if neighbor := wm.structuralNeighbor(currentFocused, direction); neighbor != nil { | ||
| 211 | - | log.Printf("[workspace] focusAdjacent: using structural neighbor for direction=%s", direction) | 211 | + | logging.Debug(fmt.Sprintf("[workspace] focusAdjacent: using structural neighbor for direction=%s", direction)) |
| 212 | wm.SetActivePane(neighbor, SourceKeyboard) | 212 | wm.SetActivePane(neighbor, SourceKeyboard) | ||
| 213 | return true | 213 | return true | ||
| 214 | } | 214 | } | ||
| @@ -216,7 +216,7 @@ func (wm *WorkspaceManager) focusAdjacent(direction string) bool { | |||||
| 216 | curX, curY, curWidth, curHeight := wm.getNavigationAllocation(currentFocused) | 216 | curX, curY, curWidth, curHeight := wm.getNavigationAllocation(currentFocused) | ||
| 217 | cx := float64(curX) + float64(curWidth)/2.0 | 217 | cx := float64(curX) + float64(curWidth)/2.0 | ||
| 218 | cy := float64(curY) + float64(curHeight)/2.0 | 218 | cy := float64(curY) + float64(curHeight)/2.0 | ||
| 219 | - | log.Printf("[workspace] focusAdjacent: current pane center=(%.0f, %.0f) direction=%s", cx, cy, direction) | 219 | + | logging.Debug(fmt.Sprintf("[workspace] focusAdjacent: current pane center=(%.0f, %.0f) direction=%s", cx, cy, direction)) |
| 220 | 220 | ||||
| 221 | leaves := wm.collectLeaves() | 221 | leaves := wm.collectLeaves() | ||
| 222 | bestScore := math.MaxFloat64 | 222 | bestScore := math.MaxFloat64 | ||
| @@ -271,13 +271,13 @@ func (wm *WorkspaceManager) focusAdjacent(direction string) bool { | |||||
| 271 | 271 | ||||
| 272 | if best != nil { | 272 | if best != nil { | ||
| 273 | bx, by, _, _ := webkit.WidgetGetAllocation(best.container) | 273 | bx, by, _, _ := webkit.WidgetGetAllocation(best.container) | ||
| 274 | - | log.Printf("[workspace] focusAdjacent: found best candidate at pos=(%d,%d) for direction=%s", bx, by, direction) | 274 | + | logging.Debug(fmt.Sprintf("[workspace] focusAdjacent: found best candidate at pos=(%d,%d) for direction=%s", bx, by, direction)) |
| 275 | wm.SetActivePane(best, SourceKeyboard) | 275 | wm.SetActivePane(best, SourceKeyboard) | ||
| 276 | return true | 276 | return true | ||
| 277 | } | 277 | } | ||
| 278 | 278 | ||||
| 279 | if len(debugCandidates) > 0 { | 279 | if len(debugCandidates) > 0 { | ||
| 280 | - | log.Printf("[workspace] focusAdjacent: NO candidate found for direction=%s current=%p center=(%.0f,%.0f) candidates=[%s]", direction, currentFocused.container, cx, cy, strings.Join(debugCandidates, "; ")) | 280 | + | logging.Debug(fmt.Sprintf("[workspace] focusAdjacent: NO candidate found for direction=%s current=%p center=(%.0f,%.0f) candidates=[%s]", direction, currentFocused.container, cx, cy, strings.Join(debugCandidates, "; "))) |
| 281 | } | 281 | } | ||
| 282 | return false | 282 | return false | ||
| 283 | } | 283 | } | ||
| @@ -311,14 +311,14 @@ func (wm *WorkspaceManager) structuralNeighbor(node *paneNode, direction string) | |||||
| 311 | cy := float64(refY) + float64(refHeight)/2.0 | 311 | cy := float64(refY) + float64(refHeight)/2.0 | ||
| 312 | axisVertical := direction == DirectionUp || direction == DirectionDown | 312 | axisVertical := direction == DirectionUp || direction == DirectionDown | ||
| 313 | 313 | ||||
| 314 | - | log.Printf("[workspace] structuralNeighbor: node=%p pos=(%d,%d) direction=%s", node.container, refX, refY, direction) | 314 | + | logging.Debug(fmt.Sprintf("[workspace] structuralNeighbor: node=%p pos=(%d,%d) direction=%s", node.container, refX, refY, direction)) |
| 315 | 315 | ||||
| 316 | // Start from the node's parent, but skip stack containers for vertical navigation | 316 | // Start from the node's parent, but skip stack containers for vertical navigation | ||
| 317 | // Stack containers are transparent to vertical navigation - we want to navigate | 317 | // Stack containers are transparent to vertical navigation - we want to navigate | ||
| 318 | // from stack to external panes, not within the stack (that's handled by NavigateStack) | 318 | // from stack to external panes, not within the stack (that's handled by NavigateStack) | ||
| 319 | startParent := node.parent | 319 | startParent := node.parent | ||
| 320 | if startParent != nil && startParent.isStacked && axisVertical { | 320 | if startParent != nil && startParent.isStacked && axisVertical { | ||
| 321 | - | log.Printf("[workspace] structuralNeighbor: skipping stack container parent, using stack's parent instead") | 321 | + | logging.Debug(fmt.Sprintf("[workspace] structuralNeighbor: skipping stack container parent, using stack's parent instead")) |
| 322 | node = startParent // Treat the stack container as the navigation node | 322 | node = startParent // Treat the stack container as the navigation node | ||
| 323 | startParent = startParent.parent | 323 | startParent = startParent.parent | ||
| 324 | } | 324 | } | ||
| @@ -326,27 +326,27 @@ func (wm *WorkspaceManager) structuralNeighbor(node *paneNode, direction string) | |||||
| 326 | for parent := startParent; parent != nil; parent = parent.parent { | 326 | for parent := startParent; parent != nil; parent = parent.parent { | ||
| 327 | isLeft := parent.left == node | 327 | isLeft := parent.left == node | ||
| 328 | isRight := parent.right == node | 328 | isRight := parent.right == node | ||
| 329 | - | log.Printf("[workspace] structuralNeighbor: checking parent orientation=%v isLeft=%v isRight=%v", parent.orientation, isLeft, isRight) | 329 | + | logging.Debug(fmt.Sprintf("[workspace] structuralNeighbor: checking parent orientation=%v isLeft=%v isRight=%v", parent.orientation, isLeft, isRight)) |
| 330 | 330 | ||||
| 331 | switch direction { | 331 | switch direction { | ||
| 332 | case DirectionUp: | 332 | case DirectionUp: | ||
| 333 | if axisVertical && parent.orientation == gtk.OrientationVertical && parent.right == node { | 333 | if axisVertical && parent.orientation == gtk.OrientationVertical && parent.right == node { | ||
| 334 | - | log.Printf("[workspace] structuralNeighbor: DirectionUp - we are RIGHT child, looking in LEFT subtree") | 334 | + | logging.Info(fmt.Sprintf("[workspace] structuralNeighbor: DirectionUp - we are RIGHT child, looking in LEFT subtree")) |
| 335 | if leaf := wm.closestLeafFromSubtree(parent.left, cx, cy, direction); leaf != nil { | 335 | if leaf := wm.closestLeafFromSubtree(parent.left, cx, cy, direction); leaf != nil { | ||
| 336 | lx, ly, _, _ := webkit.WidgetGetAllocation(leaf.container) | 336 | lx, ly, _, _ := webkit.WidgetGetAllocation(leaf.container) | ||
| 337 | - | log.Printf("[workspace] structuralNeighbor: found leaf at pos=(%d,%d)", lx, ly) | 337 | + | logging.Debug(fmt.Sprintf("[workspace] structuralNeighbor: found leaf at pos=(%d,%d)", lx, ly)) |
| 338 | return leaf | 338 | return leaf | ||
| 339 | } | 339 | } | ||
| 340 | } | 340 | } | ||
| 341 | case DirectionDown: | 341 | case DirectionDown: | ||
| 342 | if axisVertical && parent.orientation == gtk.OrientationVertical && parent.left == node { | 342 | if axisVertical && parent.orientation == gtk.OrientationVertical && parent.left == node { | ||
| 343 | - | log.Printf("[workspace] structuralNeighbor: DirectionDown - we are LEFT child, looking in RIGHT subtree") | 343 | + | logging.Info(fmt.Sprintf("[workspace] structuralNeighbor: DirectionDown - we are LEFT child, looking in RIGHT subtree")) |
| 344 | if leaf := wm.closestLeafFromSubtree(parent.right, cx, cy, direction); leaf != nil { | 344 | if leaf := wm.closestLeafFromSubtree(parent.right, cx, cy, direction); leaf != nil { | ||
| 345 | lx, ly, _, _ := wm.getNavigationAllocation(leaf) | 345 | lx, ly, _, _ := wm.getNavigationAllocation(leaf) | ||
| 346 | - | log.Printf("[workspace] structuralNeighbor: found leaf at pos=(%d,%d)", lx, ly) | 346 | + | logging.Debug(fmt.Sprintf("[workspace] structuralNeighbor: found leaf at pos=(%d,%d)", lx, ly)) |
| 347 | return leaf | 347 | return leaf | ||
| 348 | } else { | 348 | } else { | ||
| 349 | - | log.Printf("[workspace] structuralNeighbor: closestLeafFromSubtree returned nil for DirectionDown") | 349 | + | logging.Error(fmt.Sprintf("[workspace] structuralNeighbor: closestLeafFromSubtree returned nil for DirectionDown")) |
| 350 | } | 350 | } | ||
| 351 | } | 351 | } | ||
| 352 | case DirectionLeft: | 352 | case DirectionLeft: | ||
| @@ -370,7 +370,7 @@ func (wm *WorkspaceManager) structuralNeighbor(node *paneNode, direction string) | |||||
| 370 | // closestLeafFromSubtree finds the closest leaf node in a subtree based on direction | 370 | // closestLeafFromSubtree finds the closest leaf node in a subtree based on direction | ||
| 371 | func (wm *WorkspaceManager) closestLeafFromSubtree(node *paneNode, cx, cy float64, direction string) *paneNode { | 371 | func (wm *WorkspaceManager) closestLeafFromSubtree(node *paneNode, cx, cy float64, direction string) *paneNode { | ||
| 372 | leaves := wm.collectLeavesFromWithDirection(node, direction) | 372 | leaves := wm.collectLeavesFromWithDirection(node, direction) | ||
| 373 | - | log.Printf("[workspace] closestLeafFromSubtree: found %d leaves for direction=%s from cx=%.0f cy=%.0f", len(leaves), direction, cx, cy) | 373 | + | logging.Debug(fmt.Sprintf("[workspace] closestLeafFromSubtree: found %d leaves for direction=%s from cx=%.0f cy=%.0f", len(leaves), direction, cx, cy)) |
| 374 | bestScore := math.MaxFloat64 | 374 | bestScore := math.MaxFloat64 | ||
| 375 | var best *paneNode | 375 | var best *paneNode | ||
| 376 | for _, leaf := range leaves { | 376 | for _, leaf := range leaves { | ||
| @@ -383,47 +383,47 @@ func (wm *WorkspaceManager) closestLeafFromSubtree(node *paneNode, cx, cy float6 | |||||
| 383 | ty := float64(y) + float64(height)/2.0 | 383 | ty := float64(y) + float64(height)/2.0 | ||
| 384 | dx := tx - cx | 384 | dx := tx - cx | ||
| 385 | dy := ty - cy | 385 | dy := ty - cy | ||
| 386 | - | log.Printf("[workspace] closestLeafFromSubtree: leaf=%p pos=(%d,%d) center=(%.0f,%.0f) dx=%.0f dy=%.0f", leaf.container, x, y, tx, ty, dx, dy) | 386 | + | logging.Info(fmt.Sprintf("[workspace] closestLeafFromSubtree: leaf=%p pos=(%d,%d) center=(%.0f,%.0f) dx=%.0f dy=%.0f", leaf.container, x, y, tx, ty, dx, dy)) |
| 387 | var score float64 | 387 | var score float64 | ||
| 388 | switch direction { | 388 | switch direction { | ||
| 389 | case DirectionLeft: | 389 | case DirectionLeft: | ||
| 390 | if dx >= -focusEpsilon { | 390 | if dx >= -focusEpsilon { | ||
| 391 | - | log.Printf("[workspace] closestLeafFromSubtree: SKIPPED (dx=%.0f not left)", dx) | 391 | + | logging.Warn(fmt.Sprintf("[workspace] closestLeafFromSubtree: SKIPPED (dx=%.0f not left)", dx)) |
| 392 | continue | 392 | continue | ||
| 393 | } | 393 | } | ||
| 394 | score = math.Abs(dx)*1000 + math.Abs(dy) | 394 | score = math.Abs(dx)*1000 + math.Abs(dy) | ||
| 395 | case DirectionRight: | 395 | case DirectionRight: | ||
| 396 | if dx <= focusEpsilon { | 396 | if dx <= focusEpsilon { | ||
| 397 | - | log.Printf("[workspace] closestLeafFromSubtree: SKIPPED (dx=%.0f not right)", dx) | 397 | + | logging.Warn(fmt.Sprintf("[workspace] closestLeafFromSubtree: SKIPPED (dx=%.0f not right)", dx)) |
| 398 | continue | 398 | continue | ||
| 399 | } | 399 | } | ||
| 400 | score = math.Abs(dx)*1000 + math.Abs(dy) | 400 | score = math.Abs(dx)*1000 + math.Abs(dy) | ||
| 401 | case DirectionUp: | 401 | case DirectionUp: | ||
| 402 | if dy >= -focusEpsilon { | 402 | if dy >= -focusEpsilon { | ||
| 403 | - | log.Printf("[workspace] closestLeafFromSubtree: SKIPPED (dy=%.0f not above)", dy) | 403 | + | logging.Warn(fmt.Sprintf("[workspace] closestLeafFromSubtree: SKIPPED (dy=%.0f not above)", dy)) |
| 404 | continue | 404 | continue | ||
| 405 | } | 405 | } | ||
| 406 | score = math.Abs(dy)*1000 + math.Abs(dx) | 406 | score = math.Abs(dy)*1000 + math.Abs(dx) | ||
| 407 | case DirectionDown: | 407 | case DirectionDown: | ||
| 408 | if dy <= focusEpsilon { | 408 | if dy <= focusEpsilon { | ||
| 409 | - | log.Printf("[workspace] closestLeafFromSubtree: SKIPPED (dy=%.0f not below)", dy) | 409 | + | logging.Warn(fmt.Sprintf("[workspace] closestLeafFromSubtree: SKIPPED (dy=%.0f not below)", dy)) |
| 410 | continue | 410 | continue | ||
| 411 | } | 411 | } | ||
| 412 | score = math.Abs(dy)*1000 + math.Abs(dx) | 412 | score = math.Abs(dy)*1000 + math.Abs(dx) | ||
| 413 | default: | 413 | default: | ||
| 414 | continue | 414 | continue | ||
| 415 | } | 415 | } | ||
| 416 | - | log.Printf("[workspace] closestLeafFromSubtree: ACCEPTED with score=%.0f", score) | 416 | + | logging.Debug(fmt.Sprintf("[workspace] closestLeafFromSubtree: ACCEPTED with score=%.0f", score)) |
| 417 | if score < bestScore { | 417 | if score < bestScore { | ||
| 418 | bestScore = score | 418 | bestScore = score | ||
| 419 | best = leaf | 419 | best = leaf | ||
| 420 | } | 420 | } | ||
| 421 | } | 421 | } | ||
| 422 | if best == nil { | 422 | if best == nil { | ||
| 423 | - | log.Printf("[workspace] closestLeafFromSubtree: no match found, trying boundaryFallback") | 423 | + | logging.Warn(fmt.Sprintf("[workspace] closestLeafFromSubtree: no match found, trying boundaryFallback")) |
| 424 | return wm.boundaryFallback(node, direction) | 424 | return wm.boundaryFallback(node, direction) | ||
| 425 | } | 425 | } | ||
| 426 | - | log.Printf("[workspace] closestLeafFromSubtree: returning best=%p", best.container) | 426 | + | logging.Info(fmt.Sprintf("[workspace] closestLeafFromSubtree: returning best=%p", best.container)) |
| 427 | return best | 427 | return best | ||
| 428 | } | 428 | } | ||
| 429 | 429 | ||||
| @@ -437,7 +437,7 @@ func (wm *WorkspaceManager) boundaryFallbackWithDepth(node *paneNode, direction | |||||
| 437 | // Prevent infinite recursion - max tree depth should be reasonable | 437 | // Prevent infinite recursion - max tree depth should be reasonable | ||
| 438 | const maxDepth = 50 | 438 | const maxDepth = 50 | ||
| 439 | if depth > maxDepth { | 439 | if depth > maxDepth { | ||
| 440 | - | log.Printf("[workspace] boundaryFallback: max depth exceeded, possible tree corruption") | 440 | + | logging.Error(fmt.Sprintf("[workspace] boundaryFallback: max depth exceeded, possible tree corruption")) |
| 441 | return nil | 441 | return nil | ||
| 442 | } | 442 | } | ||
| 443 | 443 | ||||
M internal/app/browser/workspace_operations.go +39 -40
| @@ -3,8 +3,7 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | 6 | + | "github.com/bnema/dumber/internal/logging" |
| 7 | - | ||||
| 8 | "github.com/bnema/dumber/pkg/webkit" | 7 | "github.com/bnema/dumber/pkg/webkit" | ||
| 9 | ) | 8 | ) | ||
| 10 | 9 | ||||
| @@ -25,7 +24,7 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 25 | return nil, fmt.Errorf("invalid split direction '%s', expected one of: left, right, up, down", direction) | 24 | return nil, fmt.Errorf("invalid split direction '%s', expected one of: left, right, up, down", direction) | ||
| 26 | } | 25 | } | ||
| 27 | 26 | ||||
| 28 | - | log.Printf("[workspace] Starting split operation: target=%p direction=%s", target, direction) | 27 | + | logging.Debug(fmt.Sprintf("[workspace] Starting split operation: target=%p direction=%s", target, direction)) |
| 29 | 28 | ||||
| 30 | // Step 1: Validate geometry constraints (only in DebugBasic+) | 29 | // Step 1: Validate geometry constraints (only in DebugBasic+) | ||
| 31 | if wm.debugLevel >= DebugBasic { | 30 | if wm.debugLevel >= DebugBasic { | ||
| @@ -34,12 +33,12 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 34 | if wm.debugLevel == DebugFull { | 33 | if wm.debugLevel == DebugFull { | ||
| 35 | return nil, fmt.Errorf("split validation failed: %s", validation.Reason) | 34 | return nil, fmt.Errorf("split validation failed: %s", validation.Reason) | ||
| 36 | } | 35 | } | ||
| 37 | - | log.Printf("[workspace] WARNING: Split validation failed but allowing operation: %s", validation.Reason) | 36 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Split validation failed but allowing operation: %s", validation.Reason)) |
| 38 | } | 37 | } | ||
| 39 | 38 | ||||
| 40 | // Log if re-validation will be needed due to pending widget allocation | 39 | // Log if re-validation will be needed due to pending widget allocation | ||
| 41 | if validation.RequiresRevalidation { | 40 | if validation.RequiresRevalidation { | ||
| 42 | - | log.Printf("[workspace] Split validation passed with pending allocation - operation will proceed") | 41 | + | logging.Info(fmt.Sprintf("[workspace] Split validation passed with pending allocation - operation will proceed")) |
| 43 | } | 42 | } | ||
| 44 | 43 | ||||
| 45 | // Step 3: Validate tree invariants before operation | 44 | // Step 3: Validate tree invariants before operation | ||
| @@ -47,13 +46,13 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 47 | if wm.debugLevel == DebugFull { | 46 | if wm.debugLevel == DebugFull { | ||
| 48 | return nil, fmt.Errorf("tree validation failed before split: %w", err) | 47 | return nil, fmt.Errorf("tree validation failed before split: %w", err) | ||
| 49 | } | 48 | } | ||
| 50 | - | log.Printf("[workspace] WARNING: Tree validation failed before split but allowing operation: %v", err) | 49 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before split but allowing operation: %v", err)) |
| 51 | } | 50 | } | ||
| 52 | } | 51 | } | ||
| 53 | 52 | ||||
| 54 | // Step 2: Execute directly if we're already on the GTK main thread | 53 | // Step 2: Execute directly if we're already on the GTK main thread | ||
| 55 | if webkit.IsMainThread() { | 54 | if webkit.IsMainThread() { | ||
| 56 | - | log.Printf("[workspace] Already on main thread, executing split directly") | 55 | + | logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing split directly")) |
| 57 | newNode, err := wm.splitNode(target, direction, nil) | 56 | newNode, err := wm.splitNode(target, direction, nil) | ||
| 58 | if err != nil { | 57 | if err != nil { | ||
| 59 | return nil, err | 58 | return nil, err | ||
| @@ -61,18 +60,18 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 61 | 60 | ||||
| 62 | if wm.debugLevel >= DebugBasic { | 61 | if wm.debugLevel >= DebugBasic { | ||
| 63 | if err := wm.treeValidator.ValidateTree(wm.root, "after_split"); err != nil { | 62 | if err := wm.treeValidator.ValidateTree(wm.root, "after_split"); err != nil { | ||
| 64 | - | log.Printf("[workspace] Tree validation failed after split: %v", err) | 63 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after split: %v", err)) |
| 65 | } | 64 | } | ||
| 66 | } | 65 | } | ||
| 67 | 66 | ||||
| 68 | // Tree rebalancing is only needed after close promotions. Splits have correct allocation from GTK. | 67 | // Tree rebalancing is only needed after close promotions. Splits have correct allocation from GTK. | ||
| 69 | 68 | ||||
| 70 | - | log.Printf("[workspace] Split operation completed successfully (direct execution): newNode=%p", newNode) | 69 | + | logging.Info(fmt.Sprintf("[workspace] Split operation completed successfully (direct execution): newNode=%p", newNode)) |
| 71 | return newNode, nil | 70 | return newNode, nil | ||
| 72 | } | 71 | } | ||
| 73 | 72 | ||||
| 74 | // Step 3: Not on main thread, marshal via IdleAdd | 73 | // Step 3: Not on main thread, marshal via IdleAdd | ||
| 75 | - | log.Printf("[workspace] Not on main thread, marshalling split via IdleAdd") | 74 | + | logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling split via IdleAdd")) |
| 76 | var newNode *paneNode | 75 | var newNode *paneNode | ||
| 77 | var splitErr error | 76 | var splitErr error | ||
| 78 | done := make(chan struct{}) | 77 | done := make(chan struct{}) | ||
| @@ -82,7 +81,7 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 82 | if splitErr == nil { | 81 | if splitErr == nil { | ||
| 83 | if wm.debugLevel >= DebugBasic { | 82 | if wm.debugLevel >= DebugBasic { | ||
| 84 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_split"); verr != nil { | 83 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_split"); verr != nil { | ||
| 85 | - | log.Printf("[workspace] Tree validation failed after split: %v", verr) | 84 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after split: %v", verr)) |
| 86 | } | 85 | } | ||
| 87 | } | 86 | } | ||
| 88 | } | 87 | } | ||
| @@ -96,7 +95,7 @@ func (wm *WorkspaceManager) SplitPane(target *paneNode, direction string) (*pane | |||||
| 96 | return nil, splitErr | 95 | return nil, splitErr | ||
| 97 | } | 96 | } | ||
| 98 | 97 | ||||
| 99 | - | log.Printf("[workspace] Split operation completed successfully: newNode=%p", newNode) | 98 | + | logging.Info(fmt.Sprintf("[workspace] Split operation completed successfully: newNode=%p", newNode)) |
| 100 | return newNode, nil | 99 | return newNode, nil | ||
| 101 | } | 100 | } | ||
| 102 | 101 | ||||
| @@ -106,7 +105,7 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 106 | return fmt.Errorf("workspace manager is nil") | 105 | return fmt.Errorf("workspace manager is nil") | ||
| 107 | } | 106 | } | ||
| 108 | 107 | ||||
| 109 | - | log.Printf("[workspace] Starting close operation: node=%p", node) | 108 | + | logging.Info(fmt.Sprintf("[workspace] Starting close operation: node=%p", node)) |
| 110 | wm.paneCloseLogf("start bulletproof close node=%p", node) | 109 | wm.paneCloseLogf("start bulletproof close node=%p", node) | ||
| 111 | wm.dumpTreeState("before_close") | 110 | wm.dumpTreeState("before_close") | ||
| 112 | 111 | ||||
| @@ -116,19 +115,19 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 116 | if wm.debugLevel == DebugFull { | 115 | if wm.debugLevel == DebugFull { | ||
| 117 | return fmt.Errorf("tree validation failed before close: %w", err) | 116 | return fmt.Errorf("tree validation failed before close: %w", err) | ||
| 118 | } | 117 | } | ||
| 119 | - | log.Printf("[workspace] WARNING: Tree validation failed before close but allowing operation: %v", err) | 118 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before close but allowing operation: %v", err)) |
| 120 | } | 119 | } | ||
| 121 | } | 120 | } | ||
| 122 | 121 | ||||
| 123 | // Step 3: Check if this is a stacked pane and use enhanced lifecycle management | 122 | // Step 3: Check if this is a stacked pane and use enhanced lifecycle management | ||
| 124 | if node.parent != nil && node.parent.isStacked { | 123 | if node.parent != nil && node.parent.isStacked { | ||
| 125 | - | log.Printf("[workspace] Using enhanced stack lifecycle management for close") | 124 | + | logging.Debug(fmt.Sprintf("[workspace] Using enhanced stack lifecycle management for close")) |
| 126 | return wm.stackLifecycleManager.CloseStackedPaneWithLifecycle(node) | 125 | return wm.stackLifecycleManager.CloseStackedPaneWithLifecycle(node) | ||
| 127 | } | 126 | } | ||
| 128 | 127 | ||||
| 129 | // Step 4: Execute directly when already on the GTK main thread | 128 | // Step 4: Execute directly when already on the GTK main thread | ||
| 130 | if webkit.IsMainThread() { | 129 | if webkit.IsMainThread() { | ||
| 131 | - | log.Printf("[workspace] Already on main thread, executing close directly") | 130 | + | logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing close directly")) |
| 132 | 131 | ||||
| 133 | wm.paneCloseLogf("invoking closePane node=%p", node) | 132 | wm.paneCloseLogf("invoking closePane node=%p", node) | ||
| 134 | promoted, err := wm.closePane(node) | 133 | promoted, err := wm.closePane(node) | ||
| @@ -140,7 +139,7 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 140 | 139 | ||||
| 141 | if wm.debugLevel >= DebugBasic { | 140 | if wm.debugLevel >= DebugBasic { | ||
| 142 | if err := wm.treeValidator.ValidateTree(wm.root, "after_close"); err != nil { | 141 | if err := wm.treeValidator.ValidateTree(wm.root, "after_close"); err != nil { | ||
| 143 | - | log.Printf("[workspace] Tree validation failed after close: %v", err) | 142 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after close: %v", err)) |
| 144 | } | 143 | } | ||
| 145 | } | 144 | } | ||
| 146 | wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root) | 145 | wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root) | ||
| @@ -148,16 +147,16 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 148 | 147 | ||||
| 149 | if wm.treeRebalancer != nil { | 148 | if wm.treeRebalancer != nil { | ||
| 150 | if err := wm.treeRebalancer.RebalanceAfterClose(node, promoted); err != nil { | 149 | if err := wm.treeRebalancer.RebalanceAfterClose(node, promoted); err != nil { | ||
| 151 | - | log.Printf("[workspace] Tree rebalancing failed after close: %v", err) | 150 | + | logging.Error(fmt.Sprintf("[workspace] Tree rebalancing failed after close: %v", err)) |
| 152 | } | 151 | } | ||
| 153 | } | 152 | } | ||
| 154 | 153 | ||||
| 155 | - | log.Printf("[workspace] Close operation completed successfully (direct execution)") | 154 | + | logging.Info(fmt.Sprintf("[workspace] Close operation completed successfully (direct execution)")) |
| 156 | return nil | 155 | return nil | ||
| 157 | } | 156 | } | ||
| 158 | 157 | ||||
| 159 | // Step 5: Not on main thread, marshal via IdleAdd | 158 | // Step 5: Not on main thread, marshal via IdleAdd | ||
| 160 | - | log.Printf("[workspace] Not on main thread, marshalling close via IdleAdd") | 159 | + | logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling close via IdleAdd")) |
| 161 | var promoted *paneNode | 160 | var promoted *paneNode | ||
| 162 | var closeErr error | 161 | var closeErr error | ||
| 163 | done := make(chan struct{}) | 162 | done := make(chan struct{}) | ||
| @@ -171,7 +170,7 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 171 | } else { | 170 | } else { | ||
| 172 | if wm.debugLevel >= DebugBasic { | 171 | if wm.debugLevel >= DebugBasic { | ||
| 173 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_close"); verr != nil { | 172 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_close"); verr != nil { | ||
| 174 | - | log.Printf("[workspace] Tree validation failed after close: %v", verr) | 173 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after close: %v", verr)) |
| 175 | } | 174 | } | ||
| 176 | } | 175 | } | ||
| 177 | wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root) | 176 | wm.paneCloseLogf("closePane succeeded node=%p promoted=%p root=%p", node, promoted, wm.root) | ||
| @@ -179,7 +178,7 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 179 | 178 | ||||
| 180 | if wm.treeRebalancer != nil { | 179 | if wm.treeRebalancer != nil { | ||
| 181 | if rerr := wm.treeRebalancer.RebalanceAfterClose(node, promoted); rerr != nil { | 180 | if rerr := wm.treeRebalancer.RebalanceAfterClose(node, promoted); rerr != nil { | ||
| 182 | - | log.Printf("[workspace] Tree rebalancing failed after close: %v", rerr) | 181 | + | logging.Error(fmt.Sprintf("[workspace] Tree rebalancing failed after close: %v", rerr)) |
| 183 | } | 182 | } | ||
| 184 | } | 183 | } | ||
| 185 | } | 184 | } | ||
| @@ -193,7 +192,7 @@ func (wm *WorkspaceManager) ClosePane(node *paneNode) error { | |||||
| 193 | return closeErr | 192 | return closeErr | ||
| 194 | } | 193 | } | ||
| 195 | 194 | ||||
| 196 | - | log.Printf("[workspace] Close operation completed successfully") | 195 | + | logging.Info(fmt.Sprintf("[workspace] Close operation completed successfully")) |
| 197 | return nil | 196 | return nil | ||
| 198 | } | 197 | } | ||
| 199 | 198 | ||||
| @@ -203,7 +202,7 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 203 | return nil, fmt.Errorf("workspace manager is nil") | 202 | return nil, fmt.Errorf("workspace manager is nil") | ||
| 204 | } | 203 | } | ||
| 205 | 204 | ||||
| 206 | - | log.Printf("[workspace] Starting stack operation: target=%p", target) | 205 | + | logging.Info(fmt.Sprintf("[workspace] Starting stack operation: target=%p", target)) |
| 207 | 206 | ||||
| 208 | // Step 1: Validate stack operation constraints (only in DebugBasic+) | 207 | // Step 1: Validate stack operation constraints (only in DebugBasic+) | ||
| 209 | if wm.debugLevel >= DebugBasic { | 208 | if wm.debugLevel >= DebugBasic { | ||
| @@ -212,12 +211,12 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 212 | if wm.debugLevel == DebugFull { | 211 | if wm.debugLevel == DebugFull { | ||
| 213 | return nil, fmt.Errorf("stack validation failed: %s", validation.Reason) | 212 | return nil, fmt.Errorf("stack validation failed: %s", validation.Reason) | ||
| 214 | } | 213 | } | ||
| 215 | - | log.Printf("[workspace] WARNING: Stack validation failed but allowing operation: %s", validation.Reason) | 214 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Stack validation failed but allowing operation: %s", validation.Reason)) |
| 216 | } | 215 | } | ||
| 217 | 216 | ||||
| 218 | // Log if re-validation will be needed due to pending widget allocation | 217 | // Log if re-validation will be needed due to pending widget allocation | ||
| 219 | if validation.RequiresRevalidation { | 218 | if validation.RequiresRevalidation { | ||
| 220 | - | log.Printf("[workspace] Stack validation passed with pending allocation - operation will proceed") | 219 | + | logging.Info(fmt.Sprintf("[workspace] Stack validation passed with pending allocation - operation will proceed")) |
| 221 | } | 220 | } | ||
| 222 | } | 221 | } | ||
| 223 | 222 | ||||
| @@ -227,13 +226,13 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 227 | if wm.debugLevel == DebugFull { | 226 | if wm.debugLevel == DebugFull { | ||
| 228 | return nil, fmt.Errorf("tree validation failed before stack: %w", err) | 227 | return nil, fmt.Errorf("tree validation failed before stack: %w", err) | ||
| 229 | } | 228 | } | ||
| 230 | - | log.Printf("[workspace] WARNING: Tree validation failed before stack but allowing operation: %v", err) | 229 | + | logging.Error(fmt.Sprintf("[workspace] WARNING: Tree validation failed before stack but allowing operation: %v", err)) |
| 231 | } | 230 | } | ||
| 232 | } | 231 | } | ||
| 233 | 232 | ||||
| 234 | // Step 3: Execute directly if already on the GTK main thread | 233 | // Step 3: Execute directly if already on the GTK main thread | ||
| 235 | if webkit.IsMainThread() { | 234 | if webkit.IsMainThread() { | ||
| 236 | - | log.Printf("[workspace] Already on main thread, executing stack directly") | 235 | + | logging.Info(fmt.Sprintf("[workspace] Already on main thread, executing stack directly")) |
| 237 | newNode, err := wm.stackedPaneManager.StackPane(target) | 236 | newNode, err := wm.stackedPaneManager.StackPane(target) | ||
| 238 | if err != nil { | 237 | if err != nil { | ||
| 239 | return nil, err | 238 | return nil, err | ||
| @@ -241,16 +240,16 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 241 | 240 | ||||
| 242 | if wm.debugLevel >= DebugBasic { | 241 | if wm.debugLevel >= DebugBasic { | ||
| 243 | if err := wm.treeValidator.ValidateTree(wm.root, "after_stack"); err != nil { | 242 | if err := wm.treeValidator.ValidateTree(wm.root, "after_stack"); err != nil { | ||
| 244 | - | log.Printf("[workspace] Tree validation failed after stack: %v", err) | 243 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after stack: %v", err)) |
| 245 | } | 244 | } | ||
| 246 | } | 245 | } | ||
| 247 | 246 | ||||
| 248 | - | log.Printf("[workspace] Stack operation completed successfully (direct execution): newNode=%p", newNode) | 247 | + | logging.Info(fmt.Sprintf("[workspace] Stack operation completed successfully (direct execution): newNode=%p", newNode)) |
| 249 | return newNode, nil | 248 | return newNode, nil | ||
| 250 | } | 249 | } | ||
| 251 | 250 | ||||
| 252 | // Step 4: Not on main thread, marshal via IdleAdd | 251 | // Step 4: Not on main thread, marshal via IdleAdd | ||
| 253 | - | log.Printf("[workspace] Not on main thread, marshalling stack via IdleAdd") | 252 | + | logging.Info(fmt.Sprintf("[workspace] Not on main thread, marshalling stack via IdleAdd")) |
| 254 | var newNode *paneNode | 253 | var newNode *paneNode | ||
| 255 | var stackErr error | 254 | var stackErr error | ||
| 256 | done := make(chan struct{}) | 255 | done := make(chan struct{}) | ||
| @@ -260,7 +259,7 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 260 | if stackErr == nil { | 259 | if stackErr == nil { | ||
| 261 | if wm.debugLevel >= DebugBasic { | 260 | if wm.debugLevel >= DebugBasic { | ||
| 262 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_stack"); verr != nil { | 261 | if verr := wm.treeValidator.ValidateTree(wm.root, "after_stack"); verr != nil { | ||
| 263 | - | log.Printf("[workspace] Tree validation failed after stack: %v", verr) | 262 | + | logging.Error(fmt.Sprintf("[workspace] Tree validation failed after stack: %v", verr)) |
| 264 | } | 263 | } | ||
| 265 | } | 264 | } | ||
| 266 | } | 265 | } | ||
| @@ -274,7 +273,7 @@ func (wm *WorkspaceManager) StackPane(target *paneNode) (*paneNode, error) { | |||||
| 274 | return nil, stackErr | 273 | return nil, stackErr | ||
| 275 | } | 274 | } | ||
| 276 | 275 | ||||
| 277 | - | log.Printf("[workspace] Stack operation completed successfully: newNode=%p", newNode) | 276 | + | logging.Info(fmt.Sprintf("[workspace] Stack operation completed successfully: newNode=%p", newNode)) |
| 278 | return newNode, nil | 277 | return newNode, nil | ||
| 279 | } | 278 | } | ||
| 280 | 279 | ||||
| @@ -286,7 +285,7 @@ func (wm *WorkspaceManager) EnableEnhancedMode() { | |||||
| 286 | if wm.treeRebalancer != nil { | 285 | if wm.treeRebalancer != nil { | ||
| 287 | wm.treeRebalancer.Enable() | 286 | wm.treeRebalancer.Enable() | ||
| 288 | } | 287 | } | ||
| 289 | - | log.Printf("[workspace] Enhanced validation mode enabled") | 288 | + | logging.Info(fmt.Sprintf("[workspace] Enhanced validation mode enabled")) |
| 290 | } | 289 | } | ||
| 291 | 290 | ||||
| 292 | // DisableEnhancedMode disables enhanced features for performance | 291 | // DisableEnhancedMode disables enhanced features for performance | ||
| @@ -297,7 +296,7 @@ func (wm *WorkspaceManager) DisableEnhancedMode() { | |||||
| 297 | if wm.treeRebalancer != nil { | 296 | if wm.treeRebalancer != nil { | ||
| 298 | wm.treeRebalancer.Disable() | 297 | wm.treeRebalancer.Disable() | ||
| 299 | } | 298 | } | ||
| 300 | - | log.Printf("[workspace] Enhanced validation mode disabled") | 299 | + | logging.Info(fmt.Sprintf("[workspace] Enhanced validation mode disabled")) |
| 301 | } | 300 | } | ||
| 302 | 301 | ||||
| 303 | // SetEnhancedDebugMode enables/disables debug mode for all enhanced validation components | 302 | // SetEnhancedDebugMode enables/disables debug mode for all enhanced validation components | ||
| @@ -308,7 +307,7 @@ func (wm *WorkspaceManager) SetEnhancedDebugMode(debug bool) { | |||||
| 308 | if wm.geometryValidator != nil { | 307 | if wm.geometryValidator != nil { | ||
| 309 | wm.geometryValidator.SetDebugMode(debug) | 308 | wm.geometryValidator.SetDebugMode(debug) | ||
| 310 | } | 309 | } | ||
| 311 | - | log.Printf("[workspace] Enhanced debug mode set to: %v", debug) | 310 | + | logging.Debug(fmt.Sprintf("[workspace] Enhanced debug mode set to: %v", debug)) |
| 312 | } | 311 | } | ||
| 313 | 312 | ||||
| 314 | // GetEnhancedStats returns comprehensive statistics about all enhanced validation components | 313 | // GetEnhancedStats returns comprehensive statistics about all enhanced validation components | ||
| @@ -330,7 +329,7 @@ func (wm *WorkspaceManager) GetEnhancedStats() map[string]interface{} { | |||||
| 330 | 329 | ||||
| 331 | // ValidateWorkspaceIntegrity performs a comprehensive validation of the workspace | 330 | // ValidateWorkspaceIntegrity performs a comprehensive validation of the workspace | ||
| 332 | func (wm *WorkspaceManager) ValidateWorkspaceIntegrity() error { | 331 | func (wm *WorkspaceManager) ValidateWorkspaceIntegrity() error { | ||
| 333 | - | log.Printf("[workspace] Performing comprehensive workspace integrity check") | 332 | + | logging.Info(fmt.Sprintf("[workspace] Performing comprehensive workspace integrity check")) |
| 334 | 333 | ||||
| 335 | // Tree structure validation | 334 | // Tree structure validation | ||
| 336 | if wm.treeValidator != nil { | 335 | if wm.treeValidator != nil { | ||
| @@ -341,14 +340,14 @@ func (wm *WorkspaceManager) ValidateWorkspaceIntegrity() error { | |||||
| 341 | 340 | ||||
| 342 | // Geometry validation simplified - ValidateWorkspaceLayout removed | 341 | // Geometry validation simplified - ValidateWorkspaceLayout removed | ||
| 343 | 342 | ||||
| 344 | - | log.Printf("[workspace] Workspace integrity check completed successfully") | 343 | + | logging.Info(fmt.Sprintf("[workspace] Workspace integrity check completed successfully")) |
| 345 | return nil | 344 | return nil | ||
| 346 | } | 345 | } | ||
| 347 | 346 | ||||
| 348 | // ShutdownEnhancedComponents gracefully shuts down all enhanced validation components | 347 | // ShutdownEnhancedComponents gracefully shuts down all enhanced validation components | ||
| 349 | func (wm *WorkspaceManager) ShutdownEnhancedComponents() { | 348 | func (wm *WorkspaceManager) ShutdownEnhancedComponents() { | ||
| 350 | - | log.Printf("[workspace] Shutting down enhanced validation components") | 349 | + | logging.Info(fmt.Sprintf("[workspace] Shutting down enhanced validation components")) |
| 351 | 350 | ||||
| 352 | // Other components don't require explicit shutdown currently | 351 | // Other components don't require explicit shutdown currently | ||
| 353 | - | log.Printf("[workspace] Enhanced validation components shutdown complete") | 352 | + | logging.Info(fmt.Sprintf("[workspace] Enhanced validation components shutdown complete")) |
| 354 | } | 353 | } | ||
M internal/app/browser/workspace_pane_mode.go +21 -19
| @@ -2,7 +2,9 @@ | |||||
| 2 | package browser | 2 | package browser | ||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | + | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | ) | 8 | ) | ||
| 7 | 9 | ||||
| 8 | // EnterPaneMode activates pane mode on the currently focused pane | 10 | // EnterPaneMode activates pane mode on the currently focused pane | ||
| @@ -16,17 +18,17 @@ func (wm *WorkspaceManager) EnterPaneMode() { | |||||
| 16 | // Get the currently focused pane | 18 | // Get the currently focused pane | ||
| 17 | activeNode := wm.GetActiveNode() | 19 | activeNode := wm.GetActiveNode() | ||
| 18 | if activeNode == nil { | 20 | if activeNode == nil { | ||
| 19 | - | log.Printf("[pane-mode] Cannot enter pane mode: no active pane") | 21 | + | logging.Error(fmt.Sprintf("[pane-mode] Cannot enter pane mode: no active pane")) |
| 20 | return | 22 | return | ||
| 21 | } | 23 | } | ||
| 22 | 24 | ||||
| 23 | // Already in pane mode on this pane? | 25 | // Already in pane mode on this pane? | ||
| 24 | if wm.paneModeActive && wm.paneModeActivePane == activeNode { | 26 | if wm.paneModeActive && wm.paneModeActivePane == activeNode { | ||
| 25 | - | log.Printf("[pane-mode] Pane mode already active on this pane") | 27 | + | logging.Info(fmt.Sprintf("[pane-mode] Pane mode already active on this pane")) |
| 26 | return | 28 | return | ||
| 27 | } | 29 | } | ||
| 28 | 30 | ||||
| 29 | - | log.Printf("[pane-mode] Entering pane mode on pane %p (workspace=%p)", activeNode, wm) | 31 | + | logging.Debug(fmt.Sprintf("[pane-mode] Entering pane mode on pane %p (workspace=%p)", activeNode, wm)) |
| 30 | 32 | ||||
| 31 | wm.paneModeActive = true | 33 | wm.paneModeActive = true | ||
| 32 | wm.paneModeActivePane = activeNode | 34 | wm.paneModeActivePane = activeNode | ||
| @@ -51,7 +53,7 @@ func (wm *WorkspaceManager) ExitPaneMode(reason string) { | |||||
| 51 | return | 53 | return | ||
| 52 | } | 54 | } | ||
| 53 | 55 | ||||
| 54 | - | log.Printf("[pane-mode] Exiting pane mode: %s", reason) | 56 | + | logging.Info(fmt.Sprintf("[pane-mode] Exiting pane mode: %s", reason)) |
| 55 | 57 | ||||
| 56 | wm.paneModeActive = false | 58 | wm.paneModeActive = false | ||
| 57 | wm.paneModeActivePane = nil | 59 | wm.paneModeActivePane = nil | ||
| @@ -74,7 +76,7 @@ func (wm *WorkspaceManager) HandlePaneAction(action string) { | |||||
| 74 | 76 | ||||
| 75 | if !wm.paneModeActive { | 77 | if !wm.paneModeActive { | ||
| 76 | wm.paneMutex.Unlock() | 78 | wm.paneMutex.Unlock() | ||
| 77 | - | log.Printf("[pane-mode] Ignoring action '%s': pane mode not active", action) | 79 | + | logging.Info(fmt.Sprintf("[pane-mode] Ignoring action '%s': pane mode not active", action)) |
| 78 | return | 80 | return | ||
| 79 | } | 81 | } | ||
| 80 | 82 | ||||
| @@ -82,12 +84,12 @@ func (wm *WorkspaceManager) HandlePaneAction(action string) { | |||||
| 82 | wm.paneMutex.Unlock() | 84 | wm.paneMutex.Unlock() | ||
| 83 | 85 | ||||
| 84 | if activePane == nil { | 86 | if activePane == nil { | ||
| 85 | - | log.Printf("[pane-mode] Ignoring action '%s': no active pane", action) | 87 | + | logging.Info(fmt.Sprintf("[pane-mode] Ignoring action '%s': no active pane", action)) |
| 86 | wm.ExitPaneMode("no-active-pane") | 88 | wm.ExitPaneMode("no-active-pane") | ||
| 87 | return | 89 | return | ||
| 88 | } | 90 | } | ||
| 89 | 91 | ||||
| 90 | - | log.Printf("[pane-mode] Handling action '%s' on pane %p", action, activePane) | 92 | + | logging.Debug(fmt.Sprintf("[pane-mode] Handling action '%s' on pane %p", action, activePane)) |
| 91 | 93 | ||||
| 92 | switch action { | 94 | switch action { | ||
| 93 | case "close": | 95 | case "close": | ||
| @@ -97,7 +99,7 @@ func (wm *WorkspaceManager) HandlePaneAction(action string) { | |||||
| 97 | case "stack": | 99 | case "stack": | ||
| 98 | wm.handlePaneStack(activePane) | 100 | wm.handlePaneStack(activePane) | ||
| 99 | default: | 101 | default: | ||
| 100 | - | log.Printf("[pane-mode] Unknown action: %s", action) | 102 | + | logging.Warn(fmt.Sprintf("[pane-mode] Unknown action: %s", action)) |
| 101 | } | 103 | } | ||
| 102 | 104 | ||||
| 103 | // Exit pane mode after action | 105 | // Exit pane mode after action | ||
| @@ -110,14 +112,14 @@ func (wm *WorkspaceManager) handlePaneClose(node *paneNode) { | |||||
| 110 | return | 112 | return | ||
| 111 | } | 113 | } | ||
| 112 | 114 | ||||
| 113 | - | log.Printf("[pane-mode] Closing pane %p", node) | 115 | + | logging.Debug(fmt.Sprintf("[pane-mode] Closing pane %p", node)) |
| 114 | 116 | ||||
| 115 | // Notify JavaScript first so it can show toast | 117 | // Notify JavaScript first so it can show toast | ||
| 116 | wm.dispatchPaneModeEvent("action", "close") | 118 | wm.dispatchPaneModeEvent("action", "close") | ||
| 117 | 119 | ||||
| 118 | // Close the pane | 120 | // Close the pane | ||
| 119 | if err := wm.ClosePane(node); err != nil { | 121 | if err := wm.ClosePane(node); err != nil { | ||
| 120 | - | log.Printf("[pane-mode] Failed to close pane: %v", err) | 122 | + | logging.Error(fmt.Sprintf("[pane-mode] Failed to close pane: %v", err)) |
| 121 | } | 123 | } | ||
| 122 | } | 124 | } | ||
| 123 | 125 | ||||
| @@ -139,11 +141,11 @@ func (wm *WorkspaceManager) handlePaneSplit(node *paneNode, action string) { | |||||
| 139 | case "split-down": | 141 | case "split-down": | ||
| 140 | direction = DirectionDown | 142 | direction = DirectionDown | ||
| 141 | default: | 143 | default: | ||
| 142 | - | log.Printf("[pane-mode] Invalid split action: %s", action) | 144 | + | logging.Error(fmt.Sprintf("[pane-mode] Invalid split action: %s", action)) |
| 143 | return | 145 | return | ||
| 144 | } | 146 | } | ||
| 145 | 147 | ||||
| 146 | - | log.Printf("[pane-mode] Splitting pane %p in direction %s", node, direction) | 148 | + | logging.Debug(fmt.Sprintf("[pane-mode] Splitting pane %p in direction %s", node, direction)) |
| 147 | 149 | ||||
| 148 | // Notify JavaScript first so it can show toast | 150 | // Notify JavaScript first so it can show toast | ||
| 149 | wm.dispatchPaneModeEvent("action", action) | 151 | wm.dispatchPaneModeEvent("action", action) | ||
| @@ -151,14 +153,14 @@ func (wm *WorkspaceManager) handlePaneSplit(node *paneNode, action string) { | |||||
| 151 | // Perform the split (pass nil so it creates a fresh pane, not popup mode) | 153 | // Perform the split (pass nil so it creates a fresh pane, not popup mode) | ||
| 152 | newNode, err := wm.splitNode(node, direction, nil) | 154 | newNode, err := wm.splitNode(node, direction, nil) | ||
| 153 | if err != nil { | 155 | if err != nil { | ||
| 154 | - | log.Printf("[pane-mode] Failed to split pane: %v", err) | 156 | + | logging.Error(fmt.Sprintf("[pane-mode] Failed to split pane: %v", err)) |
| 155 | return | 157 | return | ||
| 156 | } | 158 | } | ||
| 157 | 159 | ||||
| 158 | // Load initial URL in the new pane so scripts execute | 160 | // Load initial URL in the new pane so scripts execute | ||
| 159 | wm.clonePaneState(node, newNode) | 161 | wm.clonePaneState(node, newNode) | ||
| 160 | 162 | ||||
| 161 | - | log.Printf("[pane-mode] Split successful: direction=%s", direction) | 163 | + | logging.Debug(fmt.Sprintf("[pane-mode] Split successful: direction=%s", direction)) |
| 162 | } | 164 | } | ||
| 163 | 165 | ||||
| 164 | // handlePaneStack creates a stacked pane | 166 | // handlePaneStack creates a stacked pane | ||
| @@ -167,7 +169,7 @@ func (wm *WorkspaceManager) handlePaneStack(node *paneNode) { | |||||
| 167 | return | 169 | return | ||
| 168 | } | 170 | } | ||
| 169 | 171 | ||||
| 170 | - | log.Printf("[pane-mode] Stacking pane %p", node) | 172 | + | logging.Debug(fmt.Sprintf("[pane-mode] Stacking pane %p", node)) |
| 171 | 173 | ||||
| 172 | // Notify JavaScript first so it can show toast | 174 | // Notify JavaScript first so it can show toast | ||
| 173 | wm.dispatchPaneModeEvent("action", "stack") | 175 | wm.dispatchPaneModeEvent("action", "stack") | ||
| @@ -175,14 +177,14 @@ func (wm *WorkspaceManager) handlePaneStack(node *paneNode) { | |||||
| 175 | // Perform the stack operation | 177 | // Perform the stack operation | ||
| 176 | newNode, err := wm.stackedPaneManager.StackPane(node) | 178 | newNode, err := wm.stackedPaneManager.StackPane(node) | ||
| 177 | if err != nil { | 179 | if err != nil { | ||
| 178 | - | log.Printf("[pane-mode] Failed to stack pane: %v", err) | 180 | + | logging.Error(fmt.Sprintf("[pane-mode] Failed to stack pane: %v", err)) |
| 179 | return | 181 | return | ||
| 180 | } | 182 | } | ||
| 181 | 183 | ||||
| 182 | // Load initial URL in the new pane so scripts execute | 184 | // Load initial URL in the new pane so scripts execute | ||
| 183 | wm.clonePaneState(node, newNode) | 185 | wm.clonePaneState(node, newNode) | ||
| 184 | 186 | ||||
| 185 | - | log.Printf("[pane-mode] Stack successful") | 187 | + | logging.Info(fmt.Sprintf("[pane-mode] Stack successful")) |
| 186 | } | 188 | } | ||
| 187 | 189 | ||||
| 188 | // dispatchPaneModeEvent sends a pane mode event to JavaScript for UI updates | 190 | // dispatchPaneModeEvent sends a pane mode event to JavaScript for UI updates | ||
| @@ -198,7 +200,7 @@ func (wm *WorkspaceManager) dispatchPaneModeEvent(event string, detail string) { | |||||
| 198 | } | 200 | } | ||
| 199 | 201 | ||||
| 200 | if err := view.DispatchCustomEvent("dumber:pane-mode", data); err != nil { | 202 | if err := view.DispatchCustomEvent("dumber:pane-mode", data); err != nil { | ||
| 201 | - | log.Printf("[pane-mode] Failed to dispatch event: %v", err) | 203 | + | logging.Error(fmt.Sprintf("[pane-mode] Failed to dispatch event: %v", err)) |
| 202 | } | 204 | } | ||
| 203 | } | 205 | } | ||
| 204 | 206 | ||||
M internal/app/browser/workspace_pane_ops.go +57 -57
| @@ -4,11 +4,11 @@ package browser | |||||
| 4 | import ( | 4 | import ( | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | 7 | ||||
| 9 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 8 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 10 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 9 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 11 | 10 | ||||
| 11 | + | "github.com/bnema/dumber/internal/logging" | |||
| 12 | "github.com/bnema/dumber/pkg/webkit" | 12 | "github.com/bnema/dumber/pkg/webkit" | ||
| 13 | ) | 13 | ) | ||
| 14 | 14 | ||||
| @@ -67,7 +67,7 @@ func (wm *WorkspaceManager) collectLeavesFromWithDirection(node *paneNode, direc | |||||
| 67 | return | 67 | return | ||
| 68 | } | 68 | } | ||
| 69 | if visited[n] { | 69 | if visited[n] { | ||
| 70 | - | log.Printf("[workspace] collectLeavesFromWithDirection: cycle detected in tree") | 70 | + | logging.Error(fmt.Sprintf("[workspace] collectLeavesFromWithDirection: cycle detected in tree")) |
| 71 | return | 71 | return | ||
| 72 | } | 72 | } | ||
| 73 | visited[n] = true | 73 | visited[n] = true | ||
| @@ -121,7 +121,7 @@ func (wm *WorkspaceManager) collectLeavesFrom(node *paneNode) []*paneNode { | |||||
| 121 | return | 121 | return | ||
| 122 | } | 122 | } | ||
| 123 | if visited[n] { | 123 | if visited[n] { | ||
| 124 | - | log.Printf("[workspace] collectLeavesFrom: cycle detected in tree") | 124 | + | logging.Error(fmt.Sprintf("[workspace] collectLeavesFrom: cycle detected in tree")) |
| 125 | return | 125 | return | ||
| 126 | } | 126 | } | ||
| 127 | visited[n] = true | 127 | visited[n] = true | ||
| @@ -180,22 +180,22 @@ func (wm *WorkspaceManager) createPane(view *webkit.WebView) (*BrowserPane, erro | |||||
| 180 | // clonePaneState sets up a new pane with cloned state from another pane | 180 | // clonePaneState sets up a new pane with cloned state from another pane | ||
| 181 | func (wm *WorkspaceManager) clonePaneState(_ *paneNode, target *paneNode) { | 181 | func (wm *WorkspaceManager) clonePaneState(_ *paneNode, target *paneNode) { | ||
| 182 | if wm == nil || target == nil { | 182 | if wm == nil || target == nil { | ||
| 183 | - | log.Printf("[workspace] clonePaneState: wm or target is nil") | 183 | + | logging.Debug(fmt.Sprintf("[workspace] clonePaneState: wm or target is nil")) |
| 184 | return | 184 | return | ||
| 185 | } | 185 | } | ||
| 186 | if target.pane == nil || target.pane.webView == nil { | 186 | if target.pane == nil || target.pane.webView == nil { | ||
| 187 | - | log.Printf("[workspace] clonePaneState: target pane or webView is nil") | 187 | + | logging.Debug(fmt.Sprintf("[workspace] clonePaneState: target pane or webView is nil")) |
| 188 | return | 188 | return | ||
| 189 | } | 189 | } | ||
| 190 | 190 | ||||
| 191 | // Load about:blank and auto-open omnibox | 191 | // Load about:blank and auto-open omnibox | ||
| 192 | const blankURL = "about:blank" | 192 | const blankURL = "about:blank" | ||
| 193 | 193 | ||||
| 194 | - | log.Printf("[workspace] clonePaneState: loading %s in pane %p", blankURL, target.pane) | 194 | + | logging.Debug(fmt.Sprintf("[workspace] clonePaneState: loading %s in pane %p", blankURL, target.pane)) |
| 195 | if err := target.pane.webView.LoadURL(blankURL); err != nil { | 195 | if err := target.pane.webView.LoadURL(blankURL); err != nil { | ||
| 196 | - | log.Printf("[workspace] failed to load blank page in new pane: %v", err) | 196 | + | logging.Error(fmt.Sprintf("[workspace] failed to load blank page in new pane: %v", err)) |
| 197 | } else { | 197 | } else { | ||
| 198 | - | log.Printf("[workspace] successfully initiated load of %s", blankURL) | 198 | + | logging.Debug(fmt.Sprintf("[workspace] successfully initiated load of %s", blankURL)) |
| 199 | } | 199 | } | ||
| 200 | } | 200 | } | ||
| 201 | 201 | ||||
| @@ -218,13 +218,13 @@ func (wm *WorkspaceManager) safelyDetachControllersBeforeReparent(node *paneNode | |||||
| 218 | if target.hoverToken != nil { | 218 | if target.hoverToken != nil { | ||
| 219 | target.pendingHoverReattach = true | 219 | target.pendingHoverReattach = true | ||
| 220 | target.hoverToken = nil // Clear reference, don't manually remove | 220 | target.hoverToken = nil // Clear reference, don't manually remove | ||
| 221 | - | log.Printf("[workspace] Marked hover controller for reattach on pane %p (GTK will auto-remove)", target) | 221 | + | logging.Debug(fmt.Sprintf("[workspace] Marked hover controller for reattach on pane %p (GTK will auto-remove)", target)) |
| 222 | } | 222 | } | ||
| 223 | 223 | ||||
| 224 | if target.focusControllerToken != 0 { | 224 | if target.focusControllerToken != 0 { | ||
| 225 | target.pendingFocusReattach = true | 225 | target.pendingFocusReattach = true | ||
| 226 | target.focusControllerToken = 0 // Clear reference, don't manually remove | 226 | target.focusControllerToken = 0 // Clear reference, don't manually remove | ||
| 227 | - | log.Printf("[workspace] Marked focus controller for reattach on pane %p (GTK will auto-remove)", target) | 227 | + | logging.Debug(fmt.Sprintf("[workspace] Marked focus controller for reattach on pane %p (GTK will auto-remove)", target)) |
| 228 | } | 228 | } | ||
| 229 | } | 229 | } | ||
| 230 | 230 | ||||
| @@ -246,7 +246,7 @@ func (wm *WorkspaceManager) closeCurrentPane() { | |||||
| 246 | return | 246 | return | ||
| 247 | } | 247 | } | ||
| 248 | if err := wm.ClosePane(wm.GetActiveNode()); err != nil { | 248 | if err := wm.ClosePane(wm.GetActiveNode()); err != nil { | ||
| 249 | - | log.Printf("[workspace] close current pane failed: %v", err) | 249 | + | logging.Error(fmt.Sprintf("[workspace] close current pane failed: %v", err)) |
| 250 | } | 250 | } | ||
| 251 | } | 251 | } | ||
| 252 | 252 | ||||
| @@ -343,7 +343,7 @@ func (wm *WorkspaceManager) removeFromMaps(webView *webkit.WebView) { | |||||
| 343 | delete(wm.viewToNode, webView) | 343 | delete(wm.viewToNode, webView) | ||
| 344 | delete(wm.lastSplitMsg, webView) | 344 | delete(wm.lastSplitMsg, webView) | ||
| 345 | delete(wm.lastExitMsg, webView) | 345 | delete(wm.lastExitMsg, webView) | ||
| 346 | - | log.Printf("[workspace] removed webview %p from tracking maps", webView) | 346 | + | logging.Debug(fmt.Sprintf("[workspace] removed webview %p from tracking maps", webView)) |
| 347 | } | 347 | } | ||
| 348 | 348 | ||||
| 349 | // removeFromAppPanes removes a BrowserPane from the app.panes slice | 349 | // removeFromAppPanes removes a BrowserPane from the app.panes slice | ||
| @@ -355,7 +355,7 @@ func (wm *WorkspaceManager) removeFromAppPanes(pane *BrowserPane) { | |||||
| 355 | for i, p := range wm.app.panes { | 355 | for i, p := range wm.app.panes { | ||
| 356 | if p == pane { | 356 | if p == pane { | ||
| 357 | wm.app.panes = append(wm.app.panes[:i], wm.app.panes[i+1:]...) | 357 | wm.app.panes = append(wm.app.panes[:i], wm.app.panes[i+1:]...) | ||
| 358 | - | log.Printf("[workspace] removed pane from app.panes slice, remaining: %d", len(wm.app.panes)) | 358 | + | logging.Debug(fmt.Sprintf("[workspace] removed pane from app.panes slice, remaining: %d", len(wm.app.panes))) |
| 359 | return | 359 | return | ||
| 360 | } | 360 | } | ||
| 361 | } | 361 | } | ||
| @@ -383,7 +383,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 383 | if target.parent != nil && target.parent.isStacked { | 383 | if target.parent != nil && target.parent.isStacked { | ||
| 384 | // Target is in a stack - we'll create the split around the entire stack | 384 | // Target is in a stack - we'll create the split around the entire stack | ||
| 385 | stackContainer = target.parent | 385 | stackContainer = target.parent | ||
| 386 | - | log.Printf("[workspace] target is in stack, will split around the stack: originalTarget=%p stackContainer=%p", originalTarget, stackContainer) | 386 | + | logging.Debug(fmt.Sprintf("[workspace] target is in stack, will split around the stack: originalTarget=%p stackContainer=%p", originalTarget, stackContainer)) |
| 387 | } | 387 | } | ||
| 388 | 388 | ||||
| 389 | // Determine what we're actually splitting | 389 | // Determine what we're actually splitting | ||
| @@ -394,12 +394,12 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 394 | // Case: splitting from inside a stack - we split around the entire stack | 394 | // Case: splitting from inside a stack - we split around the entire stack | ||
| 395 | splitTarget = stackContainer | 395 | splitTarget = stackContainer | ||
| 396 | splitTargetContainer = stackContainer.container | 396 | splitTargetContainer = stackContainer.container | ||
| 397 | - | log.Printf("[workspace] splitting around stack: stackContainer=%p", splitTarget) | 397 | + | logging.Debug(fmt.Sprintf("[workspace] splitting around stack: stackContainer=%p", splitTarget)) |
| 398 | } else { | 398 | } else { | ||
| 399 | // Case: normal split from a simple pane | 399 | // Case: normal split from a simple pane | ||
| 400 | splitTarget = target | 400 | splitTarget = target | ||
| 401 | splitTargetContainer = target.container | 401 | splitTargetContainer = target.container | ||
| 402 | - | log.Printf("[workspace] normal split from pane: target=%p", splitTarget) | 402 | + | logging.Info(fmt.Sprintf("[workspace] normal split from pane: target=%p", splitTarget)) |
| 403 | } | 403 | } | ||
| 404 | 404 | ||||
| 405 | // Use provided pane or create new one | 405 | // Use provided pane or create new one | ||
| @@ -408,7 +408,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 408 | if existingPane != nil { | 408 | if existingPane != nil { | ||
| 409 | // Reuse existing pane (for popup insertion) | 409 | // Reuse existing pane (for popup insertion) | ||
| 410 | newPane = existingPane | 410 | newPane = existingPane | ||
| 411 | - | log.Printf("[workspace] Using existing pane for split: %p", newPane) | 411 | + | logging.Debug(fmt.Sprintf("[workspace] Using existing pane for split: %p", newPane)) |
| 412 | } else { | 412 | } else { | ||
| 413 | // Create new WebView and pane (for normal splits) | 413 | // Create new WebView and pane (for normal splits) | ||
| 414 | newView, err := wm.createWebView() | 414 | newView, err := wm.createWebView() | ||
| @@ -420,7 +420,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 420 | if err != nil { | 420 | if err != nil { | ||
| 421 | return nil, err | 421 | return nil, err | ||
| 422 | } | 422 | } | ||
| 423 | - | log.Printf("[workspace] Created new pane for split: %p", newPane) | 423 | + | logging.Info(fmt.Sprintf("[workspace] Created new pane for split: %p", newPane)) |
| 424 | } | 424 | } | ||
| 425 | 425 | ||||
| 426 | if handler := newPane.MessageHandler(); handler != nil { | 426 | if handler := newPane.MessageHandler(); handler != nil { | ||
| @@ -451,13 +451,13 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 451 | if splitTargetContainer != nil { | 451 | if splitTargetContainer != nil { | ||
| 452 | webkit.WidgetSetHExpand(splitTargetContainer, true) | 452 | webkit.WidgetSetHExpand(splitTargetContainer, true) | ||
| 453 | webkit.WidgetSetVExpand(splitTargetContainer, true) | 453 | webkit.WidgetSetVExpand(splitTargetContainer, true) | ||
| 454 | - | log.Printf("[workspace] configured stack wrapper for split: %p", splitTargetContainer) | 454 | + | logging.Debug(fmt.Sprintf("[workspace] configured stack wrapper for split: %p", splitTargetContainer)) |
| 455 | } | 455 | } | ||
| 456 | } | 456 | } | ||
| 457 | 457 | ||||
| 458 | orientation, existingFirst := mapDirection(direction) | 458 | orientation, existingFirst := mapDirection(direction) | ||
| 459 | - | log.Printf("[workspace] splitting direction=%s orientation=%v existingFirst=%v splitTarget.parent=%p splitTarget.isStacked=%v isPopup=%v", | 459 | + | logging.Debug(fmt.Sprintf("[workspace] splitting direction=%s orientation=%v existingFirst=%v splitTarget.parent=%p splitTarget.isStacked=%v isPopup=%v", |
| 460 | - | direction, orientation, existingFirst, splitTarget.parent, splitTarget.isStacked, existingPane != nil) | 460 | + | direction, orientation, existingFirst, splitTarget.parent, splitTarget.isStacked, existingPane != nil)) |
| 461 | 461 | ||||
| 462 | paned := gtk.NewPaned(orientation) | 462 | paned := gtk.NewPaned(orientation) | ||
| 463 | if paned == nil { | 463 | if paned == nil { | ||
| @@ -500,7 +500,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 500 | // Remove active border class if present (prevents GTK bloom filter corruption during unparent) | 500 | // Remove active border class if present (prevents GTK bloom filter corruption during unparent) | ||
| 501 | if webkit.WidgetHasCSSClass(splitTargetContainer, activePaneClass) { | 501 | if webkit.WidgetHasCSSClass(splitTargetContainer, activePaneClass) { | ||
| 502 | webkit.WidgetRemoveCSSClass(splitTargetContainer, activePaneClass) | 502 | webkit.WidgetRemoveCSSClass(splitTargetContainer, activePaneClass) | ||
| 503 | - | log.Printf("[workspace] removed active class from split target (will transfer to new pane)") | 503 | + | logging.Debug(fmt.Sprintf("[workspace] removed active class from split target (will transfer to new pane)")) |
| 504 | } | 504 | } | ||
| 505 | } | 505 | } | ||
| 506 | 506 | ||||
| @@ -510,7 +510,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 510 | // For non-root splits, unparent from parent paned | 510 | // For non-root splits, unparent from parent paned | ||
| 511 | if parent != nil && parent.container != nil { | 511 | if parent != nil && parent.container != nil { | ||
| 512 | // Split target has a parent paned - remove it (automatically unparents in GTK4) | 512 | // Split target has a parent paned - remove it (automatically unparents in GTK4) | ||
| 513 | - | log.Printf("[workspace] unparenting split target container=%p from parent paned=%p", splitTargetContainer, parent.container) | 513 | + | logging.Debug(fmt.Sprintf("[workspace] unparenting split target container=%p from parent paned=%p", splitTargetContainer, parent.container)) |
| 514 | if parentPaned, ok := parent.container.(*gtk.Paned); ok && parentPaned != nil { | 514 | if parentPaned, ok := parent.container.(*gtk.Paned); ok && parentPaned != nil { | ||
| 515 | if parent.left == splitTarget { | 515 | if parent.left == splitTarget { | ||
| 516 | parentPaned.SetStartChild(nil) | 516 | parentPaned.SetStartChild(nil) | ||
| @@ -553,33 +553,33 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 553 | if parent == nil { | 553 | if parent == nil { | ||
| 554 | // ROOT SPLIT: Manipulate TabManager.ContentArea | 554 | // ROOT SPLIT: Manipulate TabManager.ContentArea | ||
| 555 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | 555 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | ||
| 556 | - | log.Printf("[workspace] ERROR: Cannot perform root split - tab manager not initialized") | 556 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: Cannot perform root split - tab manager not initialized")) |
| 557 | return nil, fmt.Errorf("tab manager not initialized") | 557 | return nil, fmt.Errorf("tab manager not initialized") | ||
| 558 | } | 558 | } | ||
| 559 | 559 | ||||
| 560 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | 560 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | ||
| 561 | if !ok || contentBox == nil { | 561 | if !ok || contentBox == nil { | ||
| 562 | - | log.Printf("[workspace] ERROR: ContentArea is not a Box") | 562 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: ContentArea is not a Box")) |
| 563 | return nil, fmt.Errorf("ContentArea is not a Box") | 563 | return nil, fmt.Errorf("ContentArea is not a Box") | ||
| 564 | } | 564 | } | ||
| 565 | 565 | ||||
| 566 | // Remove the old workspace root from ContentArea | 566 | // Remove the old workspace root from ContentArea | ||
| 567 | contentBox.Remove(splitTargetContainer) | 567 | contentBox.Remove(splitTargetContainer) | ||
| 568 | - | log.Printf("[workspace] ROOT SPLIT: removed old root %p from ContentArea", splitTargetContainer) | 568 | + | logging.Debug(fmt.Sprintf("[workspace] ROOT SPLIT: removed old root %p from ContentArea", splitTargetContainer)) |
| 569 | 569 | ||||
| 570 | // Attach the new paned to ContentArea FIRST | 570 | // Attach the new paned to ContentArea FIRST | ||
| 571 | contentBox.Append(paned) | 571 | contentBox.Append(paned) | ||
| 572 | - | log.Printf("[workspace] ROOT SPLIT: attached paned %p to ContentArea", paned) | 572 | + | logging.Debug(fmt.Sprintf("[workspace] ROOT SPLIT: attached paned %p to ContentArea", paned)) |
| 573 | 573 | ||||
| 574 | // Now add children to the paned - GTK will handle reparenting automatically | 574 | // Now add children to the paned - GTK will handle reparenting automatically | ||
| 575 | if existingFirst { | 575 | if existingFirst { | ||
| 576 | paned.SetStartChild(splitTargetContainer) | 576 | paned.SetStartChild(splitTargetContainer) | ||
| 577 | paned.SetEndChild(newContainer) | 577 | paned.SetEndChild(newContainer) | ||
| 578 | - | log.Printf("[workspace] ROOT SPLIT: added splitTarget=%p (start), new=%p (end)", splitTargetContainer, newContainer) | 578 | + | logging.Debug(fmt.Sprintf("[workspace] ROOT SPLIT: added splitTarget=%p (start), new=%p (end)", splitTargetContainer, newContainer)) |
| 579 | } else { | 579 | } else { | ||
| 580 | paned.SetStartChild(newContainer) | 580 | paned.SetStartChild(newContainer) | ||
| 581 | paned.SetEndChild(splitTargetContainer) | 581 | paned.SetEndChild(splitTargetContainer) | ||
| 582 | - | log.Printf("[workspace] ROOT SPLIT: added new=%p (start), splitTarget=%p (end)", newContainer, splitTargetContainer) | 582 | + | logging.Debug(fmt.Sprintf("[workspace] ROOT SPLIT: added new=%p (start), splitTarget=%p (end)", newContainer, splitTargetContainer)) |
| 583 | } | 583 | } | ||
| 584 | } else { | 584 | } else { | ||
| 585 | // NON-ROOT SPLIT: Standard approach | 585 | // NON-ROOT SPLIT: Standard approach | ||
| @@ -589,13 +589,13 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 589 | if splitTargetContainer != nil { | 589 | if splitTargetContainer != nil { | ||
| 590 | paned.SetStartChild(splitTargetContainer) | 590 | paned.SetStartChild(splitTargetContainer) | ||
| 591 | paned.SetEndChild(newContainer) | 591 | paned.SetEndChild(newContainer) | ||
| 592 | - | log.Printf("[workspace] added splitTarget=%p as start child, new=%p as end child", splitTargetContainer, newContainer) | 592 | + | logging.Debug(fmt.Sprintf("[workspace] added splitTarget=%p as start child, new=%p as end child", splitTargetContainer, newContainer)) |
| 593 | } | 593 | } | ||
| 594 | } else { | 594 | } else { | ||
| 595 | if splitTargetContainer != nil { | 595 | if splitTargetContainer != nil { | ||
| 596 | paned.SetStartChild(newContainer) | 596 | paned.SetStartChild(newContainer) | ||
| 597 | paned.SetEndChild(splitTargetContainer) | 597 | paned.SetEndChild(splitTargetContainer) | ||
| 598 | - | log.Printf("[workspace] added new=%p as start child, splitTarget=%p as end child", newContainer, splitTargetContainer) | 598 | + | logging.Debug(fmt.Sprintf("[workspace] added new=%p as start child, splitTarget=%p as end child", newContainer, splitTargetContainer)) |
| 599 | } | 599 | } | ||
| 600 | } | 600 | } | ||
| 601 | 601 | ||||
| @@ -608,7 +608,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 608 | } | 608 | } | ||
| 609 | webkit.WidgetQueueAllocate(parent.container) | 609 | webkit.WidgetQueueAllocate(parent.container) | ||
| 610 | paned.QueueAllocate() | 610 | paned.QueueAllocate() | ||
| 611 | - | log.Printf("[workspace] paned inserted into parent=%p", parent.container) | 611 | + | logging.Info(fmt.Sprintf("[workspace] paned inserted into parent=%p", parent.container)) |
| 612 | } | 612 | } | ||
| 613 | } | 613 | } | ||
| 614 | 614 | ||||
| @@ -637,7 +637,7 @@ func (wm *WorkspaceManager) splitNode(target *paneNode, direction string, existi | |||||
| 637 | wm.app.panes = append(wm.app.panes, newPane) | 637 | wm.app.panes = append(wm.app.panes, newPane) | ||
| 638 | } else { | 638 | } else { | ||
| 639 | // For existing popup panes, they're already in app.panes from createPane() in handlePopupReadyToShow | 639 | // For existing popup panes, they're already in app.panes from createPane() in handlePopupReadyToShow | ||
| 640 | - | log.Printf("[workspace] Skipping app.panes append for existing popup pane (already added)") | 640 | + | logging.Info(fmt.Sprintf("[workspace] Skipping app.panes append for existing popup pane (already added)")) |
| 641 | } | 641 | } | ||
| 642 | 642 | ||||
| 643 | if newPane.zoomController != nil { | 643 | if newPane.zoomController != nil { | ||
| @@ -755,26 +755,26 @@ func (wm *WorkspaceManager) swapContainers(grand *paneNode, parent *paneNode, si | |||||
| 755 | // so it has no GTK parent now. | 755 | // so it has no GTK parent now. | ||
| 756 | 756 | ||||
| 757 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | 757 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | ||
| 758 | - | log.Printf("[workspace] ERROR: Cannot promote sibling to root - tab manager not initialized") | 758 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: Cannot promote sibling to root - tab manager not initialized")) |
| 759 | return | 759 | return | ||
| 760 | } | 760 | } | ||
| 761 | 761 | ||||
| 762 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | 762 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | ||
| 763 | if !ok || contentBox == nil { | 763 | if !ok || contentBox == nil { | ||
| 764 | - | log.Printf("[workspace] ERROR: ContentArea is not a Box") | 764 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: ContentArea is not a Box")) |
| 765 | return | 765 | return | ||
| 766 | } | 766 | } | ||
| 767 | 767 | ||||
| 768 | // CRITICAL: Remove the old paned from ContentArea first | 768 | // CRITICAL: Remove the old paned from ContentArea first | ||
| 769 | if parent != nil && parent.container != nil { | 769 | if parent != nil && parent.container != nil { | ||
| 770 | contentBox.Remove(parent.container) | 770 | contentBox.Remove(parent.container) | ||
| 771 | - | log.Printf("[workspace] Removed old paned %p from ContentArea", parent.container) | 771 | + | logging.Debug(fmt.Sprintf("[workspace] Removed old paned %p from ContentArea", parent.container)) |
| 772 | } | 772 | } | ||
| 773 | 773 | ||||
| 774 | // Now add sibling to ContentArea as the new root | 774 | // Now add sibling to ContentArea as the new root | ||
| 775 | contentBox.Append(sibling.container) | 775 | contentBox.Append(sibling.container) | ||
| 776 | webkit.WidgetSetVisible(sibling.container, true) | 776 | webkit.WidgetSetVisible(sibling.container, true) | ||
| 777 | - | log.Printf("[workspace] Promoted sibling %p to root in ContentArea", sibling.container) | 777 | + | logging.Debug(fmt.Sprintf("[workspace] Promoted sibling %p to root in ContentArea", sibling.container)) |
| 778 | } | 778 | } | ||
| 779 | return | 779 | return | ||
| 780 | } | 780 | } | ||
| @@ -789,15 +789,15 @@ func (wm *WorkspaceManager) swapContainers(grand *paneNode, parent *paneNode, si | |||||
| 789 | // GTK4 PanedSetStartChild/EndChild auto-unparent from current parent | 789 | // GTK4 PanedSetStartChild/EndChild auto-unparent from current parent | ||
| 790 | // Focus was already cleared before unparenting closed pane | 790 | // Focus was already cleared before unparenting closed pane | ||
| 791 | if parent := webkit.WidgetGetParent(sibling.container); parent != nil && parent != grand.container { | 791 | if parent := webkit.WidgetGetParent(sibling.container); parent != nil && parent != grand.container { | ||
| 792 | - | log.Printf("[workspace] widget %p reparenting from %p to %p (GTK4 will auto-unparent)", sibling.container, parent, grand.container) | 792 | + | logging.Debug(fmt.Sprintf("[workspace] widget %p reparenting from %p to %p (GTK4 will auto-unparent)", sibling.container, parent, grand.container)) |
| 793 | } | 793 | } | ||
| 794 | 794 | ||||
| 795 | if grand.left == sibling { | 795 | if grand.left == sibling { | ||
| 796 | grandPaned.SetStartChild(sibling.container) | 796 | grandPaned.SetStartChild(sibling.container) | ||
| 797 | - | log.Printf("[workspace] swapContainers: set widget %p as start child of paned %p", sibling.container, grand.container) | 797 | + | logging.Debug(fmt.Sprintf("[workspace] swapContainers: set widget %p as start child of paned %p", sibling.container, grand.container)) |
| 798 | } else { | 798 | } else { | ||
| 799 | grandPaned.SetEndChild(sibling.container) | 799 | grandPaned.SetEndChild(sibling.container) | ||
| 800 | - | log.Printf("[workspace] swapContainers: set widget %p as end child of paned %p", sibling.container, grand.container) | 800 | + | logging.Debug(fmt.Sprintf("[workspace] swapContainers: set widget %p as end child of paned %p", sibling.container, grand.container)) |
| 801 | } | 801 | } | ||
| 802 | webkit.WidgetQueueAllocate(grand.container) | 802 | webkit.WidgetQueueAllocate(grand.container) | ||
| 803 | } | 803 | } | ||
| @@ -821,7 +821,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||||
| 821 | return | 821 | return | ||
| 822 | } | 822 | } | ||
| 823 | 823 | ||||
| 824 | - | log.Printf("[workspace] Cascading promotion: replacing paned %p with its only child %p", singleChildPaned, onlyChild) | 824 | + | logging.Info(fmt.Sprintf("[workspace] Cascading promotion: replacing paned %p with its only child %p", singleChildPaned, onlyChild)) |
| 825 | 825 | ||||
| 826 | greatGrandparent := singleChildPaned.parent | 826 | greatGrandparent := singleChildPaned.parent | ||
| 827 | 827 | ||||
| @@ -830,7 +830,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||||
| 830 | if greatGrandparent == nil { | 830 | if greatGrandparent == nil { | ||
| 831 | // Single child becomes new root | 831 | // Single child becomes new root | ||
| 832 | wm.root = onlyChild | 832 | wm.root = onlyChild | ||
| 833 | - | log.Printf("[workspace] Only child %p promoted to root", onlyChild) | 833 | + | logging.Debug(fmt.Sprintf("[workspace] Only child %p promoted to root", onlyChild)) |
| 834 | 834 | ||||
| 835 | // Attach to window | 835 | // Attach to window | ||
| 836 | if onlyChild.container != nil { | 836 | if onlyChild.container != nil { | ||
| @@ -847,7 +847,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||||
| 847 | greatGrandparent.right = onlyChild | 847 | greatGrandparent.right = onlyChild | ||
| 848 | } | 848 | } | ||
| 849 | 849 | ||||
| 850 | - | log.Printf("[workspace] Only child %p attached to great-grandparent %p", onlyChild, greatGrandparent) | 850 | + | logging.Debug(fmt.Sprintf("[workspace] Only child %p attached to great-grandparent %p", onlyChild, greatGrandparent)) |
| 851 | 851 | ||||
| 852 | // Reparent widget in GTK | 852 | // Reparent widget in GTK | ||
| 853 | if greatGrandPaned, ok := greatGrandparent.container.(*gtk.Paned); ok && greatGrandPaned != nil && onlyChild.container != nil { | 853 | if greatGrandPaned, ok := greatGrandparent.container.(*gtk.Paned); ok && greatGrandPaned != nil && onlyChild.container != nil { | ||
| @@ -880,7 +880,7 @@ func (wm *WorkspaceManager) cascadePromotion(singleChildPaned *paneNode) { | |||||
| 880 | 880 | ||||
| 881 | // Check if great-grandparent now also has only one child (recursive case) | 881 | // Check if great-grandparent now also has only one child (recursive case) | ||
| 882 | if greatGrandparent != nil && ((greatGrandparent.left == nil) != (greatGrandparent.right == nil)) { | 882 | if greatGrandparent != nil && ((greatGrandparent.left == nil) != (greatGrandparent.right == nil)) { | ||
| 883 | - | log.Printf("[workspace] Great-grandparent %p also has only one child, continuing cascade", greatGrandparent) | 883 | + | logging.Info(fmt.Sprintf("[workspace] Great-grandparent %p also has only one child, continuing cascade", greatGrandparent)) |
| 884 | wm.cascadePromotion(greatGrandparent) | 884 | wm.cascadePromotion(greatGrandparent) | ||
| 885 | } | 885 | } | ||
| 886 | } | 886 | } | ||
| @@ -900,7 +900,7 @@ func (wm *WorkspaceManager) attachRoot(root *paneNode) { | |||||
| 900 | // Detach from previous container (paned, stack, etc.) before attaching to ContentArea | 900 | // Detach from previous container (paned, stack, etc.) before attaching to ContentArea | ||
| 901 | // While GTK4 auto-unparents for paned operations, Box.Append requires manual unparent | 901 | // While GTK4 auto-unparents for paned operations, Box.Append requires manual unparent | ||
| 902 | if parent := webkit.WidgetGetParent(root.container); parent != nil { | 902 | if parent := webkit.WidgetGetParent(root.container); parent != nil { | ||
| 903 | - | log.Printf("[workspace] unparenting widget %p from parent %p before root attach", root.container, parent) | 903 | + | logging.Debug(fmt.Sprintf("[workspace] unparenting widget %p from parent %p before root attach", root.container, parent)) |
| 904 | webkit.WidgetUnparent(root.container) | 904 | webkit.WidgetUnparent(root.container) | ||
| 905 | // Note: In gotk4, the Go object reference remains valid after unparent | 905 | // Note: In gotk4, the Go object reference remains valid after unparent | ||
| 906 | // Only the GTK parent relationship is cleared | 906 | // Only the GTK parent relationship is cleared | ||
| @@ -913,13 +913,13 @@ func (wm *WorkspaceManager) attachRoot(root *paneNode) { | |||||
| 913 | 913 | ||||
| 914 | // Add to ContentArea (the old root will be orphaned and garbage collected) | 914 | // Add to ContentArea (the old root will be orphaned and garbage collected) | ||
| 915 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | 915 | if wm.app == nil || wm.app.tabManager == nil || wm.app.tabManager.ContentArea == nil { | ||
| 916 | - | log.Printf("[workspace] ERROR: Cannot attach root - tab manager not initialized") | 916 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: Cannot attach root - tab manager not initialized")) |
| 917 | return | 917 | return | ||
| 918 | } | 918 | } | ||
| 919 | 919 | ||||
| 920 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | 920 | contentBox, ok := wm.app.tabManager.ContentArea.(*gtk.Box) | ||
| 921 | if !ok || contentBox == nil { | 921 | if !ok || contentBox == nil { | ||
| 922 | - | log.Printf("[workspace] ERROR: ContentArea is not a Box") | 922 | + | logging.Error(fmt.Sprintf("[workspace] ERROR: ContentArea is not a Box")) |
| 923 | return | 923 | return | ||
| 924 | } | 924 | } | ||
| 925 | 925 | ||||
| @@ -945,9 +945,9 @@ func (wm *WorkspaceManager) attachRoot(root *paneNode) { | |||||
| 945 | 945 | ||||
| 946 | // Verify attachment succeeded | 946 | // Verify attachment succeeded | ||
| 947 | if finalParent := webkit.WidgetGetParent(root.container); finalParent == nil { | 947 | if finalParent := webkit.WidgetGetParent(root.container); finalParent == nil { | ||
| 948 | - | log.Printf("[workspace] WARNING: widget %p has no parent after SetChild", root.container) | 948 | + | logging.Warn(fmt.Sprintf("[workspace] WARNING: widget %p has no parent after SetChild", root.container)) |
| 949 | } else { | 949 | } else { | ||
| 950 | - | log.Printf("[workspace] attachRoot successful: widget %p now child of %p", root.container, finalParent) | 950 | + | logging.Debug(fmt.Sprintf("[workspace] attachRoot successful: widget %p now child of %p", root.container, finalParent)) |
| 951 | } | 951 | } | ||
| 952 | } | 952 | } | ||
| 953 | 953 | ||||
| @@ -974,7 +974,7 @@ func (wm *WorkspaceManager) cleanupPane(node *paneNode, generation uint) { | |||||
| 974 | pane.Cleanup() | 974 | pane.Cleanup() | ||
| 975 | pane.CleanupFromWorkspace(wm) | 975 | pane.CleanupFromWorkspace(wm) | ||
| 976 | if pane.webView != nil { | 976 | if pane.webView != nil { | ||
| 977 | - | log.Printf("[workspace] releasing WebView reference: %p", pane.webView) | 977 | + | logging.Debug(fmt.Sprintf("[workspace] releasing WebView reference: %p", pane.webView)) |
| 978 | pane.webView = nil | 978 | pane.webView = nil | ||
| 979 | } | 979 | } | ||
| 980 | node.pane = nil | 980 | node.pane = nil | ||
| @@ -1002,7 +1002,7 @@ func (wm *WorkspaceManager) decommissionParent(parent *paneNode, generation uint | |||||
| 1002 | if !parent.isLeaf && parent.container != nil { | 1002 | if !parent.isLeaf && parent.container != nil { | ||
| 1003 | // In gotk4, we don't need to manually destroy - GTK handles this when unparented | 1003 | // In gotk4, we don't need to manually destroy - GTK handles this when unparented | ||
| 1004 | // Just mark it for cleanup | 1004 | // Just mark it for cleanup | ||
| 1005 | - | log.Printf("[workspace] marked paned widget for cleanup: %p", parent.container) | 1005 | + | logging.Debug(fmt.Sprintf("[workspace] marked paned widget for cleanup: %p", parent.container)) |
| 1006 | } | 1006 | } | ||
| 1007 | 1007 | ||||
| 1008 | wm.cleanupPane(parent, generation) | 1008 | wm.cleanupPane(parent, generation) | ||
| @@ -1031,7 +1031,7 @@ func (wm *WorkspaceManager) promoteNewRoot(ctx closeContext, oldRoot *paneNode) | |||||
| 1031 | 1031 | ||||
| 1032 | // cleanupAndExit handles the final pane cleanup and application exit | 1032 | // cleanupAndExit handles the final pane cleanup and application exit | ||
| 1033 | func (wm *WorkspaceManager) cleanupAndExit(node *paneNode) (*paneNode, error) { | 1033 | func (wm *WorkspaceManager) cleanupAndExit(node *paneNode) (*paneNode, error) { | ||
| 1034 | - | log.Printf("[workspace] closing final pane; exiting browser") | 1034 | + | logging.Info(fmt.Sprintf("[workspace] closing final pane; exiting browser")) |
| 1035 | wm.cleanupPane(node, wm.nextCleanupGeneration()) | 1035 | wm.cleanupPane(node, wm.nextCleanupGeneration()) | ||
| 1036 | wm.detachHover(node) | 1036 | wm.detachHover(node) | ||
| 1037 | 1037 | ||||
| @@ -1098,7 +1098,7 @@ func (wm *WorkspaceManager) closePane(node *paneNode) (*paneNode, error) { | |||||
| 1098 | } else if parent.right == node { | 1098 | } else if parent.right == node { | ||
| 1099 | parentPaned.SetEndChild(nil) | 1099 | parentPaned.SetEndChild(nil) | ||
| 1100 | } | 1100 | } | ||
| 1101 | - | log.Printf("[workspace] unparented closed pane container: %p", node.container) | 1101 | + | logging.Debug(fmt.Sprintf("[workspace] unparented closed pane container: %p", node.container)) |
| 1102 | } | 1102 | } | ||
| 1103 | } | 1103 | } | ||
| 1104 | } | 1104 | } | ||
| @@ -1112,7 +1112,7 @@ func (wm *WorkspaceManager) closePane(node *paneNode) (*paneNode, error) { | |||||
| 1112 | } else if parent.right == sibling { | 1112 | } else if parent.right == sibling { | ||
| 1113 | parentPaned.SetEndChild(nil) | 1113 | parentPaned.SetEndChild(nil) | ||
| 1114 | } | 1114 | } | ||
| 1115 | - | log.Printf("[workspace] unparented sibling container %p from parent paned %p", sibling.container, parent.container) | 1115 | + | logging.Debug(fmt.Sprintf("[workspace] unparented sibling container %p from parent paned %p", sibling.container, parent.container)) |
| 1116 | } | 1116 | } | ||
| 1117 | } | 1117 | } | ||
| 1118 | 1118 | ||||
| @@ -1142,7 +1142,7 @@ func (wm *WorkspaceManager) closePane(node *paneNode) (*paneNode, error) { | |||||
| 1142 | // STEP 8: Check if grandparent now has only one child (cascade promotion needed) | 1142 | // STEP 8: Check if grandparent now has only one child (cascade promotion needed) | ||
| 1143 | if grandparent != nil && ((grandparent.left == nil) != (grandparent.right == nil)) { | 1143 | if grandparent != nil && ((grandparent.left == nil) != (grandparent.right == nil)) { | ||
| 1144 | // Grandparent has exactly one child - it should be promoted too | 1144 | // Grandparent has exactly one child - it should be promoted too | ||
| 1145 | - | log.Printf("[workspace] Grandparent %p now has only one child, cascading promotion", grandparent) | 1145 | + | logging.Info(fmt.Sprintf("[workspace] Grandparent %p now has only one child, cascading promotion", grandparent)) |
| 1146 | wm.cascadePromotion(grandparent) | 1146 | wm.cascadePromotion(grandparent) | ||
| 1147 | } | 1147 | } | ||
| 1148 | 1148 | ||||
| @@ -1278,7 +1278,7 @@ func (wm *WorkspaceManager) syncPanedDivider(paned *gtk.Paned, orientation gtk.O | |||||
| 1278 | } | 1278 | } | ||
| 1279 | 1279 | ||||
| 1280 | paned.SetPosition(pos) | 1280 | paned.SetPosition(pos) | ||
| 1281 | - | log.Printf("[workspace] Set paned position to 50%%: %d (orientation=%d, size=%dx%d)", pos, orientation, alloc.Width(), alloc.Height()) | 1281 | + | logging.Debug(fmt.Sprintf("[workspace] Set paned position to 50%%: %d (orientation=%d, size=%dx%d)", pos, orientation, alloc.Width(), alloc.Height())) |
| 1282 | return true | 1282 | return true | ||
| 1283 | } | 1283 | } | ||
| 1284 | 1284 | ||||
| @@ -1305,7 +1305,7 @@ func (wm *WorkspaceManager) syncPanedDivider(paned *gtk.Paned, orientation gtk.O | |||||
| 1305 | 1305 | ||||
| 1306 | if frames >= maxFrames { | 1306 | if frames >= maxFrames { | ||
| 1307 | alloc := paned.Allocation() | 1307 | alloc := paned.Allocation() | ||
| 1308 | - | log.Printf("[workspace] Unable to stabilize paned position after %d frames (size=%dx%d)", frames, alloc.Width(), alloc.Height()) | 1308 | + | logging.Error(fmt.Sprintf("[workspace] Unable to stabilize paned position after %d frames (size=%dx%d)", frames, alloc.Width(), alloc.Height())) |
| 1309 | return false | 1309 | return false | ||
| 1310 | } | 1310 | } | ||
| 1311 | 1311 | ||||
M internal/app/browser/workspace_popup.go +73 -72
| @@ -2,11 +2,12 @@ | |||||
| 2 | package browser | 2 | package browser | ||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | "strings" | 6 | "strings" | ||
| 7 | "time" | 7 | "time" | ||
| 8 | 8 | ||||
| 9 | "github.com/bnema/dumber/internal/config" | 9 | "github.com/bnema/dumber/internal/config" | ||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | "github.com/bnema/dumber/pkg/webkit" | 11 | "github.com/bnema/dumber/pkg/webkit" | ||
| 11 | webkitgtk "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 12 | webkitgtk "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 12 | ) | 13 | ) | ||
| @@ -24,8 +25,8 @@ const ( | |||||
| 24 | // registerOAuthAutoClose sets up OAuth auto-close functionality for popups | 25 | // registerOAuthAutoClose sets up OAuth auto-close functionality for popups | ||
| 25 | // Note: OAuth detection is now handled by the main-world.js injection script | 26 | // Note: OAuth detection is now handled by the main-world.js injection script | ||
| 26 | func (wm *WorkspaceManager) registerOAuthAutoClose(view *webkit.WebView, url string) { | 27 | func (wm *WorkspaceManager) registerOAuthAutoClose(view *webkit.WebView, url string) { | ||
| 27 | - | log.Printf("[workspace] OAuth auto-close enabled for popup with URL: %s", url) | 28 | + | logging.Info(fmt.Sprintf("[workspace] OAuth auto-close enabled for popup with URL: %s", url)) |
| 28 | - | log.Printf("[workspace] OAuth detection will be handled by main-world.js injection script") | 29 | + | logging.Info(fmt.Sprintf("[workspace] OAuth detection will be handled by main-world.js injection script")) |
| 29 | } | 30 | } | ||
| 30 | 31 | ||||
| 31 | // insertIndependentPane inserts a new independent pane next to the source | 32 | // insertIndependentPane inserts a new independent pane next to the source | ||
| @@ -59,7 +60,7 @@ func (wm *WorkspaceManager) configureRelatedPopup(sourceNode *paneNode, webView | |||||
| 59 | related := webView | 60 | related := webView | ||
| 60 | newPane, err := wm.createPane(related) | 61 | newPane, err := wm.createPane(related) | ||
| 61 | if err != nil { | 62 | if err != nil { | ||
| 62 | - | log.Printf("[workspace] failed to create related popup pane: %v", err) | 63 | + | logging.Error(fmt.Sprintf("[workspace] failed to create related popup pane: %v", err)) |
| 63 | return | 64 | return | ||
| 64 | } | 65 | } | ||
| 65 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 66 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| @@ -68,7 +69,7 @@ func (wm *WorkspaceManager) configureRelatedPopup(sourceNode *paneNode, webView | |||||
| 68 | } | 69 | } | ||
| 69 | // Use splitNode with existing pane instead of insertPopupPane | 70 | // Use splitNode with existing pane instead of insertPopupPane | ||
| 70 | if _, err := wm.splitNode(sourceNode, direction, newPane); err != nil { | 71 | if _, err := wm.splitNode(sourceNode, direction, newPane); err != nil { | ||
| 71 | - | log.Printf("[workspace] failed to insert related popup pane: %v", err) | 72 | + | logging.Error(fmt.Sprintf("[workspace] failed to insert related popup pane: %v", err)) |
| 72 | return | 73 | return | ||
| 73 | } | 74 | } | ||
| 74 | node := wm.viewToNode[related] | 75 | node := wm.viewToNode[related] | ||
| @@ -99,7 +100,7 @@ func (wm *WorkspaceManager) configureRelatedPopup(sourceNode *paneNode, webView | |||||
| 99 | sourceNode.activePopupChildren = make([]string, 0) | 100 | sourceNode.activePopupChildren = make([]string, 0) | ||
| 100 | } | 101 | } | ||
| 101 | sourceNode.activePopupChildren = append(sourceNode.activePopupChildren, popupWebViewID) | 102 | sourceNode.activePopupChildren = append(sourceNode.activePopupChildren, popupWebViewID) | ||
| 102 | - | log.Printf("[workspace] Added popup %s to parent's activePopupChildren (count: %d)", popupWebViewID, len(sourceNode.activePopupChildren)) | 103 | + | logging.Debug(fmt.Sprintf("[workspace] Added popup %s to parent's activePopupChildren (count: %d)", popupWebViewID, len(sourceNode.activePopupChildren))) |
| 103 | 104 | ||||
| 104 | // Register navigation policy handler for parent to block script-initiated navigations | 105 | // Register navigation policy handler for parent to block script-initiated navigations | ||
| 105 | // when it has active popup children (prevents OAuth from hijacking parent pane) | 106 | // when it has active popup children (prevents OAuth from hijacking parent pane) | ||
| @@ -112,27 +113,27 @@ func (wm *WorkspaceManager) configureRelatedPopup(sourceNode *paneNode, webView | |||||
| 112 | 113 | ||||
| 113 | // Block script-initiated navigations if this pane has active popup children | 114 | // Block script-initiated navigations if this pane has active popup children | ||
| 114 | if len(sourceNode.activePopupChildren) > 0 { | 115 | if len(sourceNode.activePopupChildren) > 0 { | ||
| 115 | - | log.Printf("[workspace] BLOCKED script-initiated navigation in parent pane (has %d active popups): %s", len(sourceNode.activePopupChildren), url) | 116 | + | logging.Info(fmt.Sprintf("[workspace] BLOCKED script-initiated navigation in parent pane (has %d active popups): %s", len(sourceNode.activePopupChildren), url)) |
| 116 | return false | 117 | return false | ||
| 117 | } | 118 | } | ||
| 118 | 119 | ||||
| 119 | // Allow if no active popups | 120 | // Allow if no active popups | ||
| 120 | return true | 121 | return true | ||
| 121 | }) | 122 | }) | ||
| 122 | - | log.Printf("[workspace] Registered navigation policy handler for parent pane") | 123 | + | logging.Debug(fmt.Sprintf("[workspace] Registered navigation policy handler for parent pane")) |
| 123 | } | 124 | } | ||
| 124 | } | 125 | } | ||
| 125 | 126 | ||||
| 126 | // Pipe into existing auto-close flow only for popups (confirmed by detection) | 127 | // Pipe into existing auto-close flow only for popups (confirmed by detection) | ||
| 127 | related.RegisterCloseHandler(func() { | 128 | related.RegisterCloseHandler(func() { | ||
| 128 | - | log.Printf("[workspace] Popup requesting close via window.close()") | 129 | + | logging.Info(fmt.Sprintf("[workspace] Popup requesting close via window.close()")) |
| 129 | 130 | ||||
| 130 | // Remove this popup from parent's activePopupChildren | 131 | // Remove this popup from parent's activePopupChildren | ||
| 131 | if sourceNode != nil && popupWebViewID != "" { | 132 | if sourceNode != nil && popupWebViewID != "" { | ||
| 132 | for i, childID := range sourceNode.activePopupChildren { | 133 | for i, childID := range sourceNode.activePopupChildren { | ||
| 133 | if childID == popupWebViewID { | 134 | if childID == popupWebViewID { | ||
| 134 | sourceNode.activePopupChildren = append(sourceNode.activePopupChildren[:i], sourceNode.activePopupChildren[i+1:]...) | 135 | sourceNode.activePopupChildren = append(sourceNode.activePopupChildren[:i], sourceNode.activePopupChildren[i+1:]...) | ||
| 135 | - | log.Printf("[workspace] Removed popup %s from parent's activePopupChildren (remaining: %d)", popupWebViewID, len(sourceNode.activePopupChildren)) | 136 | + | logging.Debug(fmt.Sprintf("[workspace] Removed popup %s from parent's activePopupChildren (remaining: %d)", popupWebViewID, len(sourceNode.activePopupChildren))) |
| 136 | break | 137 | break | ||
| 137 | } | 138 | } | ||
| 138 | } | 139 | } | ||
| @@ -145,7 +146,7 @@ func (wm *WorkspaceManager) configureRelatedPopup(sourceNode *paneNode, webView | |||||
| 145 | return false | 146 | return false | ||
| 146 | } | 147 | } | ||
| 147 | if err := wm.ClosePane(n); err != nil { | 148 | if err := wm.ClosePane(n); err != nil { | ||
| 148 | - | log.Printf("[workspace] Failed to close popup pane: %v", err) | 149 | + | logging.Error(fmt.Sprintf("[workspace] Failed to close popup pane: %v", err)) |
| 149 | } | 150 | } | ||
| 150 | return false | 151 | return false | ||
| 151 | }, n) | 152 | }, n) | ||
| @@ -179,32 +180,32 @@ func (wm *WorkspaceManager) applyPopupSizeConstraints(view *webkit.WebView, widt | |||||
| 179 | if container := view.RootWidget(); container != nil { | 180 | if container := view.RootWidget(); container != nil { | ||
| 180 | webkit.WidgetSetSizeRequest(container, minWidth, minHeight) | 181 | webkit.WidgetSetSizeRequest(container, minWidth, minHeight) | ||
| 181 | webkit.WidgetQueueResize(container) | 182 | webkit.WidgetQueueResize(container) | ||
| 182 | - | log.Printf("[workspace] Applied minimum size %dx%d to popup container=%p", minWidth, minHeight, container) | 183 | + | logging.Debug(fmt.Sprintf("[workspace] Applied minimum size %dx%d to popup container=%p", minWidth, minHeight, container)) |
| 183 | } | 184 | } | ||
| 184 | 185 | ||||
| 185 | // Apply to WebView widget | 186 | // Apply to WebView widget | ||
| 186 | if webViewWidget := view.Widget(); webViewWidget != nil { | 187 | if webViewWidget := view.Widget(); webViewWidget != nil { | ||
| 187 | webkit.WidgetSetSizeRequest(webViewWidget, minWidth, minHeight) | 188 | webkit.WidgetSetSizeRequest(webViewWidget, minWidth, minHeight) | ||
| 188 | webkit.WidgetQueueResize(webViewWidget) | 189 | webkit.WidgetQueueResize(webViewWidget) | ||
| 189 | - | log.Printf("[workspace] Applied minimum size %dx%d to popup webview=%p", minWidth, minHeight, webViewWidget) | 190 | + | logging.Debug(fmt.Sprintf("[workspace] Applied minimum size %dx%d to popup webview=%p", minWidth, minHeight, webViewWidget)) |
| 190 | } | 191 | } | ||
| 191 | } | 192 | } | ||
| 192 | 193 | ||||
| 193 | // shouldAutoClose checks simple OAuth-like URL patterns and config flag | 194 | // shouldAutoClose checks simple OAuth-like URL patterns and config flag | ||
| 194 | func (wm *WorkspaceManager) shouldAutoClose(url string) bool { | 195 | func (wm *WorkspaceManager) shouldAutoClose(url string) bool { | ||
| 195 | - | log.Printf("[workspace] shouldAutoClose called for URL: %s", url) | 196 | + | logging.Info(fmt.Sprintf("[workspace] shouldAutoClose called for URL: %s", url)) |
| 196 | 197 | ||||
| 197 | if wm == nil || wm.app == nil || wm.app.config == nil { | 198 | if wm == nil || wm.app == nil || wm.app.config == nil { | ||
| 198 | - | log.Printf("[workspace] shouldAutoClose: missing config, returning true") | 199 | + | logging.Error(fmt.Sprintf("[workspace] shouldAutoClose: missing config, returning true")) |
| 199 | return true | 200 | return true | ||
| 200 | } | 201 | } | ||
| 201 | if !wm.app.config.Workspace.Popups.OAuthAutoClose { | 202 | if !wm.app.config.Workspace.Popups.OAuthAutoClose { | ||
| 202 | - | log.Printf("[workspace] shouldAutoClose: OAuthAutoClose disabled in config, returning false") | 203 | + | logging.Info(fmt.Sprintf("[workspace] shouldAutoClose: OAuthAutoClose disabled in config, returning false")) |
| 203 | return false | 204 | return false | ||
| 204 | } | 205 | } | ||
| 205 | 206 | ||||
| 206 | u := strings.ToLower(url) | 207 | u := strings.ToLower(url) | ||
| 207 | - | log.Printf("[workspace] shouldAutoClose: checking lowercase URL: %s", u) | 208 | + | logging.Debug(fmt.Sprintf("[workspace] shouldAutoClose: checking lowercase URL: %s", u)) |
| 208 | 209 | ||||
| 209 | // RFC 6749 compliant OAuth 2.0 URL patterns | 210 | // RFC 6749 compliant OAuth 2.0 URL patterns | ||
| 210 | oauthPatterns := []string{ | 211 | oauthPatterns := []string{ | ||
| @@ -225,12 +226,12 @@ func (wm *WorkspaceManager) shouldAutoClose(url string) bool { | |||||
| 225 | // Check for OAuth patterns in URL | 226 | // Check for OAuth patterns in URL | ||
| 226 | for _, pattern := range oauthPatterns { | 227 | for _, pattern := range oauthPatterns { | ||
| 227 | if strings.Contains(u, pattern) { | 228 | if strings.Contains(u, pattern) { | ||
| 228 | - | log.Printf("[workspace] shouldAutoClose: MATCHED pattern '%s' in URL, returning true", pattern) | 229 | + | logging.Info(fmt.Sprintf("[workspace] shouldAutoClose: MATCHED pattern '%s' in URL, returning true", pattern)) |
| 229 | return true | 230 | return true | ||
| 230 | } | 231 | } | ||
| 231 | } | 232 | } | ||
| 232 | 233 | ||||
| 233 | - | log.Printf("[workspace] shouldAutoClose: no OAuth patterns matched, returning false") | 234 | + | logging.Info(fmt.Sprintf("[workspace] shouldAutoClose: no OAuth patterns matched, returning false")) |
| 234 | return false | 235 | return false | ||
| 235 | } | 236 | } | ||
| 236 | 237 | ||||
| @@ -247,14 +248,14 @@ func (wm *WorkspaceManager) handleDetectedWindowType(sourceNode *paneNode, webVi | |||||
| 247 | return // Already placed | 248 | return // Already placed | ||
| 248 | } | 249 | } | ||
| 249 | 250 | ||||
| 250 | - | log.Printf("[workspace] Smart detection result: type=%d url=%s", windowType, url) | 251 | + | logging.Info(fmt.Sprintf("[workspace] Smart detection result: type=%d url=%s", windowType, url)) |
| 251 | 252 | ||||
| 252 | switch windowType { | 253 | switch windowType { | ||
| 253 | case webkit.WindowTypeTab: | 254 | case webkit.WindowTypeTab: | ||
| 254 | // For tabs, create a NEW independent WebView (can't use the related one) | 255 | // For tabs, create a NEW independent WebView (can't use the related one) | ||
| 255 | webkitCfg, err := wm.app.buildWebkitConfig() | 256 | webkitCfg, err := wm.app.buildWebkitConfig() | ||
| 256 | if err != nil { | 257 | if err != nil { | ||
| 257 | - | log.Printf("[workspace] failed to build webkit config for tab: %v", err) | 258 | + | logging.Error(fmt.Sprintf("[workspace] failed to build webkit config for tab: %v", err)) |
| 258 | return | 259 | return | ||
| 259 | } | 260 | } | ||
| 260 | // Tabs are embedded in workspace paned containers - no separate window needed | 261 | // Tabs are embedded in workspace paned containers - no separate window needed | ||
| @@ -263,7 +264,7 @@ func (wm *WorkspaceManager) handleDetectedWindowType(sourceNode *paneNode, webVi | |||||
| 263 | // Create independent WebView matching our standard tab insertion path | 264 | // Create independent WebView matching our standard tab insertion path | ||
| 264 | independentView, err := webkit.NewWebView(webkitCfg) | 265 | independentView, err := webkit.NewWebView(webkitCfg) | ||
| 265 | if err != nil { | 266 | if err != nil { | ||
| 266 | - | log.Printf("[workspace] failed to create independent tab WebView: %v", err) | 267 | + | logging.Error(fmt.Sprintf("[workspace] failed to create independent tab WebView: %v", err)) |
| 267 | return | 268 | return | ||
| 268 | } | 269 | } | ||
| 269 | 270 | ||||
| @@ -273,7 +274,7 @@ func (wm *WorkspaceManager) handleDetectedWindowType(sourceNode *paneNode, webVi | |||||
| 273 | // The related webView was just for detection - we don't use it for tabs | 274 | // The related webView was just for detection - we don't use it for tabs | ||
| 274 | // Insert the new independent view as a tab | 275 | // Insert the new independent view as a tab | ||
| 275 | if err := wm.insertIndependentPane(sourceNode, independentView, url); err != nil { | 276 | if err := wm.insertIndependentPane(sourceNode, independentView, url); err != nil { | ||
| 276 | - | log.Printf("[workspace] Failed to insert independent pane: %v", err) | 277 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert independent pane: %v", err)) |
| 277 | } | 278 | } | ||
| 278 | 279 | ||||
| 279 | case webkit.WindowTypePopup: | 280 | case webkit.WindowTypePopup: | ||
| @@ -296,16 +297,16 @@ func (wm *WorkspaceManager) setupPopupHandling(webView *webkit.WebView, parentNo | |||||
| 296 | // Get URL from navigation action | 297 | // Get URL from navigation action | ||
| 297 | req := navAction.Request() | 298 | req := navAction.Request() | ||
| 298 | if req == nil { | 299 | if req == nil { | ||
| 299 | - | log.Printf("[workspace] create signal: no request in navigation action") | 300 | + | logging.Info(fmt.Sprintf("[workspace] create signal: no request in navigation action")) |
| 300 | return nil | 301 | return nil | ||
| 301 | } | 302 | } | ||
| 302 | url := req.URI() | 303 | url := req.URI() | ||
| 303 | 304 | ||||
| 304 | - | log.Printf("[workspace] create signal received for URL: %s", url) | 305 | + | logging.Info(fmt.Sprintf("[workspace] create signal received for URL: %s", url)) |
| 305 | 306 | ||||
| 306 | // Check if popups are enabled in config | 307 | // Check if popups are enabled in config | ||
| 307 | if wm.app.config != nil && !wm.app.config.Workspace.Popups.OpenInNewPane { | 308 | if wm.app.config != nil && !wm.app.config.Workspace.Popups.OpenInNewPane { | ||
| 308 | - | log.Printf("[workspace] Popup creation disabled in config - blocking popup") | 309 | + | logging.Info(fmt.Sprintf("[workspace] Popup creation disabled in config - blocking popup")) |
| 309 | return nil | 310 | return nil | ||
| 310 | } | 311 | } | ||
| 311 | 312 | ||||
| @@ -313,59 +314,59 @@ func (wm *WorkspaceManager) setupPopupHandling(webView *webkit.WebView, parentNo | |||||
| 313 | // WebKit requires the related-view property to be set during construction for proper lifecycle | 314 | // WebKit requires the related-view property to be set during construction for proper lifecycle | ||
| 314 | // This ensures session/cookie sharing and proper WindowFeatures initialization | 315 | // This ensures session/cookie sharing and proper WindowFeatures initialization | ||
| 315 | // All initialization (wrapping, container, scripts) happens in ready-to-show | 316 | // All initialization (wrapping, container, scripts) happens in ready-to-show | ||
| 316 | - | log.Printf("[workspace] Step 1: Creating bare gotk4 WebView with related-view property") | 317 | + | logging.Debug(fmt.Sprintf("[workspace] Step 1: Creating bare gotk4 WebView with related-view property")) |
| 317 | barePopupView := webkit.NewBareRelatedWebView(webView.GtkWebView()) | 318 | barePopupView := webkit.NewBareRelatedWebView(webView.GtkWebView()) | ||
| 318 | if barePopupView == nil { | 319 | if barePopupView == nil { | ||
| 319 | - | log.Printf("[workspace] Failed to create bare related popup WebView - blocking popup") | 320 | + | logging.Error(fmt.Sprintf("[workspace] Failed to create bare related popup WebView - blocking popup")) |
| 320 | return nil | 321 | return nil | ||
| 321 | } | 322 | } | ||
| 322 | - | log.Printf("[workspace] Step 1 complete: bare related gotk4 WebView created=%p (parent=%p)", barePopupView, webView.GtkWebView()) | 323 | + | logging.Debug(fmt.Sprintf("[workspace] Step 1 complete: bare related gotk4 WebView created=%p (parent=%p)", barePopupView, webView.GtkWebView())) |
| 323 | 324 | ||||
| 324 | // Wrap the bare view immediately to get a proper ID | 325 | // Wrap the bare view immediately to get a proper ID | ||
| 325 | // BUT don't initialize it yet - that happens in ready-to-show | 326 | // BUT don't initialize it yet - that happens in ready-to-show | ||
| 326 | - | log.Printf("[workspace] Step 2: Wrapping bare view (no initialization)") | 327 | + | logging.Info(fmt.Sprintf("[workspace] Step 2: Wrapping bare view (no initialization)")) |
| 327 | wrappedView := webkit.WrapBareWebView(barePopupView) | 328 | wrappedView := webkit.WrapBareWebView(barePopupView) | ||
| 328 | if wrappedView == nil { | 329 | if wrappedView == nil { | ||
| 329 | - | log.Printf("[workspace] Failed to wrap bare popup WebView - blocking popup") | 330 | + | logging.Error(fmt.Sprintf("[workspace] Failed to wrap bare popup WebView - blocking popup")) |
| 330 | return nil | 331 | return nil | ||
| 331 | } | 332 | } | ||
| 332 | 333 | ||||
| 333 | // Use the wrapper's ID for tracking | 334 | // Use the wrapper's ID for tracking | ||
| 334 | popupID := wrappedView.ID() | 335 | popupID := wrappedView.ID() | ||
| 335 | - | log.Printf("[workspace] Step 2 complete: wrapped view created with ID=%d", popupID) | 336 | + | logging.Debug(fmt.Sprintf("[workspace] Step 2 complete: wrapped view created with ID=%d", popupID)) |
| 336 | 337 | ||||
| 337 | // Store wrapped view (not initialized) for initialization in ready-to-show | 338 | // Store wrapped view (not initialized) for initialization in ready-to-show | ||
| 338 | - | log.Printf("[workspace] Step 3: Storing in pendingPopups map") | 339 | + | logging.Info(fmt.Sprintf("[workspace] Step 3: Storing in pendingPopups map")) |
| 339 | wm.pendingPopups[popupID] = &pendingPopup{ | 340 | wm.pendingPopups[popupID] = &pendingPopup{ | ||
| 340 | wrappedView: wrappedView, | 341 | wrappedView: wrappedView, | ||
| 341 | parentView: webView, | 342 | parentView: webView, | ||
| 342 | parentNode: parentNode, | 343 | parentNode: parentNode, | ||
| 343 | url: url, | 344 | url: url, | ||
| 344 | } | 345 | } | ||
| 345 | - | log.Printf("[workspace] Step 3 complete: stored in pendingPopups") | 346 | + | logging.Info(fmt.Sprintf("[workspace] Step 3 complete: stored in pendingPopups")) |
| 346 | 347 | ||||
| 347 | // Connect signals immediately BEFORE returning to WebKit | 348 | // Connect signals immediately BEFORE returning to WebKit | ||
| 348 | // This is safe because we're only connecting signal handlers, not accessing properties | 349 | // This is safe because we're only connecting signal handlers, not accessing properties | ||
| 349 | // WebKit will configure WindowFeatures AFTER this callback returns, which is fine | 350 | // WebKit will configure WindowFeatures AFTER this callback returns, which is fine | ||
| 350 | - | log.Printf("[workspace] Step 4: Connecting signals immediately before returning to WebKit") | 351 | + | logging.Info(fmt.Sprintf("[workspace] Step 4: Connecting signals immediately before returning to WebKit")) |
| 351 | 352 | ||||
| 352 | // Connect ready-to-show signal | 353 | // Connect ready-to-show signal | ||
| 353 | barePopupView.ConnectReadyToShow(func() { | 354 | barePopupView.ConnectReadyToShow(func() { | ||
| 354 | - | log.Printf("[workspace] ready-to-show signal received for popup ID=%d", popupID) | 355 | + | logging.Info(fmt.Sprintf("[workspace] ready-to-show signal received for popup ID=%d", popupID)) |
| 355 | wm.handlePopupReadyToShow(popupID) | 356 | wm.handlePopupReadyToShow(popupID) | ||
| 356 | }) | 357 | }) | ||
| 357 | - | log.Printf("[workspace] Connected ready-to-show signal for popup ID=%d", popupID) | 358 | + | logging.Debug(fmt.Sprintf("[workspace] Connected ready-to-show signal for popup ID=%d", popupID)) |
| 358 | 359 | ||||
| 359 | // Connect close handler | 360 | // Connect close handler | ||
| 360 | barePopupView.ConnectClose(func() { | 361 | barePopupView.ConnectClose(func() { | ||
| 361 | - | log.Printf("[workspace] close signal received for popup ID=%d", popupID) | 362 | + | logging.Info(fmt.Sprintf("[workspace] close signal received for popup ID=%d", popupID)) |
| 362 | wm.handlePopupClose(popupID) | 363 | wm.handlePopupClose(popupID) | ||
| 363 | }) | 364 | }) | ||
| 364 | - | log.Printf("[workspace] Connected close signal for popup ID=%d", popupID) | 365 | + | logging.Debug(fmt.Sprintf("[workspace] Connected close signal for popup ID=%d", popupID)) |
| 365 | 366 | ||||
| 366 | - | log.Printf("[workspace] Step 4 complete: signals connected") | 367 | + | logging.Debug(fmt.Sprintf("[workspace] Step 4 complete: signals connected")) |
| 367 | 368 | ||||
| 368 | - | log.Printf("[workspace] Step 5: Returning wrapped view to WebKit (will extract bare view in ConnectCreate)") | 369 | + | logging.Debug(fmt.Sprintf("[workspace] Step 5: Returning wrapped view to WebKit (will extract bare view in ConnectCreate)")) |
| 369 | 370 | ||||
| 370 | // Return the wrapped view - WebKit will extract the underlying gotk4 view and configure it | 371 | // Return the wrapped view - WebKit will extract the underlying gotk4 view and configure it | ||
| 371 | return wrappedView | 372 | return wrappedView | ||
| @@ -376,12 +377,12 @@ func (wm *WorkspaceManager) setupPopupHandling(webView *webkit.WebView, parentNo | |||||
| 376 | func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | 377 | func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | ||
| 377 | info, ok := wm.pendingPopups[popupID] | 378 | info, ok := wm.pendingPopups[popupID] | ||
| 378 | if !ok { | 379 | if !ok { | ||
| 379 | - | log.Printf("[workspace] ready-to-show for unknown popup ID: %d", popupID) | 380 | + | logging.Warn(fmt.Sprintf("[workspace] ready-to-show for unknown popup ID: %d", popupID)) |
| 380 | return | 381 | return | ||
| 381 | } | 382 | } | ||
| 382 | delete(wm.pendingPopups, popupID) | 383 | delete(wm.pendingPopups, popupID) | ||
| 383 | 384 | ||||
| 384 | - | log.Printf("[workspace] ready-to-show received for popup ID=%d, starting full initialization", popupID) | 385 | + | logging.Info(fmt.Sprintf("[workspace] ready-to-show received for popup ID=%d, starting full initialization", popupID)) |
| 385 | 386 | ||||
| 386 | // NOW it's safe to initialize (WebKit has configured WindowProperties) | 387 | // NOW it's safe to initialize (WebKit has configured WindowProperties) | ||
| 387 | // We already have the wrapped view from setupPopupHandling | 388 | // We already have the wrapped view from setupPopupHandling | ||
| @@ -390,30 +391,30 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 390 | // Build webkit config for initialization | 391 | // Build webkit config for initialization | ||
| 391 | webkitCfg, err := wm.app.buildWebkitConfig() | 392 | webkitCfg, err := wm.app.buildWebkitConfig() | ||
| 392 | if err != nil { | 393 | if err != nil { | ||
| 393 | - | log.Printf("[workspace] Failed to build webkit config for popup initialization: %v", err) | 394 | + | logging.Error(fmt.Sprintf("[workspace] Failed to build webkit config for popup initialization: %v", err)) |
| 394 | return | 395 | return | ||
| 395 | } | 396 | } | ||
| 396 | webkitCfg.CreateWindow = false // Embedded in workspace | 397 | webkitCfg.CreateWindow = false // Embedded in workspace | ||
| 397 | 398 | ||||
| 398 | // Initialize the wrapped view with full configuration | 399 | // Initialize the wrapped view with full configuration | ||
| 399 | if err := wrappedView.InitializeFromBare(webkitCfg); err != nil { | 400 | if err := wrappedView.InitializeFromBare(webkitCfg); err != nil { | ||
| 400 | - | log.Printf("[workspace] Failed to initialize popup WebView: %v", err) | 401 | + | logging.Error(fmt.Sprintf("[workspace] Failed to initialize popup WebView: %v", err)) |
| 401 | return | 402 | return | ||
| 402 | } | 403 | } | ||
| 403 | 404 | ||||
| 404 | // Register with content blocking service for network/cosmetic filtering | 405 | // Register with content blocking service for network/cosmetic filtering | ||
| 405 | wm.app.RegisterWebViewForFiltering(wrappedView) | 406 | wm.app.RegisterWebViewForFiltering(wrappedView) | ||
| 406 | 407 | ||||
| 407 | - | log.Printf("[workspace] Successfully initialized popup WebView ID %d", wrappedView.ID()) | 408 | + | logging.Debug(fmt.Sprintf("[workspace] Successfully initialized popup WebView ID %d", wrappedView.ID())) |
| 408 | 409 | ||||
| 409 | // Create pane for the fully initialized WebView | 410 | // Create pane for the fully initialized WebView | ||
| 410 | popupPane, err := wm.createPane(wrappedView) | 411 | popupPane, err := wm.createPane(wrappedView) | ||
| 411 | if err != nil { | 412 | if err != nil { | ||
| 412 | - | log.Printf("[workspace] Failed to create popup pane: %v", err) | 413 | + | logging.Error(fmt.Sprintf("[workspace] Failed to create popup pane: %v", err)) |
| 413 | return | 414 | return | ||
| 414 | } | 415 | } | ||
| 415 | 416 | ||||
| 416 | - | log.Printf("[workspace] Created popup pane, now inserting into workspace") | 417 | + | logging.Info(fmt.Sprintf("[workspace] Created popup pane, now inserting into workspace")) |
| 417 | 418 | ||||
| 418 | // Determine window type from node (set during setupPopupHandling) | 419 | // Determine window type from node (set during setupPopupHandling) | ||
| 419 | node := wm.viewToNode[wrappedView] | 420 | node := wm.viewToNode[wrappedView] | ||
| @@ -428,10 +429,10 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 428 | var behaviorStr string | 429 | var behaviorStr string | ||
| 429 | if windowType == webkit.WindowTypeTab { | 430 | if windowType == webkit.WindowTypeTab { | ||
| 430 | behaviorStr = wm.app.config.Workspace.Popups.BlankTargetBehavior | 431 | behaviorStr = wm.app.config.Workspace.Popups.BlankTargetBehavior | ||
| 431 | - | log.Printf("[workspace] WindowTypeTab (_blank link) detected, using BlankTargetBehavior: %s", behaviorStr) | 432 | + | logging.Debug(fmt.Sprintf("[workspace] WindowTypeTab (_blank link) detected, using BlankTargetBehavior: %s", behaviorStr)) |
| 432 | } else { | 433 | } else { | ||
| 433 | behaviorStr = string(wm.app.config.Workspace.Popups.Behavior) | 434 | behaviorStr = string(wm.app.config.Workspace.Popups.Behavior) | ||
| 434 | - | log.Printf("[workspace] WindowTypePopup detected, using Behavior: %s", behaviorStr) | 435 | + | logging.Debug(fmt.Sprintf("[workspace] WindowTypePopup detected, using Behavior: %s", behaviorStr)) |
| 435 | } | 436 | } | ||
| 436 | 437 | ||||
| 437 | // Map string to PopupBehavior enum | 438 | // Map string to PopupBehavior enum | ||
| @@ -447,7 +448,7 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 447 | behavior = config.PopupBehaviorSplit // Fallback | 448 | behavior = config.PopupBehaviorSplit // Fallback | ||
| 448 | } | 449 | } | ||
| 449 | 450 | ||||
| 450 | - | log.Printf("[workspace] Final behavior: %s", behavior) | 451 | + | logging.Info(fmt.Sprintf("[workspace] Final behavior: %s", behavior)) |
| 451 | 452 | ||||
| 452 | switch behavior { | 453 | switch behavior { | ||
| 453 | case config.PopupBehaviorSplit: | 454 | case config.PopupBehaviorSplit: | ||
| @@ -457,7 +458,7 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 457 | direction = DirectionRight | 458 | direction = DirectionRight | ||
| 458 | } | 459 | } | ||
| 459 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | 460 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | ||
| 460 | - | log.Printf("[workspace] Failed to insert split popup pane: %v", err) | 461 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert split popup pane: %v", err)) |
| 461 | return | 462 | return | ||
| 462 | } | 463 | } | ||
| 463 | 464 | ||||
| @@ -465,7 +466,7 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 465 | // Stacked pane behavior | 466 | // Stacked pane behavior | ||
| 466 | _, err := wm.stackedPaneManager.StackPane(info.parentNode) | 467 | _, err := wm.stackedPaneManager.StackPane(info.parentNode) | ||
| 467 | if err != nil { | 468 | if err != nil { | ||
| 468 | - | log.Printf("[workspace] Failed to stack existing pane: %v", err) | 469 | + | logging.Error(fmt.Sprintf("[workspace] Failed to stack existing pane: %v", err)) |
| 469 | return | 470 | return | ||
| 470 | } | 471 | } | ||
| 471 | // Insert the popup pane in the same container - use splitNode with existing popup pane | 472 | // Insert the popup pane in the same container - use splitNode with existing popup pane | ||
| @@ -474,44 +475,44 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 474 | direction = DirectionRight | 475 | direction = DirectionRight | ||
| 475 | } | 476 | } | ||
| 476 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | 477 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | ||
| 477 | - | log.Printf("[workspace] Failed to insert stacked popup pane: %v", err) | 478 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert stacked popup pane: %v", err)) |
| 478 | return | 479 | return | ||
| 479 | } | 480 | } | ||
| 480 | 481 | ||||
| 481 | case config.PopupBehaviorTabbed: | 482 | case config.PopupBehaviorTabbed: | ||
| 482 | // Tabbed behavior - TODO: implement proper tabbed interface | 483 | // Tabbed behavior - TODO: implement proper tabbed interface | ||
| 483 | - | log.Printf("[workspace] WARNING: Tabbed popup behavior not yet implemented, falling back to split") | 484 | + | logging.Warn(fmt.Sprintf("[workspace] WARNING: Tabbed popup behavior not yet implemented, falling back to split")) |
| 484 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 485 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| 485 | if direction == "" { | 486 | if direction == "" { | ||
| 486 | direction = DirectionRight | 487 | direction = DirectionRight | ||
| 487 | } | 488 | } | ||
| 488 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | 489 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | ||
| 489 | - | log.Printf("[workspace] Failed to insert tabbed popup pane: %v", err) | 490 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert tabbed popup pane: %v", err)) |
| 490 | return | 491 | return | ||
| 491 | } | 492 | } | ||
| 492 | 493 | ||||
| 493 | case config.PopupBehaviorWindowed: | 494 | case config.PopupBehaviorWindowed: | ||
| 494 | // Windowed behavior - opens in new workspace/window | 495 | // Windowed behavior - opens in new workspace/window | ||
| 495 | // TODO: implement new window creation with GTK | 496 | // TODO: implement new window creation with GTK | ||
| 496 | - | log.Printf("[workspace] WARNING: Windowed popup behavior not yet implemented, falling back to split") | 497 | + | logging.Warn(fmt.Sprintf("[workspace] WARNING: Windowed popup behavior not yet implemented, falling back to split")) |
| 497 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 498 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| 498 | if direction == "" { | 499 | if direction == "" { | ||
| 499 | direction = DirectionRight | 500 | direction = DirectionRight | ||
| 500 | } | 501 | } | ||
| 501 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | 502 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | ||
| 502 | - | log.Printf("[workspace] Failed to insert windowed popup pane: %v", err) | 503 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert windowed popup pane: %v", err)) |
| 503 | return | 504 | return | ||
| 504 | } | 505 | } | ||
| 505 | 506 | ||||
| 506 | default: | 507 | default: | ||
| 507 | // Fallback to split if unknown behavior | 508 | // Fallback to split if unknown behavior | ||
| 508 | - | log.Printf("[workspace] Unknown popup behavior '%s', falling back to split", behavior) | 509 | + | logging.Warn(fmt.Sprintf("[workspace] Unknown popup behavior '%s', falling back to split", behavior)) |
| 509 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | 510 | direction := strings.ToLower(wm.app.config.Workspace.Popups.Placement) | ||
| 510 | if direction == "" { | 511 | if direction == "" { | ||
| 511 | direction = DirectionRight | 512 | direction = DirectionRight | ||
| 512 | } | 513 | } | ||
| 513 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | 514 | if _, err := wm.splitNode(info.parentNode, direction, popupPane); err != nil { | ||
| 514 | - | log.Printf("[workspace] Failed to insert popup pane: %v", err) | 515 | + | logging.Error(fmt.Sprintf("[workspace] Failed to insert popup pane: %v", err)) |
| 515 | return | 516 | return | ||
| 516 | } | 517 | } | ||
| 517 | } | 518 | } | ||
| @@ -534,8 +535,8 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 534 | info.parentNode.activePopupChildren = make([]string, 0) | 535 | info.parentNode.activePopupChildren = make([]string, 0) | ||
| 535 | } | 536 | } | ||
| 536 | info.parentNode.activePopupChildren = append(info.parentNode.activePopupChildren, popupWebViewID) | 537 | info.parentNode.activePopupChildren = append(info.parentNode.activePopupChildren, popupWebViewID) | ||
| 537 | - | log.Printf("[workspace] Added popup %s to parent's activePopupChildren (count: %d)", | 538 | + | logging.Debug(fmt.Sprintf("[workspace] Added popup %s to parent's activePopupChildren (count: %d)", |
| 538 | - | popupWebViewID, len(info.parentNode.activePopupChildren)) | 539 | + | popupWebViewID, len(info.parentNode.activePopupChildren))) |
| 539 | 540 | ||||
| 540 | // Register navigation policy handler for parent | 541 | // Register navigation policy handler for parent | ||
| 541 | if info.parentNode.pane != nil && info.parentNode.pane.WebView() != nil { | 542 | if info.parentNode.pane != nil && info.parentNode.pane.WebView() != nil { | ||
| @@ -544,8 +545,8 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 544 | return true | 545 | return true | ||
| 545 | } | 546 | } | ||
| 546 | if len(info.parentNode.activePopupChildren) > 0 { | 547 | if len(info.parentNode.activePopupChildren) > 0 { | ||
| 547 | - | log.Printf("[workspace] BLOCKED script-initiated navigation in parent pane (has %d active popups): %s", | 548 | + | logging.Info(fmt.Sprintf("[workspace] BLOCKED script-initiated navigation in parent pane (has %d active popups): %s", |
| 548 | - | len(info.parentNode.activePopupChildren), url) | 549 | + | len(info.parentNode.activePopupChildren), url)) |
| 549 | return false | 550 | return false | ||
| 550 | } | 551 | } | ||
| 551 | return true | 552 | return true | ||
| @@ -564,17 +565,17 @@ func (wm *WorkspaceManager) handlePopupReadyToShow(popupID uint64) { | |||||
| 564 | // Widgets are already shown by splitNode - no need to show again | 565 | // Widgets are already shown by splitNode - no need to show again | ||
| 565 | // GTK4 handles visibility automatically during reparenting | 566 | // GTK4 handles visibility automatically during reparenting | ||
| 566 | 567 | ||||
| 567 | - | log.Printf("[workspace] Popup pane inserted and visible for URL: %s", info.url) | 568 | + | logging.Info(fmt.Sprintf("[workspace] Popup pane inserted and visible for URL: %s", info.url)) |
| 568 | } | 569 | } | ||
| 569 | 570 | ||||
| 570 | // handlePopupClose responds to WebKit's close signal | 571 | // handlePopupClose responds to WebKit's close signal | ||
| 571 | func (wm *WorkspaceManager) handlePopupClose(popupID uint64) { | 572 | func (wm *WorkspaceManager) handlePopupClose(popupID uint64) { | ||
| 572 | - | log.Printf("[workspace] close signal received for popup native=%d", popupID) | 573 | + | logging.Info(fmt.Sprintf("[workspace] close signal received for popup native=%d", popupID)) |
| 573 | 574 | ||||
| 574 | // Check if it's in pending (not yet shown) | 575 | // Check if it's in pending (not yet shown) | ||
| 575 | if info, ok := wm.pendingPopups[popupID]; ok { | 576 | if info, ok := wm.pendingPopups[popupID]; ok { | ||
| 576 | delete(wm.pendingPopups, popupID) | 577 | delete(wm.pendingPopups, popupID) | ||
| 577 | - | log.Printf("[workspace] Popup closed before ready-to-show, cleaning up") | 578 | + | logging.Info(fmt.Sprintf("[workspace] Popup closed before ready-to-show, cleaning up")) |
| 578 | // Just remove from pending, WebKit will handle cleanup | 579 | // Just remove from pending, WebKit will handle cleanup | ||
| 579 | _ = info // avoid unused variable | 580 | _ = info // avoid unused variable | ||
| 580 | return | 581 | return | ||
| @@ -590,7 +591,7 @@ func (wm *WorkspaceManager) handlePopupClose(popupID uint64) { | |||||
| 590 | } | 591 | } | ||
| 591 | 592 | ||||
| 592 | if targetNode == nil { | 593 | if targetNode == nil { | ||
| 593 | - | log.Printf("[workspace] close signal for unknown popup ID=%d, already closed?", popupID) | 594 | + | logging.Warn(fmt.Sprintf("[workspace] close signal for unknown popup ID=%d, already closed?", popupID)) |
| 594 | return | 595 | return | ||
| 595 | } | 596 | } | ||
| 596 | 597 | ||||
| @@ -610,16 +611,16 @@ func (wm *WorkspaceManager) handlePopupClose(popupID uint64) { | |||||
| 610 | targetNode.parentPane.activePopupChildren = append( | 611 | targetNode.parentPane.activePopupChildren = append( | ||
| 611 | targetNode.parentPane.activePopupChildren[:i], | 612 | targetNode.parentPane.activePopupChildren[:i], | ||
| 612 | targetNode.parentPane.activePopupChildren[i+1:]...) | 613 | targetNode.parentPane.activePopupChildren[i+1:]...) | ||
| 613 | - | log.Printf("[workspace] Removed popup from parent's activePopupChildren (remaining: %d)", | 614 | + | logging.Debug(fmt.Sprintf("[workspace] Removed popup from parent's activePopupChildren (remaining: %d)", |
| 614 | - | len(targetNode.parentPane.activePopupChildren)) | 615 | + | len(targetNode.parentPane.activePopupChildren))) |
| 615 | break | 616 | break | ||
| 616 | } | 617 | } | ||
| 617 | } | 618 | } | ||
| 618 | } | 619 | } | ||
| 619 | 620 | ||||
| 620 | // Clean close via existing ClosePane (WebKit already stopped) | 621 | // Clean close via existing ClosePane (WebKit already stopped) | ||
| 621 | - | log.Printf("[workspace] Closing popup pane via ClosePane") | 622 | + | logging.Info(fmt.Sprintf("[workspace] Closing popup pane via ClosePane")) |
| 622 | if err := wm.ClosePane(targetNode); err != nil { | 623 | if err := wm.ClosePane(targetNode); err != nil { | ||
| 623 | - | log.Printf("[workspace] Error closing popup pane: %v", err) | 624 | + | logging.Error(fmt.Sprintf("[workspace] Error closing popup pane: %v", err)) |
| 624 | } | 625 | } | ||
| 625 | } | 626 | } | ||
M internal/app/browser/workspace_stack_lifecycle.go +6 -6
| @@ -4,7 +4,7 @@ package browser | |||||
| 4 | import ( | 4 | import ( | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | 7 | + | "github.com/bnema/dumber/internal/logging" |
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | // StackLifecycleManager manages the lifecycle hooks around stacked pane operations. | 10 | // StackLifecycleManager manages the lifecycle hooks around stacked pane operations. | ||
| @@ -34,8 +34,8 @@ func (slm *StackLifecycleManager) CloseStackedPaneWithLifecycle(node *paneNode) | |||||
| 34 | return errors.New("node is not part of a stacked pane") | 34 | return errors.New("node is not part of a stacked pane") | ||
| 35 | } | 35 | } | ||
| 36 | 36 | ||||
| 37 | - | log.Printf("[stack-lifecycle] Closing stacked pane: node=%p stack=%p stackSize=%d", | 37 | + | logging.Info(fmt.Sprintf("[stack-lifecycle] Closing stacked pane: node=%p stack=%p stackSize=%d", |
| 38 | - | node, stackNode, len(stackNode.stackedPanes)) | 38 | + | node, stackNode, len(stackNode.stackedPanes))) |
| 39 | 39 | ||||
| 40 | if err := slm.validateStackState(stackNode, "before close"); err != nil { | 40 | if err := slm.validateStackState(stackNode, "before close"); err != nil { | ||
| 41 | return fmt.Errorf("invalid stack state before close: %w", err) | 41 | return fmt.Errorf("invalid stack state before close: %w", err) | ||
| @@ -51,7 +51,7 @@ func (slm *StackLifecycleManager) CloseStackedPaneWithLifecycle(node *paneNode) | |||||
| 51 | 51 | ||||
| 52 | if slm.treeValidator != nil { | 52 | if slm.treeValidator != nil { | ||
| 53 | if err := slm.treeValidator.ValidateTree(slm.wm.root, "after stack close"); err != nil { | 53 | if err := slm.treeValidator.ValidateTree(slm.wm.root, "after stack close"); err != nil { | ||
| 54 | - | log.Printf("[stack-lifecycle] Tree validation failed after stack close: %v", err) | 54 | + | logging.Error(fmt.Sprintf("[stack-lifecycle] Tree validation failed after stack close: %v", err)) |
| 55 | } | 55 | } | ||
| 56 | } | 56 | } | ||
| 57 | 57 | ||||
| @@ -86,7 +86,7 @@ func (slm *StackLifecycleManager) validateStackState(stackNode *paneNode, operat | |||||
| 86 | } | 86 | } | ||
| 87 | } | 87 | } | ||
| 88 | 88 | ||||
| 89 | - | log.Printf("[stack-lifecycle] Stack state valid for operation '%s': size=%d activeIndex=%d", | 89 | + | logging.Info(fmt.Sprintf("[stack-lifecycle] Stack state valid for operation '%s': size=%d activeIndex=%d", |
| 90 | - | operation, len(stackNode.stackedPanes), stackNode.activeStackIndex) | 90 | + | operation, len(stackNode.stackedPanes), stackNode.activeStackIndex)) |
| 91 | return nil | 91 | return nil | ||
| 92 | } | 92 | } | ||
M internal/app/browser/workspace_utils.go +3 -4
| @@ -3,8 +3,7 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | 6 | + | "github.com/bnema/dumber/internal/logging" |
| 7 | - | ||||
| 8 | "github.com/bnema/dumber/pkg/webkit" | 7 | "github.com/bnema/dumber/pkg/webkit" | ||
| 9 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 8 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| 10 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 9 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| @@ -182,7 +181,7 @@ func (wm *WorkspaceManager) applyActivePaneBorder(node *paneNode) { | |||||
| 182 | } | 181 | } | ||
| 183 | 182 | ||||
| 184 | webkit.WidgetSetVisible(node.borderOverlay, true) | 183 | webkit.WidgetSetVisible(node.borderOverlay, true) | ||
| 185 | - | log.Printf("[workspace] Showing border overlay for pane %p", node) | 184 | + | logging.Debug(fmt.Sprintf("[workspace] Showing border overlay for pane %p", node)) |
| 186 | } | 185 | } | ||
| 187 | 186 | ||||
| 188 | // removeActivePaneBorder hides the border overlay for a pane. | 187 | // removeActivePaneBorder hides the border overlay for a pane. | ||
| @@ -192,7 +191,7 @@ func (wm *WorkspaceManager) removeActivePaneBorder(node *paneNode) { | |||||
| 192 | } | 191 | } | ||
| 193 | 192 | ||||
| 194 | webkit.WidgetSetVisible(node.borderOverlay, false) | 193 | webkit.WidgetSetVisible(node.borderOverlay, false) | ||
| 195 | - | log.Printf("[workspace] Hiding border overlay for pane %p", node) | 194 | + | logging.Debug(fmt.Sprintf("[workspace] Hiding border overlay for pane %p", node)) |
| 196 | } | 195 | } | ||
| 197 | 196 | ||||
| 198 | // Widget cleanup utilities | 197 | // Widget cleanup utilities | ||
M internal/app/control/clipboard.go +7 -6
| @@ -1,8 +1,9 @@ | |||||
| 1 | package control | 1 | package control | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | "github.com/bnema/dumber/pkg/clipboard" | 7 | "github.com/bnema/dumber/pkg/clipboard" | ||
| 7 | "github.com/bnema/dumber/pkg/webkit" | 8 | "github.com/bnema/dumber/pkg/webkit" | ||
| 8 | ) | 9 | ) | ||
| @@ -21,19 +22,19 @@ func NewClipboardController(webView *webkit.WebView) *ClipboardController { | |||||
| 21 | 22 | ||||
| 22 | // CopyCurrentURL copies the current WebView URL to the clipboard | 23 | // CopyCurrentURL copies the current WebView URL to the clipboard | ||
| 23 | func (c *ClipboardController) CopyCurrentURL() { | 24 | func (c *ClipboardController) CopyCurrentURL() { | ||
| 24 | - | log.Printf("Shortcut: Copy URL") | 25 | + | logging.Debug(fmt.Sprintf("Shortcut: Copy URL")) |
| 25 | currentURL := c.webView.GetCurrentURL() | 26 | currentURL := c.webView.GetCurrentURL() | ||
| 26 | if currentURL == "" { | 27 | if currentURL == "" { | ||
| 27 | - | log.Printf("No URL to copy") | 28 | + | logging.Warn(fmt.Sprintf("No URL to copy")) |
| 28 | c.showToast("No URL to copy", "error") | 29 | c.showToast("No URL to copy", "error") | ||
| 29 | return | 30 | return | ||
| 30 | } | 31 | } | ||
| 31 | 32 | ||||
| 32 | if err := clipboard.CopyToClipboard(currentURL); err != nil { | 33 | if err := clipboard.CopyToClipboard(currentURL); err != nil { | ||
| 33 | - | log.Printf("Failed to copy URL to clipboard: %v", err) | 34 | + | logging.Error(fmt.Sprintf("Failed to copy URL to clipboard: %v", err)) |
| 34 | c.showToast("Failed to copy URL", "error") | 35 | c.showToast("Failed to copy URL", "error") | ||
| 35 | } else { | 36 | } else { | ||
| 36 | - | log.Printf("URL copied to clipboard: %s", currentURL) | 37 | + | logging.Debug(fmt.Sprintf("URL copied to clipboard: %s", currentURL)) |
| 37 | c.showToast("URL copied to clipboard", "success") | 38 | c.showToast("URL copied to clipboard", "success") | ||
| 38 | } | 39 | } | ||
| 39 | } | 40 | } | ||
| @@ -52,7 +53,7 @@ func (c *ClipboardController) showToast(message, toastType string) { | |||||
| 52 | } | 53 | } | ||
| 53 | 54 | ||||
| 54 | if err := c.webView.DispatchCustomEvent("dumber:toast", detail); err != nil { | 55 | if err := c.webView.DispatchCustomEvent("dumber:toast", detail); err != nil { | ||
| 55 | - | log.Printf("Failed to dispatch toast event: %v", err) | 56 | + | logging.Error(fmt.Sprintf("Failed to dispatch toast event: %v", err)) |
| 56 | } | 57 | } | ||
| 57 | } | 58 | } | ||
| 58 | 59 | ||||
M internal/app/control/navigation.go +14 -13
| @@ -2,9 +2,10 @@ package control | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | "os" | 6 | "os" | ||
| 7 | 7 | ||||
| 8 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | "github.com/bnema/dumber/internal/services" | 9 | "github.com/bnema/dumber/internal/services" | ||
| 9 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 10 | ) | 11 | ) | ||
| @@ -42,7 +43,7 @@ func (n *NavigationController) NavigateToURL(input string) error { | |||||
| 42 | 43 | ||||
| 43 | // Record navigation in browser service | 44 | // Record navigation in browser service | ||
| 44 | if _, navErr := n.browserService.Navigate(ctx, result.URL); navErr != nil { | 45 | if _, navErr := n.browserService.Navigate(ctx, result.URL); navErr != nil { | ||
| 45 | - | log.Printf("Warning: failed to navigate to %s: %v", result.URL, navErr) | 46 | + | logging.Warn(fmt.Sprintf("Warning: failed to navigate to %s: %v", result.URL, navErr)) |
| 46 | } | 47 | } | ||
| 47 | 48 | ||||
| 48 | var ( | 49 | var ( | ||
| @@ -56,18 +57,18 @@ func (n *NavigationController) NavigateToURL(input string) error { | |||||
| 56 | if n.webView != nil { | 57 | if n.webView != nil { | ||
| 57 | if n.webView.UsesDomZoom() { | 58 | if n.webView.UsesDomZoom() { | ||
| 58 | if err := n.webView.SeedDomZoom(zoomLevel); err != nil { | 59 | if err := n.webView.SeedDomZoom(zoomLevel); err != nil { | ||
| 59 | - | log.Printf("Warning: failed to seed DOM zoom for %s: %v", result.URL, err) | 60 | + | logging.Warn(fmt.Sprintf("Warning: failed to seed DOM zoom for %s: %v", result.URL, err)) |
| 60 | } | 61 | } | ||
| 61 | } else { | 62 | } else { | ||
| 62 | n.webView.RunOnMainThread(func() { | 63 | n.webView.RunOnMainThread(func() { | ||
| 63 | if err := n.webView.SetZoom(zoomLevel); err != nil { | 64 | if err := n.webView.SetZoom(zoomLevel); err != nil { | ||
| 64 | - | log.Printf("Warning: failed to prime native zoom for %s: %v", result.URL, err) | 65 | + | logging.Warn(fmt.Sprintf("Warning: failed to prime native zoom for %s: %v", result.URL, err)) |
| 65 | } | 66 | } | ||
| 66 | }) | 67 | }) | ||
| 67 | } | 68 | } | ||
| 68 | } | 69 | } | ||
| 69 | } else { | 70 | } else { | ||
| 70 | - | log.Printf("Warning: failed to lookup zoom for %s: %v", result.URL, err) | 71 | + | logging.Warn(fmt.Sprintf("Warning: failed to lookup zoom for %s: %v", result.URL, err)) |
| 71 | } | 72 | } | ||
| 72 | } | 73 | } | ||
| 73 | 74 | ||||
| @@ -91,16 +92,16 @@ func (n *NavigationController) NavigateToURL(input string) error { | |||||
| 91 | // HandleBrowseCommand processes the browse command line argument | 92 | // HandleBrowseCommand processes the browse command line argument | ||
| 92 | func (n *NavigationController) HandleBrowseCommand() { | 93 | func (n *NavigationController) HandleBrowseCommand() { | ||
| 93 | if len(os.Args) >= 3 && os.Args[1] == "browse" { | 94 | if len(os.Args) >= 3 && os.Args[1] == "browse" { | ||
| 94 | - | log.Printf("Browse command detected: %s", os.Args[2]) | 95 | + | logging.Debug(fmt.Sprintf("Browse command detected: %s", os.Args[2])) |
| 95 | ctx := context.Background() | 96 | ctx := context.Background() | ||
| 96 | result, err := n.parserService.ParseInput(ctx, os.Args[2]) | 97 | result, err := n.parserService.ParseInput(ctx, os.Args[2]) | ||
| 97 | if err == nil { | 98 | if err == nil { | ||
| 98 | - | log.Printf("Parsed input → URL: %s", result.URL) | 99 | + | logging.Debug(fmt.Sprintf("Parsed input → URL: %s", result.URL)) |
| 99 | if _, navErr := n.browserService.Navigate(ctx, result.URL); navErr != nil { | 100 | if _, navErr := n.browserService.Navigate(ctx, result.URL); navErr != nil { | ||
| 100 | - | log.Printf("Warning: failed to navigate to %s: %v", result.URL, navErr) | 101 | + | logging.Warn(fmt.Sprintf("Warning: failed to navigate to %s: %v", result.URL, navErr)) |
| 101 | } | 102 | } | ||
| 102 | if n.webView != nil { | 103 | if n.webView != nil { | ||
| 103 | - | log.Printf("Loading URL in WebView: %s", result.URL) | 104 | + | logging.Debug(fmt.Sprintf("Loading URL in WebView: %s", result.URL)) |
| 104 | var ( | 105 | var ( | ||
| 105 | zoomLevel float64 | 106 | zoomLevel float64 | ||
| 106 | haveZoom bool | 107 | haveZoom bool | ||
| @@ -111,21 +112,21 @@ func (n *NavigationController) HandleBrowseCommand() { | |||||
| 111 | haveZoom = true | 112 | haveZoom = true | ||
| 112 | if n.webView.UsesDomZoom() { | 113 | if n.webView.UsesDomZoom() { | ||
| 113 | if err := n.webView.SeedDomZoom(zoomLevel); err != nil { | 114 | if err := n.webView.SeedDomZoom(zoomLevel); err != nil { | ||
| 114 | - | log.Printf("Warning: failed to seed DOM zoom for %s: %v", result.URL, err) | 115 | + | logging.Warn(fmt.Sprintf("Warning: failed to seed DOM zoom for %s: %v", result.URL, err)) |
| 115 | } | 116 | } | ||
| 116 | } else { | 117 | } else { | ||
| 117 | n.webView.RunOnMainThread(func() { | 118 | n.webView.RunOnMainThread(func() { | ||
| 118 | if err := n.webView.SetZoom(zoomLevel); err != nil { | 119 | if err := n.webView.SetZoom(zoomLevel); err != nil { | ||
| 119 | - | log.Printf("Warning: failed to prime native zoom for %s: %v", result.URL, err) | 120 | + | logging.Warn(fmt.Sprintf("Warning: failed to prime native zoom for %s: %v", result.URL, err)) |
| 120 | } | 121 | } | ||
| 121 | }) | 122 | }) | ||
| 122 | } | 123 | } | ||
| 123 | } else { | 124 | } else { | ||
| 124 | - | log.Printf("Warning: failed to lookup zoom for %s: %v", result.URL, err) | 125 | + | logging.Warn(fmt.Sprintf("Warning: failed to lookup zoom for %s: %v", result.URL, err)) |
| 125 | } | 126 | } | ||
| 126 | } | 127 | } | ||
| 127 | if err := n.webView.LoadURL(result.URL); err != nil { | 128 | if err := n.webView.LoadURL(result.URL); err != nil { | ||
| 128 | - | log.Printf("Warning: failed to load URL: %v", err) | 129 | + | logging.Warn(fmt.Sprintf("Warning: failed to load URL: %v", err)) |
| 129 | } | 130 | } | ||
| 130 | if n.zoomController != nil { | 131 | if n.zoomController != nil { | ||
| 131 | if haveZoom { | 132 | if haveZoom { | ||
M internal/app/control/zoom.go +9 -8
| @@ -2,9 +2,10 @@ package control | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | "time" | 6 | "time" | ||
| 7 | 7 | ||||
| 8 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | "github.com/bnema/dumber/internal/services" | 9 | "github.com/bnema/dumber/internal/services" | ||
| 9 | "github.com/bnema/dumber/pkg/webkit" | 10 | "github.com/bnema/dumber/pkg/webkit" | ||
| 10 | ) | 11 | ) | ||
| @@ -74,11 +75,11 @@ func (z *ZoomController) handleZoomChange(level float64) { | |||||
| 74 | go func(url string, level float64) { | 75 | go func(url string, level float64) { | ||
| 75 | ctx := context.Background() | 76 | ctx := context.Background() | ||
| 76 | if err := z.browserService.SetZoomLevel(ctx, url, level); err != nil { | 77 | if err := z.browserService.SetZoomLevel(ctx, url, level); err != nil { | ||
| 77 | - | log.Printf("[zoom] failed to save level %.2f for %s: %v", level, url, err) | 78 | + | logging.Error(fmt.Sprintf("[zoom] failed to save level %.2f for %s: %v", level, url, err)) |
| 78 | return | 79 | return | ||
| 79 | } | 80 | } | ||
| 80 | key := services.ZoomKeyForLog(url) | 81 | key := services.ZoomKeyForLog(url) | ||
| 81 | - | log.Printf("[zoom] saved %.2f for %s", level, key) | 82 | + | logging.Debug(fmt.Sprintf("[zoom] saved %.2f for %s", level, key)) |
| 82 | }(url, level) | 83 | }(url, level) | ||
| 83 | 84 | ||||
| 84 | // Only show toast for user-initiated zoom changes, not during init or programmatic changes | 85 | // Only show toast for user-initiated zoom changes, not during init or programmatic changes | ||
| @@ -89,15 +90,15 @@ func (z *ZoomController) handleZoomChange(level float64) { | |||||
| 89 | 90 | ||||
| 90 | // showZoomToast displays a zoom level notification using TypeScript toast system | 91 | // showZoomToast displays a zoom level notification using TypeScript toast system | ||
| 91 | func (z *ZoomController) showZoomToast(level float64) { | 92 | func (z *ZoomController) showZoomToast(level float64) { | ||
| 92 | - | log.Printf("[zoom] Attempting to show zoom toast for level %.2f", level) | 93 | + | logging.Debug(fmt.Sprintf("[zoom] Attempting to show zoom toast for level %.2f", level)) |
| 93 | 94 | ||||
| 94 | if z.webView == nil { | 95 | if z.webView == nil { | ||
| 95 | - | log.Printf("[zoom] webview unavailable for zoom toast") | 96 | + | logging.Debug(fmt.Sprintf("[zoom] webview unavailable for zoom toast")) |
| 96 | return | 97 | return | ||
| 97 | } | 98 | } | ||
| 98 | 99 | ||||
| 99 | if err := z.webView.DispatchCustomEvent("dumber:toast:zoom", map[string]any{"level": level}); err != nil { | 100 | if err := z.webView.DispatchCustomEvent("dumber:toast:zoom", map[string]any{"level": level}); err != nil { | ||
| 100 | - | log.Printf("[zoom] failed to dispatch zoom toast: %v", err) | 101 | + | logging.Error(fmt.Sprintf("[zoom] failed to dispatch zoom toast: %v", err)) |
| 101 | } | 102 | } | ||
| 102 | } | 103 | } | ||
| 103 | 104 | ||||
| @@ -172,11 +173,11 @@ func (z *ZoomController) applyZoomLevel(url, domain string, zoomLevel float64, a | |||||
| 172 | }) | 173 | }) | ||
| 173 | 174 | ||||
| 174 | if err := z.webView.SetZoom(zoomLevel); err != nil { | 175 | if err := z.webView.SetZoom(zoomLevel); err != nil { | ||
| 175 | - | log.Printf("Warning: failed to set zoom: %v", err) | 176 | + | logging.Warn(fmt.Sprintf("Warning: failed to set zoom: %v", err)) |
| 176 | return | 177 | return | ||
| 177 | } | 178 | } | ||
| 178 | 179 | ||||
| 179 | - | log.Printf("[zoom] loaded %.2f for %s", zoomLevel, domain) | 180 | + | logging.Debug(fmt.Sprintf("[zoom] loaded %.2f for %s", zoomLevel, domain)) |
| 180 | if allowToast && domain != z.lastZoomDomain && z.lastZoomDomain != "" { | 181 | if allowToast && domain != z.lastZoomDomain && z.lastZoomDomain != "" { | ||
| 181 | z.showZoomToast(zoomLevel) | 182 | z.showZoomToast(zoomLevel) | ||
| 182 | } | 183 | } | ||
M internal/app/environment/codec.go +17 -16
| @@ -1,18 +1,19 @@ | |||||
| 1 | package environment | 1 | package environment | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "os" | 5 | "os" | ||
| 6 | "strconv" | 6 | "strconv" | ||
| 7 | "strings" | 7 | "strings" | ||
| 8 | 8 | ||||
| 9 | "github.com/bnema/dumber/internal/config" | 9 | "github.com/bnema/dumber/internal/config" | ||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| 12 | // ApplyCodecConfiguration applies codec preferences to environment variables and GStreamer settings | 13 | // ApplyCodecConfiguration applies codec preferences to environment variables and GStreamer settings | ||
| 13 | func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | 14 | func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | ||
| 14 | - | log.Printf("[codec] Applying codec preferences: preferred=%s, force_av1=%t, block_vp9=%t", | 15 | + | logging.Info(fmt.Sprintf("[codec] Applying codec preferences: preferred=%s, force_av1=%t, block_vp9=%t", |
| 15 | - | codecPrefs.PreferredCodecs, codecPrefs.ForceAV1, codecPrefs.BlockVP9) | 16 | + | codecPrefs.PreferredCodecs, codecPrefs.ForceAV1, codecPrefs.BlockVP9)) |
| 16 | 17 | ||||
| 17 | // Build comprehensive GStreamer plugin rankings | 18 | // Build comprehensive GStreamer plugin rankings | ||
| 18 | var rankSettings []string | 19 | var rankSettings []string | ||
| @@ -32,7 +33,7 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 32 | "avdec_av1:256", // libav AV1 decoder | 33 | "avdec_av1:256", // libav AV1 decoder | ||
| 33 | } | 34 | } | ||
| 34 | rankSettings = append(rankSettings, av1Ranks...) | 35 | rankSettings = append(rankSettings, av1Ranks...) | ||
| 35 | - | log.Printf("[codec] Promoted AV1 decoders for maximum priority") | 36 | + | logging.Info(fmt.Sprintf("[codec] Promoted AV1 decoders for maximum priority")) |
| 36 | } | 37 | } | ||
| 37 | 38 | ||||
| 38 | // H.264 decoder management | 39 | // H.264 decoder management | ||
| @@ -58,7 +59,7 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 58 | vp9Demotions = append(vp9Demotions, "vp9dec:64", "avdec_vp9:64") | 59 | vp9Demotions = append(vp9Demotions, "vp9dec:64", "avdec_vp9:64") | ||
| 59 | } | 60 | } | ||
| 60 | rankSettings = append(rankSettings, vp9Demotions...) | 61 | rankSettings = append(rankSettings, vp9Demotions...) | ||
| 61 | - | log.Printf("[codec] Demoted/blocked VP9 decoders") | 62 | + | logging.Info(fmt.Sprintf("[codec] Demoted/blocked VP9 decoders")) |
| 62 | } | 63 | } | ||
| 63 | 64 | ||||
| 64 | // VP8 decoder blocking | 65 | // VP8 decoder blocking | ||
| @@ -69,16 +70,16 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 69 | "avdec_vp8:0", | 70 | "avdec_vp8:0", | ||
| 70 | } | 71 | } | ||
| 71 | rankSettings = append(rankSettings, vp8Demotions...) | 72 | rankSettings = append(rankSettings, vp8Demotions...) | ||
| 72 | - | log.Printf("[codec] Blocked VP8 decoders") | 73 | + | logging.Info(fmt.Sprintf("[codec] Blocked VP8 decoders")) |
| 73 | } | 74 | } | ||
| 74 | 75 | ||||
| 75 | // Apply the complete ranking system | 76 | // Apply the complete ranking system | ||
| 76 | if len(rankSettings) > 0 { | 77 | if len(rankSettings) > 0 { | ||
| 77 | finalRank := strings.Join(rankSettings, ",") | 78 | finalRank := strings.Join(rankSettings, ",") | ||
| 78 | if err := os.Setenv("GST_PLUGIN_FEATURE_RANK", finalRank); err != nil { | 79 | if err := os.Setenv("GST_PLUGIN_FEATURE_RANK", finalRank); err != nil { | ||
| 79 | - | log.Printf("[codec] Warning: failed to set GST_PLUGIN_FEATURE_RANK: %v", err) | 80 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_PLUGIN_FEATURE_RANK: %v", err)) |
| 80 | } else { | 81 | } else { | ||
| 81 | - | log.Printf("[codec] Set comprehensive GST_PLUGIN_FEATURE_RANK: %s", finalRank) | 82 | + | logging.Info(fmt.Sprintf("[codec] Set comprehensive GST_PLUGIN_FEATURE_RANK: %s", finalRank)) |
| 82 | } | 83 | } | ||
| 83 | } | 84 | } | ||
| 84 | 85 | ||||
| @@ -87,14 +88,14 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 87 | bufferSize := strconv.Itoa(codecPrefs.VideoBufferSizeMB * 1024 * 1024) | 88 | bufferSize := strconv.Itoa(codecPrefs.VideoBufferSizeMB * 1024 * 1024) | ||
| 88 | 89 | ||||
| 89 | if err := os.Setenv("GST_BUFFER_SIZE", bufferSize); err != nil { | 90 | if err := os.Setenv("GST_BUFFER_SIZE", bufferSize); err != nil { | ||
| 90 | - | log.Printf("[codec] Warning: failed to set GST_BUFFER_SIZE: %v", err) | 91 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_BUFFER_SIZE: %v", err)) |
| 91 | } | 92 | } | ||
| 92 | 93 | ||||
| 93 | if err := os.Setenv("GST_QUEUE2_MAX_SIZE_BYTES", bufferSize); err != nil { | 94 | if err := os.Setenv("GST_QUEUE2_MAX_SIZE_BYTES", bufferSize); err != nil { | ||
| 94 | - | log.Printf("[codec] Warning: failed to set GST_QUEUE2_MAX_SIZE_BYTES: %v", err) | 95 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_QUEUE2_MAX_SIZE_BYTES: %v", err)) |
| 95 | } | 96 | } | ||
| 96 | 97 | ||||
| 97 | - | log.Printf("[codec] Set video buffer size to %dMB", codecPrefs.VideoBufferSizeMB) | 98 | + | logging.Info(fmt.Sprintf("[codec] Set video buffer size to %dMB", codecPrefs.VideoBufferSizeMB)) |
| 98 | } | 99 | } | ||
| 99 | 100 | ||||
| 100 | // Set queue buffer time if specified | 101 | // Set queue buffer time if specified | ||
| @@ -103,9 +104,9 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 103 | bufferTime := strconv.Itoa(codecPrefs.QueueBufferTimeSec * 1000000000) | 104 | bufferTime := strconv.Itoa(codecPrefs.QueueBufferTimeSec * 1000000000) | ||
| 104 | 105 | ||||
| 105 | if err := os.Setenv("GST_QUEUE2_MAX_SIZE_TIME", bufferTime); err != nil { | 106 | if err := os.Setenv("GST_QUEUE2_MAX_SIZE_TIME", bufferTime); err != nil { | ||
| 106 | - | log.Printf("[codec] Warning: failed to set GST_QUEUE2_MAX_SIZE_TIME: %v", err) | 107 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_QUEUE2_MAX_SIZE_TIME: %v", err)) |
| 107 | } else { | 108 | } else { | ||
| 108 | - | log.Printf("[codec] Set queue buffer time to %ds", codecPrefs.QueueBufferTimeSec) | 109 | + | logging.Info(fmt.Sprintf("[codec] Set queue buffer time to %ds", codecPrefs.QueueBufferTimeSec)) |
| 109 | } | 110 | } | ||
| 110 | } | 111 | } | ||
| 111 | 112 | ||||
| @@ -113,16 +114,16 @@ func ApplyCodecConfiguration(codecPrefs config.CodecConfig) { | |||||
| 113 | if codecPrefs.ForceAV1 { | 114 | if codecPrefs.ForceAV1 { | ||
| 114 | // Enable AV1 hardware decoding if available | 115 | // Enable AV1 hardware decoding if available | ||
| 115 | if err := os.Setenv("GST_AV1_DECODER_ENABLE_HW", "1"); err != nil { | 116 | if err := os.Setenv("GST_AV1_DECODER_ENABLE_HW", "1"); err != nil { | ||
| 116 | - | log.Printf("[codec] Warning: failed to set GST_AV1_DECODER_ENABLE_HW: %v", err) | 117 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_AV1_DECODER_ENABLE_HW: %v", err)) |
| 117 | } | 118 | } | ||
| 118 | } | 119 | } | ||
| 119 | 120 | ||||
| 120 | // Disable video post-processing to avoid VA-API issues | 121 | // Disable video post-processing to avoid VA-API issues | ||
| 121 | if err := os.Setenv("GST_VAAPI_DISABLE_VPP", "1"); err != nil { | 122 | if err := os.Setenv("GST_VAAPI_DISABLE_VPP", "1"); err != nil { | ||
| 122 | - | log.Printf("[codec] Warning: failed to set GST_VAAPI_DISABLE_VPP: %v", err) | 123 | + | logging.Warn(fmt.Sprintf("[codec] Warning: failed to set GST_VAAPI_DISABLE_VPP: %v", err)) |
| 123 | } | 124 | } | ||
| 124 | 125 | ||||
| 125 | - | log.Printf("[codec] Codec configuration applied successfully") | 126 | + | logging.Info(fmt.Sprintf("[codec] Codec configuration applied successfully")) |
| 126 | } | 127 | } | ||
| 127 | 128 | ||||
| 128 | // BuildBlockedCodecsList converts config codec blocking preferences to a string slice | 129 | // BuildBlockedCodecsList converts config codec blocking preferences to a string slice | ||
M internal/app/environment/locale.go +8 -6
| @@ -1,10 +1,12 @@ | |||||
| 1 | package environment | 1 | package environment | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "os" | 5 | "os" | ||
| 6 | "os/exec" | 6 | "os/exec" | ||
| 7 | "strings" | 7 | "strings" | ||
| 8 | + | ||||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | ) | 10 | ) | ||
| 9 | 11 | ||||
| 10 | // DetectAndSetKeyboardLocale attempts to determine the current keyboard layout | 12 | // DetectAndSetKeyboardLocale attempts to determine the current keyboard layout | ||
| @@ -38,7 +40,7 @@ func DetectAndSetKeyboardLocale() { | |||||
| 38 | cand := strings.TrimSpace(parts[1]) | 40 | cand := strings.TrimSpace(parts[1]) | ||
| 39 | if cand != "" { | 41 | if cand != "" { | ||
| 40 | locale = cand | 42 | locale = cand | ||
| 41 | - | log.Printf("[locale] keyboard layout from localectl: %s", locale) | 43 | + | logging.Debug(fmt.Sprintf("[locale] keyboard layout from localectl: %s", locale)) |
| 42 | } | 44 | } | ||
| 43 | } | 45 | } | ||
| 44 | break | 46 | break | ||
| @@ -57,7 +59,7 @@ func DetectAndSetKeyboardLocale() { | |||||
| 57 | cand := strings.TrimSpace(parts[1]) | 59 | cand := strings.TrimSpace(parts[1]) | ||
| 58 | if cand != "" { | 60 | if cand != "" { | ||
| 59 | locale = cand | 61 | locale = cand | ||
| 60 | - | log.Printf("[locale] keyboard layout from setxkbmap: %s", locale) | 62 | + | logging.Debug(fmt.Sprintf("[locale] keyboard layout from setxkbmap: %s", locale)) |
| 61 | } | 63 | } | ||
| 62 | } | 64 | } | ||
| 63 | break | 65 | break | ||
| @@ -87,13 +89,13 @@ func DetectAndSetKeyboardLocale() { | |||||
| 87 | if i := strings.IndexByte(locale, '_'); i > 0 { | 89 | if i := strings.IndexByte(locale, '_'); i > 0 { | ||
| 88 | locale = locale[:i] | 90 | locale = locale[:i] | ||
| 89 | } | 91 | } | ||
| 90 | - | log.Printf("[locale] keyboard layout from system locale: %s", locale) | 92 | + | logging.Debug(fmt.Sprintf("[locale] keyboard layout from system locale: %s", locale)) |
| 91 | } | 93 | } | ||
| 92 | 94 | ||||
| 93 | if locale == "" { | 95 | if locale == "" { | ||
| 94 | locale = "en" | 96 | locale = "en" | ||
| 95 | - | log.Printf("[locale] keyboard layout defaulted to: %s", locale) | 97 | + | logging.Debug(fmt.Sprintf("[locale] keyboard layout defaulted to: %s", locale)) |
| 96 | } | 98 | } | ||
| 97 | 99 | ||||
| 98 | - | log.Printf("[locale] keyboard locale detected: %s", locale) | 100 | + | logging.Info(fmt.Sprintf("[locale] keyboard locale detected: %s", locale)) |
| 99 | } | 101 | } | ||
M internal/app/environment/media.go +6 -4
| @@ -1,8 +1,10 @@ | |||||
| 1 | package environment | 1 | package environment | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "os" | 5 | "os" | ||
| 6 | + | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | ) | 8 | ) | ||
| 7 | 9 | ||||
| 8 | // SetupMediaPipeline proactively hardens media pipeline to avoid WebProcess crashes from buggy VAAPI/plugins. | 10 | // SetupMediaPipeline proactively hardens media pipeline to avoid WebProcess crashes from buggy VAAPI/plugins. | ||
| @@ -12,15 +14,15 @@ func SetupMediaPipeline() { | |||||
| 12 | // Demote VAAPI-related elements so they won't be auto-picked | 14 | // Demote VAAPI-related elements so they won't be auto-picked | ||
| 13 | if os.Getenv("GST_PLUGIN_FEATURE_RANK") == "" { | 15 | if os.Getenv("GST_PLUGIN_FEATURE_RANK") == "" { | ||
| 14 | if err := os.Setenv("GST_PLUGIN_FEATURE_RANK", "vaapisink:0,vaapidecodebin:0,vaapih264dec:0,vaapivideoconvert:0"); err != nil { | 16 | if err := os.Setenv("GST_PLUGIN_FEATURE_RANK", "vaapisink:0,vaapidecodebin:0,vaapih264dec:0,vaapivideoconvert:0"); err != nil { | ||
| 15 | - | log.Printf("Warning: failed to set GST_PLUGIN_FEATURE_RANK: %v", err) | 17 | + | logging.Warn(fmt.Sprintf("Warning: failed to set GST_PLUGIN_FEATURE_RANK: %v", err)) |
| 16 | } | 18 | } | ||
| 17 | } | 19 | } | ||
| 18 | // Optionally disable media stream features that can engage complex pipelines (webcam/mic) | 20 | // Optionally disable media stream features that can engage complex pipelines (webcam/mic) | ||
| 19 | if os.Getenv("WEBKIT_DISABLE_MEDIA_STREAM") == "" { | 21 | if os.Getenv("WEBKIT_DISABLE_MEDIA_STREAM") == "" { | ||
| 20 | if err := os.Setenv("WEBKIT_DISABLE_MEDIA_STREAM", "1"); err != nil { | 22 | if err := os.Setenv("WEBKIT_DISABLE_MEDIA_STREAM", "1"); err != nil { | ||
| 21 | - | log.Printf("Warning: failed to set WEBKIT_DISABLE_MEDIA_STREAM: %v", err) | 23 | + | logging.Warn(fmt.Sprintf("Warning: failed to set WEBKIT_DISABLE_MEDIA_STREAM: %v", err)) |
| 22 | } | 24 | } | ||
| 23 | } | 25 | } | ||
| 24 | - | log.Printf("[media] Safe mode enabled: VAAPI demoted, webcam/mic disabled (override with DUMBER_MEDIA_SAFE=0)") | 26 | + | logging.Info(fmt.Sprintf("[media] Safe mode enabled: VAAPI demoted, webcam/mic disabled (override with DUMBER_MEDIA_SAFE=0)")) |
| 25 | } | 27 | } | ||
| 26 | } | 28 | } | ||
M internal/app/messaging/handler.go +55 -56
| @@ -5,7 +5,6 @@ import ( | |||||
| 5 | "context" | 5 | "context" | ||
| 6 | "encoding/json" | 6 | "encoding/json" | ||
| 7 | "fmt" | 7 | "fmt" | ||
| 8 | - | "log" | |||
| 9 | "strconv" | 8 | "strconv" | ||
| 10 | "time" | 9 | "time" | ||
| 11 | 10 | ||||
| @@ -79,7 +78,7 @@ func (h *Handler) SetWorkspaceObserver(observer WorkspaceObserver) { | |||||
| 79 | func (h *Handler) Handle(payload string) { | 78 | func (h *Handler) Handle(payload string) { | ||
| 80 | msg, err := parseIncomingMessage(payload) | 79 | msg, err := parseIncomingMessage(payload) | ||
| 81 | if err != nil { | 80 | if err != nil { | ||
| 82 | - | log.Printf("[ERROR] Failed to unmarshal message: %v", err) | 81 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to unmarshal message: %v", err)) |
| 83 | return | 82 | return | ||
| 84 | } | 83 | } | ||
| 85 | 84 | ||||
| @@ -202,7 +201,7 @@ func (h *Handler) SetWebView(webView *webkit.WebView) { | |||||
| 202 | func (h *Handler) handleNavigation(msg Message) { | 201 | func (h *Handler) handleNavigation(msg Message) { | ||
| 203 | if h.navigationController != nil { | 202 | if h.navigationController != nil { | ||
| 204 | if err := h.navigationController.NavigateToURL(msg.URL); err != nil { | 203 | if err := h.navigationController.NavigateToURL(msg.URL); err != nil { | ||
| 205 | - | log.Printf("[messaging] Navigation controller failed for input %q: %v", msg.URL, err) | 204 | + | logging.Error(fmt.Sprintf("[messaging] Navigation controller failed for input %q: %v", msg.URL, err)) |
| 206 | h.legacyNavigate(msg) | 205 | h.legacyNavigate(msg) | ||
| 207 | } | 206 | } | ||
| 208 | return | 207 | return | ||
| @@ -215,12 +214,12 @@ func (h *Handler) legacyNavigate(msg Message) { | |||||
| 215 | ctx := context.Background() | 214 | ctx := context.Background() | ||
| 216 | res, err := h.parserService.ParseInput(ctx, msg.URL) | 215 | res, err := h.parserService.ParseInput(ctx, msg.URL) | ||
| 217 | if err != nil { | 216 | if err != nil { | ||
| 218 | - | log.Printf("[messaging] Legacy navigation parse failed for %q: %v", msg.URL, err) | 217 | + | logging.Error(fmt.Sprintf("[messaging] Legacy navigation parse failed for %q: %v", msg.URL, err)) |
| 219 | return | 218 | return | ||
| 220 | } | 219 | } | ||
| 221 | 220 | ||||
| 222 | if _, navErr := h.browserService.Navigate(ctx, res.URL); navErr != nil { | 221 | if _, navErr := h.browserService.Navigate(ctx, res.URL); navErr != nil { | ||
| 223 | - | log.Printf("Warning: failed to navigate to %s: %v", res.URL, navErr) | 222 | + | logging.Warn(fmt.Sprintf("Warning: failed to navigate to %s: %v", res.URL, navErr)) |
| 224 | } | 223 | } | ||
| 225 | 224 | ||||
| 226 | if h.webView == nil { | 225 | if h.webView == nil { | ||
| @@ -228,12 +227,12 @@ func (h *Handler) legacyNavigate(msg Message) { | |||||
| 228 | } | 227 | } | ||
| 229 | 228 | ||||
| 230 | if err := h.webView.LoadURL(res.URL); err != nil { | 229 | if err := h.webView.LoadURL(res.URL); err != nil { | ||
| 231 | - | log.Printf("[messaging] Legacy LoadURL failed for %s: %v", res.URL, err) | 230 | + | logging.Error(fmt.Sprintf("[messaging] Legacy LoadURL failed for %s: %v", res.URL, err)) |
| 232 | } | 231 | } | ||
| 233 | 232 | ||||
| 234 | if z, zerr := h.browserService.GetZoomLevel(ctx, res.URL); zerr == nil { | 233 | if z, zerr := h.browserService.GetZoomLevel(ctx, res.URL); zerr == nil { | ||
| 235 | if err := h.webView.SetZoom(z); err != nil { | 234 | if err := h.webView.SetZoom(z); err != nil { | ||
| 236 | - | log.Printf("[messaging] Legacy SetZoom failed for %s: %v", res.URL, err) | 235 | + | logging.Error(fmt.Sprintf("[messaging] Legacy SetZoom failed for %s: %v", res.URL, err)) |
| 237 | } | 236 | } | ||
| 238 | } | 237 | } | ||
| 239 | } | 238 | } | ||
| @@ -242,26 +241,26 @@ func (h *Handler) legacyNavigate(msg Message) { | |||||
| 242 | // handleQuery computes omnibox suggestions natively and returns them to the GUI without fetch | 241 | // handleQuery computes omnibox suggestions natively and returns them to the GUI without fetch | ||
| 243 | func (h *Handler) handleWorkspace(msg Message) { | 242 | func (h *Handler) handleWorkspace(msg Message) { | ||
| 244 | if h.workspaceObserver == nil { | 243 | if h.workspaceObserver == nil { | ||
| 245 | - | log.Printf("[workspace] Received workspace event %q but no observer registered", msg.Event) | 244 | + | logging.Warn(fmt.Sprintf("[workspace] Received workspace event %q but no observer registered", msg.Event)) |
| 246 | return | 245 | return | ||
| 247 | } | 246 | } | ||
| 248 | if h.webView == nil { | 247 | if h.webView == nil { | ||
| 249 | - | log.Printf("[workspace] Ignoring workspace event %q: webview not attached", msg.Event) | 248 | + | logging.Debug(fmt.Sprintf("[workspace] Ignoring workspace event %q: webview not attached", msg.Event)) |
| 250 | return | 249 | return | ||
| 251 | } | 250 | } | ||
| 252 | - | log.Printf("[workspace] Forwarding workspace event: event=%s direction=%s action=%s webviewId=%s", msg.Event, msg.Direction, msg.Action, msg.WebViewID) | 251 | + | logging.Debug(fmt.Sprintf("[workspace] Forwarding workspace event: event=%s direction=%s action=%s webviewId=%s", msg.Event, msg.Direction, msg.Action, msg.WebViewID)) |
| 253 | h.workspaceObserver.OnWorkspaceMessage(h.webView, msg) | 252 | h.workspaceObserver.OnWorkspaceMessage(h.webView, msg) | ||
| 254 | } | 253 | } | ||
| 255 | 254 | ||||
| 256 | func (h *Handler) handleClosePopup(msg Message) { | 255 | func (h *Handler) handleClosePopup(msg Message) { | ||
| 257 | - | log.Printf("[messaging] Received close-popup request: webviewId=%s reason=%s", msg.WebViewID, msg.Reason) | 256 | + | logging.Debug(fmt.Sprintf("[messaging] Received close-popup request: webviewId=%s reason=%s", msg.WebViewID, msg.Reason)) |
| 258 | 257 | ||||
| 259 | if h.workspaceObserver == nil { | 258 | if h.workspaceObserver == nil { | ||
| 260 | - | log.Printf("[messaging] No workspace observer registered for close-popup request") | 259 | + | logging.Warn(fmt.Sprintf("[messaging] No workspace observer registered for close-popup request")) |
| 261 | return | 260 | return | ||
| 262 | } | 261 | } | ||
| 263 | if h.webView == nil { | 262 | if h.webView == nil { | ||
| 264 | - | log.Printf("[messaging] No webview attached for close-popup request") | 263 | + | logging.Debug(fmt.Sprintf("[messaging] No webview attached for close-popup request")) |
| 265 | return | 264 | return | ||
| 266 | } | 265 | } | ||
| 267 | 266 | ||||
| @@ -273,7 +272,7 @@ func (h *Handler) handleClosePopup(msg Message) { | |||||
| 273 | Reason: msg.Reason, | 272 | Reason: msg.Reason, | ||
| 274 | } | 273 | } | ||
| 275 | 274 | ||||
| 276 | - | log.Printf("[messaging] Forwarding close-popup to workspace: webviewId=%s reason=%s", msg.WebViewID, msg.Reason) | 275 | + | logging.Debug(fmt.Sprintf("[messaging] Forwarding close-popup to workspace: webviewId=%s reason=%s", msg.WebViewID, msg.Reason)) |
| 277 | h.workspaceObserver.OnWorkspaceMessage(h.webView, closeMsg) | 276 | h.workspaceObserver.OnWorkspaceMessage(h.webView, closeMsg) | ||
| 278 | } | 277 | } | ||
| 279 | 278 | ||||
| @@ -342,7 +341,7 @@ func (h *Handler) handleQuery(msg Message) { | |||||
| 342 | // Prefer unified page-world API; fallback to legacy global function | 341 | // Prefer unified page-world API; fallback to legacy global function | ||
| 343 | script := "(window.__dumber?.omnibox?.suggestions ? window.__dumber.omnibox.suggestions(" + string(b) + ") : (window.__dumber_omnibox_suggestions && window.__dumber_omnibox_suggestions(" + string(b) + ")))" | 342 | script := "(window.__dumber?.omnibox?.suggestions ? window.__dumber.omnibox.suggestions(" + string(b) + ") : (window.__dumber_omnibox_suggestions && window.__dumber_omnibox_suggestions(" + string(b) + ")))" | ||
| 344 | if err := h.webView.InjectScript(script); err != nil { | 343 | if err := h.webView.InjectScript(script); err != nil { | ||
| 345 | - | log.Printf("[ERROR] Failed to inject omnibox suggestions: %v", err) | 344 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to inject omnibox suggestions: %v", err)) |
| 346 | } | 345 | } | ||
| 347 | } | 346 | } | ||
| 348 | } | 347 | } | ||
| @@ -362,7 +361,7 @@ func (h *Handler) handleOmniboxInitialHistory(msg Message) { | |||||
| 362 | // Get config to determine behavior | 361 | // Get config to determine behavior | ||
| 363 | cfg, err := h.browserService.GetConfig(ctx) | 362 | cfg, err := h.browserService.GetConfig(ctx) | ||
| 364 | if err != nil { | 363 | if err != nil { | ||
| 365 | - | log.Printf("[ERROR] Failed to get config: %v", err) | 364 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to get config: %v", err)) |
| 366 | return | 365 | return | ||
| 367 | } | 366 | } | ||
| 368 | behavior := cfg.Omnibox.InitialBehavior | 367 | behavior := cfg.Omnibox.InitialBehavior | ||
| @@ -396,7 +395,7 @@ func (h *Handler) handleOmniboxInitialHistory(msg Message) { | |||||
| 396 | } | 395 | } | ||
| 397 | 396 | ||||
| 398 | if err != nil { | 397 | if err != nil { | ||
| 399 | - | log.Printf("[ERROR] Failed to fetch initial history: %v", err) | 398 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to fetch initial history: %v", err)) |
| 400 | return | 399 | return | ||
| 401 | } | 400 | } | ||
| 402 | 401 | ||||
| @@ -437,7 +436,7 @@ func (h *Handler) handleOmniboxInitialHistory(msg Message) { | |||||
| 437 | if b, err := json.Marshal(results); err == nil { | 436 | if b, err := json.Marshal(results); err == nil { | ||
| 438 | script := "(window.__dumber?.omnibox?.suggestions ? window.__dumber.omnibox.suggestions(" + string(b) + ") : (window.__dumber_omnibox_suggestions && window.__dumber_omnibox_suggestions(" + string(b) + ")))" | 437 | script := "(window.__dumber?.omnibox?.suggestions ? window.__dumber.omnibox.suggestions(" + string(b) + ") : (window.__dumber_omnibox_suggestions && window.__dumber_omnibox_suggestions(" + string(b) + ")))" | ||
| 439 | if err := h.webView.InjectScript(script); err != nil { | 438 | if err := h.webView.InjectScript(script); err != nil { | ||
| 440 | - | log.Printf("[ERROR] Failed to inject initial history: %v", err) | 439 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to inject initial history: %v", err)) |
| 441 | } | 440 | } | ||
| 442 | } | 441 | } | ||
| 443 | } | 442 | } | ||
| @@ -495,7 +494,7 @@ func (h *Handler) handleWailsBridge(msg Message) { | |||||
| 495 | // handleTheme processes theme-related messages | 494 | // handleTheme processes theme-related messages | ||
| 496 | func (h *Handler) handleTheme(msg Message) { | 495 | func (h *Handler) handleTheme(msg Message) { | ||
| 497 | if msg.Value != "" && msg.Value != h.lastTheme { | 496 | if msg.Value != "" && msg.Value != h.lastTheme { | ||
| 498 | - | log.Printf("[theme] color-scheme changed: %s", msg.Value) | 497 | + | logging.Debug(fmt.Sprintf("[theme] color-scheme changed: %s", msg.Value)) |
| 499 | h.lastTheme = msg.Value | 498 | h.lastTheme = msg.Value | ||
| 500 | } | 499 | } | ||
| 501 | } | 500 | } | ||
| @@ -541,7 +540,7 @@ func (h *Handler) handleHistoryRecent(msg Message) { | |||||
| 541 | } | 540 | } | ||
| 542 | 541 | ||||
| 543 | if err != nil { | 542 | if err != nil { | ||
| 544 | - | log.Printf("[messaging] Failed to get recent history: %v", err) | 543 | + | logging.Error(fmt.Sprintf("[messaging] Failed to get recent history: %v", err)) |
| 545 | requestIdParam := "" | 544 | requestIdParam := "" | ||
| 546 | if msg.RequestID != "" { | 545 | if msg.RequestID != "" { | ||
| 547 | requestIdParam = ", '" + msg.RequestID + "'" | 546 | requestIdParam = ", '" + msg.RequestID + "'" | ||
| @@ -567,7 +566,7 @@ func (h *Handler) handleHistoryStats(_ Message) { | |||||
| 567 | ctx := context.Background() | 566 | ctx := context.Background() | ||
| 568 | stats, err := h.browserService.GetHistoryStats(ctx) | 567 | stats, err := h.browserService.GetHistoryStats(ctx) | ||
| 569 | if err != nil { | 568 | if err != nil { | ||
| 570 | - | log.Printf("[messaging] Failed to get history stats: %v", err) | 569 | + | logging.Error(fmt.Sprintf("[messaging] Failed to get history stats: %v", err)) |
| 571 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to load history stats')") | 570 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to load history stats')") | ||
| 572 | return | 571 | return | ||
| 573 | } | 572 | } | ||
| @@ -591,7 +590,7 @@ func (h *Handler) handleHistorySearch(msg Message) { | |||||
| 591 | 590 | ||||
| 592 | entries, err := h.browserService.SearchHistory(ctx, query, limit) | 591 | entries, err := h.browserService.SearchHistory(ctx, query, limit) | ||
| 593 | if err != nil { | 592 | if err != nil { | ||
| 594 | - | log.Printf("[messaging] Failed to search history: %v", err) | 593 | + | logging.Error(fmt.Sprintf("[messaging] Failed to search history: %v", err)) |
| 595 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to search history')") | 594 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to search history')") | ||
| 596 | return | 595 | return | ||
| 597 | } | 596 | } | ||
| @@ -607,7 +606,7 @@ func (h *Handler) handleHistoryDelete(msg Message) { | |||||
| 607 | } | 606 | } | ||
| 608 | 607 | ||||
| 609 | if msg.HistoryID == "" { | 608 | if msg.HistoryID == "" { | ||
| 610 | - | log.Printf("[messaging] History delete: missing historyId") | 609 | + | logging.Warn(fmt.Sprintf("[messaging] History delete: missing historyId")) |
| 611 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Missing history ID')") | 610 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Missing history ID')") | ||
| 612 | return | 611 | return | ||
| 613 | } | 612 | } | ||
| @@ -615,7 +614,7 @@ func (h *Handler) handleHistoryDelete(msg Message) { | |||||
| 615 | // Convert string ID to int64 | 614 | // Convert string ID to int64 | ||
| 616 | id, err := strconv.ParseInt(msg.HistoryID, 10, 64) | 615 | id, err := strconv.ParseInt(msg.HistoryID, 10, 64) | ||
| 617 | if err != nil { | 616 | if err != nil { | ||
| 618 | - | log.Printf("[messaging] History delete: invalid ID format: %v", err) | 617 | + | logging.Error(fmt.Sprintf("[messaging] History delete: invalid ID format: %v", err)) |
| 619 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Invalid history ID format')") | 618 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Invalid history ID format')") | ||
| 620 | return | 619 | return | ||
| 621 | } | 620 | } | ||
| @@ -623,7 +622,7 @@ func (h *Handler) handleHistoryDelete(msg Message) { | |||||
| 623 | ctx := context.Background() | 622 | ctx := context.Background() | ||
| 624 | err = h.browserService.DeleteHistoryEntry(ctx, id) | 623 | err = h.browserService.DeleteHistoryEntry(ctx, id) | ||
| 625 | if err != nil { | 624 | if err != nil { | ||
| 626 | - | log.Printf("[messaging] Failed to delete history entry: %v", err) | 625 | + | logging.Error(fmt.Sprintf("[messaging] Failed to delete history entry: %v", err)) |
| 627 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to delete history entry')") | 626 | _ = h.webView.InjectScript("window.__dumber_history_error && window.__dumber_history_error('Failed to delete history entry')") | ||
| 628 | return | 627 | return | ||
| 629 | } | 628 | } | ||
| @@ -650,7 +649,7 @@ func (h *Handler) handleConsoleMessage(msg Message) { | |||||
| 650 | } | 649 | } | ||
| 651 | 650 | ||||
| 652 | if err := json.Unmarshal(msg.Payload, &consolePayload); err != nil { | 651 | if err := json.Unmarshal(msg.Payload, &consolePayload); err != nil { | ||
| 653 | - | log.Printf("[messaging] Failed to unmarshal console-message: %v", err) | 652 | + | logging.Error(fmt.Sprintf("[messaging] Failed to unmarshal console-message: %v", err)) |
| 654 | return | 653 | return | ||
| 655 | } | 654 | } | ||
| 656 | 655 | ||||
| @@ -661,44 +660,44 @@ func (h *Handler) handleConsoleMessage(msg Message) { | |||||
| 661 | // handleWebViewIDRequest responds to JavaScript requests for the webview ID | 660 | // handleWebViewIDRequest responds to JavaScript requests for the webview ID | ||
| 662 | func (h *Handler) handleWebViewIDRequest(msg Message) { | 661 | func (h *Handler) handleWebViewIDRequest(msg Message) { | ||
| 663 | if h.webView == nil { | 662 | if h.webView == nil { | ||
| 664 | - | log.Printf("[messaging] Cannot provide webview ID - no webview available") | 663 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide webview ID - no webview available")) |
| 665 | return | 664 | return | ||
| 666 | } | 665 | } | ||
| 667 | 666 | ||||
| 668 | // Check if WebView is destroyed before attempting any operations | 667 | // Check if WebView is destroyed before attempting any operations | ||
| 669 | if h.webView.IsDestroyed() { | 668 | if h.webView.IsDestroyed() { | ||
| 670 | - | log.Printf("[messaging] Cannot provide webview ID - webview is destroyed") | 669 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide webview ID - webview is destroyed")) |
| 671 | return | 670 | return | ||
| 672 | } | 671 | } | ||
| 673 | 672 | ||||
| 674 | webViewID := h.webView.ID() | 673 | webViewID := h.webView.ID() | ||
| 675 | - | log.Printf("[messaging] Sending webview ID %d to JavaScript", webViewID) | 674 | + | logging.Debug(fmt.Sprintf("[messaging] Sending webview ID %d to JavaScript", webViewID)) |
| 676 | 675 | ||||
| 677 | // Send the webview ID back to JavaScript via custom event | 676 | // Send the webview ID back to JavaScript via custom event | ||
| 678 | if err := h.webView.DispatchCustomEvent("dumber:webview-id", map[string]any{ | 677 | if err := h.webView.DispatchCustomEvent("dumber:webview-id", map[string]any{ | ||
| 679 | "webviewId": webViewID, | 678 | "webviewId": webViewID, | ||
| 680 | "timestamp": time.Now().UnixMilli(), | 679 | "timestamp": time.Now().UnixMilli(), | ||
| 681 | }); err != nil { | 680 | }); err != nil { | ||
| 682 | - | log.Printf("[messaging] Failed to send webview ID: %v", err) | 681 | + | logging.Error(fmt.Sprintf("[messaging] Failed to send webview ID: %v", err)) |
| 683 | } | 682 | } | ||
| 684 | } | 683 | } | ||
| 685 | 684 | ||||
| 686 | // handleGetSearchShortcuts sends search shortcuts configuration to JavaScript | 685 | // handleGetSearchShortcuts sends search shortcuts configuration to JavaScript | ||
| 687 | func (h *Handler) handleGetSearchShortcuts(msg Message) { | 686 | func (h *Handler) handleGetSearchShortcuts(msg Message) { | ||
| 688 | if h.webView == nil { | 687 | if h.webView == nil { | ||
| 689 | - | log.Printf("[messaging] Cannot provide search shortcuts - no webview available") | 688 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide search shortcuts - no webview available")) |
| 690 | return | 689 | return | ||
| 691 | } | 690 | } | ||
| 692 | 691 | ||||
| 693 | if h.webView.IsDestroyed() { | 692 | if h.webView.IsDestroyed() { | ||
| 694 | - | log.Printf("[messaging] Cannot provide search shortcuts - webview is destroyed") | 693 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide search shortcuts - webview is destroyed")) |
| 695 | return | 694 | return | ||
| 696 | } | 695 | } | ||
| 697 | 696 | ||||
| 698 | // Get search shortcuts from browser service config | 697 | // Get search shortcuts from browser service config | ||
| 699 | shortcuts, err := h.browserService.GetSearchShortcuts(context.Background()) | 698 | shortcuts, err := h.browserService.GetSearchShortcuts(context.Background()) | ||
| 700 | if err != nil { | 699 | if err != nil { | ||
| 701 | - | log.Printf("[messaging] Failed to get search shortcuts: %v", err) | 700 | + | logging.Error(fmt.Sprintf("[messaging] Failed to get search shortcuts: %v", err)) |
| 702 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to get search shortcuts')") | 701 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to get search shortcuts')") | ||
| 703 | return | 702 | return | ||
| 704 | } | 703 | } | ||
| @@ -706,25 +705,25 @@ func (h *Handler) handleGetSearchShortcuts(msg Message) { | |||||
| 706 | // Marshal to JSON | 705 | // Marshal to JSON | ||
| 707 | b, err := json.Marshal(shortcuts) | 706 | b, err := json.Marshal(shortcuts) | ||
| 708 | if err != nil { | 707 | if err != nil { | ||
| 709 | - | log.Printf("[messaging] Failed to marshal search shortcuts: %v", err) | 708 | + | logging.Error(fmt.Sprintf("[messaging] Failed to marshal search shortcuts: %v", err)) |
| 710 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to load search shortcuts')") | 709 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts_error && window.__dumber_search_shortcuts_error('Failed to load search shortcuts')") | ||
| 711 | return | 710 | return | ||
| 712 | } | 711 | } | ||
| 713 | 712 | ||||
| 714 | // Inject the search shortcuts into the page | 713 | // Inject the search shortcuts into the page | ||
| 715 | - | log.Printf("[messaging] Sending search shortcuts to JavaScript") | 714 | + | logging.Debug(fmt.Sprintf("[messaging] Sending search shortcuts to JavaScript")) |
| 716 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts && window.__dumber_search_shortcuts(" + string(b) + ")") | 715 | _ = h.webView.InjectScript("window.__dumber_search_shortcuts && window.__dumber_search_shortcuts(" + string(b) + ")") | ||
| 717 | } | 716 | } | ||
| 718 | 717 | ||||
| 719 | // handleGetColorPalettes sends color palettes configuration to JavaScript | 718 | // handleGetColorPalettes sends color palettes configuration to JavaScript | ||
| 720 | func (h *Handler) handleGetColorPalettes(msg Message) { | 719 | func (h *Handler) handleGetColorPalettes(msg Message) { | ||
| 721 | if h.webView == nil { | 720 | if h.webView == nil { | ||
| 722 | - | log.Printf("[messaging] Cannot provide color palettes - no webview available") | 721 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide color palettes - no webview available")) |
| 723 | return | 722 | return | ||
| 724 | } | 723 | } | ||
| 725 | 724 | ||||
| 726 | if h.webView.IsDestroyed() { | 725 | if h.webView.IsDestroyed() { | ||
| 727 | - | log.Printf("[messaging] Cannot provide color palettes - webview is destroyed") | 726 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide color palettes - webview is destroyed")) |
| 728 | return | 727 | return | ||
| 729 | } | 728 | } | ||
| 730 | 729 | ||||
| @@ -734,32 +733,32 @@ func (h *Handler) handleGetColorPalettes(msg Message) { | |||||
| 734 | // Marshal to JSON | 733 | // Marshal to JSON | ||
| 735 | b, err := json.Marshal(palettes) | 734 | b, err := json.Marshal(palettes) | ||
| 736 | if err != nil { | 735 | if err != nil { | ||
| 737 | - | log.Printf("[messaging] Failed to marshal color palettes: %v", err) | 736 | + | logging.Error(fmt.Sprintf("[messaging] Failed to marshal color palettes: %v", err)) |
| 738 | _ = h.webView.InjectScript("window.__dumber_color_palettes_error && window.__dumber_color_palettes_error('Failed to load color palettes')") | 737 | _ = h.webView.InjectScript("window.__dumber_color_palettes_error && window.__dumber_color_palettes_error('Failed to load color palettes')") | ||
| 739 | return | 738 | return | ||
| 740 | } | 739 | } | ||
| 741 | 740 | ||||
| 742 | // Inject the color palettes into the page | 741 | // Inject the color palettes into the page | ||
| 743 | - | log.Printf("[messaging] Sending color palettes to JavaScript") | 742 | + | logging.Debug(fmt.Sprintf("[messaging] Sending color palettes to JavaScript")) |
| 744 | _ = h.webView.InjectScript("window.__dumber_color_palettes && window.__dumber_color_palettes(" + string(b) + ")") | 743 | _ = h.webView.InjectScript("window.__dumber_color_palettes && window.__dumber_color_palettes(" + string(b) + ")") | ||
| 745 | } | 744 | } | ||
| 746 | 745 | ||||
| 747 | // handleGetFavorites sends all favorites to JavaScript | 746 | // handleGetFavorites sends all favorites to JavaScript | ||
| 748 | func (h *Handler) handleGetFavorites(msg Message) { | 747 | func (h *Handler) handleGetFavorites(msg Message) { | ||
| 749 | if h.webView == nil { | 748 | if h.webView == nil { | ||
| 750 | - | log.Printf("[messaging] Cannot provide favorites - no webview available") | 749 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide favorites - no webview available")) |
| 751 | return | 750 | return | ||
| 752 | } | 751 | } | ||
| 753 | 752 | ||||
| 754 | if h.webView.IsDestroyed() { | 753 | if h.webView.IsDestroyed() { | ||
| 755 | - | log.Printf("[messaging] Cannot provide favorites - webview is destroyed") | 754 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot provide favorites - webview is destroyed")) |
| 756 | return | 755 | return | ||
| 757 | } | 756 | } | ||
| 758 | 757 | ||||
| 759 | // Get favorites from browser service | 758 | // Get favorites from browser service | ||
| 760 | favorites, err := h.browserService.GetFavorites(context.Background()) | 759 | favorites, err := h.browserService.GetFavorites(context.Background()) | ||
| 761 | if err != nil { | 760 | if err != nil { | ||
| 762 | - | log.Printf("[messaging] Failed to get favorites: %v", err) | 761 | + | logging.Error(fmt.Sprintf("[messaging] Failed to get favorites: %v", err)) |
| 763 | _ = h.webView.InjectScript("window.__dumber_favorites_error && window.__dumber_favorites_error('Failed to get favorites')") | 762 | _ = h.webView.InjectScript("window.__dumber_favorites_error && window.__dumber_favorites_error('Failed to get favorites')") | ||
| 764 | return | 763 | return | ||
| 765 | } | 764 | } | ||
| @@ -767,37 +766,37 @@ func (h *Handler) handleGetFavorites(msg Message) { | |||||
| 767 | // Marshal to JSON | 766 | // Marshal to JSON | ||
| 768 | b, err := json.Marshal(favorites) | 767 | b, err := json.Marshal(favorites) | ||
| 769 | if err != nil { | 768 | if err != nil { | ||
| 770 | - | log.Printf("[messaging] Failed to marshal favorites: %v", err) | 769 | + | logging.Error(fmt.Sprintf("[messaging] Failed to marshal favorites: %v", err)) |
| 771 | _ = h.webView.InjectScript("window.__dumber_favorites_error && window.__dumber_favorites_error('Failed to load favorites')") | 770 | _ = h.webView.InjectScript("window.__dumber_favorites_error && window.__dumber_favorites_error('Failed to load favorites')") | ||
| 772 | return | 771 | return | ||
| 773 | } | 772 | } | ||
| 774 | 773 | ||||
| 775 | // Inject the favorites into the page | 774 | // Inject the favorites into the page | ||
| 776 | - | log.Printf("[messaging] Sending %d favorites to JavaScript", len(favorites)) | 775 | + | logging.Debug(fmt.Sprintf("[messaging] Sending %d favorites to JavaScript", len(favorites))) |
| 777 | _ = h.webView.InjectScript("window.__dumber_favorites && window.__dumber_favorites(" + string(b) + ")") | 776 | _ = h.webView.InjectScript("window.__dumber_favorites && window.__dumber_favorites(" + string(b) + ")") | ||
| 778 | } | 777 | } | ||
| 779 | 778 | ||||
| 780 | // handleToggleFavorite adds or removes a URL from favorites | 779 | // handleToggleFavorite adds or removes a URL from favorites | ||
| 781 | func (h *Handler) handleToggleFavorite(msg Message) { | 780 | func (h *Handler) handleToggleFavorite(msg Message) { | ||
| 782 | if h.webView == nil { | 781 | if h.webView == nil { | ||
| 783 | - | log.Printf("[messaging] Cannot toggle favorite - no webview available") | 782 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot toggle favorite - no webview available")) |
| 784 | return | 783 | return | ||
| 785 | } | 784 | } | ||
| 786 | 785 | ||||
| 787 | if h.webView.IsDestroyed() { | 786 | if h.webView.IsDestroyed() { | ||
| 788 | - | log.Printf("[messaging] Cannot toggle favorite - webview is destroyed") | 787 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot toggle favorite - webview is destroyed")) |
| 789 | return | 788 | return | ||
| 790 | } | 789 | } | ||
| 791 | 790 | ||||
| 792 | if msg.URL == "" { | 791 | if msg.URL == "" { | ||
| 793 | - | log.Printf("[messaging] Cannot toggle favorite - URL is empty") | 792 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot toggle favorite - URL is empty")) |
| 794 | return | 793 | return | ||
| 795 | } | 794 | } | ||
| 796 | 795 | ||||
| 797 | // Toggle the favorite | 796 | // Toggle the favorite | ||
| 798 | added, err := h.browserService.ToggleFavorite(context.Background(), msg.URL, msg.Title, msg.FaviconURL) | 797 | added, err := h.browserService.ToggleFavorite(context.Background(), msg.URL, msg.Title, msg.FaviconURL) | ||
| 799 | if err != nil { | 798 | if err != nil { | ||
| 800 | - | log.Printf("[messaging] Failed to toggle favorite for %s: %v", msg.URL, err) | 799 | + | logging.Error(fmt.Sprintf("[messaging] Failed to toggle favorite for %s: %v", msg.URL, err)) |
| 801 | _ = h.webView.InjectScript("window.__dumber_favorite_toggled_error && window.__dumber_favorite_toggled_error('Failed to toggle favorite')") | 800 | _ = h.webView.InjectScript("window.__dumber_favorite_toggled_error && window.__dumber_favorite_toggled_error('Failed to toggle favorite')") | ||
| 802 | return | 801 | return | ||
| 803 | } | 802 | } | ||
| @@ -809,35 +808,35 @@ func (h *Handler) handleToggleFavorite(msg Message) { | |||||
| 809 | } | 808 | } | ||
| 810 | b, err := json.Marshal(result) | 809 | b, err := json.Marshal(result) | ||
| 811 | if err != nil { | 810 | if err != nil { | ||
| 812 | - | log.Printf("[messaging] Failed to marshal toggle result: %v", err) | 811 | + | logging.Error(fmt.Sprintf("[messaging] Failed to marshal toggle result: %v", err)) |
| 813 | return | 812 | return | ||
| 814 | } | 813 | } | ||
| 815 | 814 | ||||
| 816 | - | log.Printf("[messaging] Favorite toggled for %s (added: %v)", msg.URL, added) | 815 | + | logging.Debug(fmt.Sprintf("[messaging] Favorite toggled for %s (added: %v)", msg.URL, added)) |
| 817 | _ = h.webView.InjectScript("window.__dumber_favorite_toggled && window.__dumber_favorite_toggled(" + string(b) + ")") | 816 | _ = h.webView.InjectScript("window.__dumber_favorite_toggled && window.__dumber_favorite_toggled(" + string(b) + ")") | ||
| 818 | } | 817 | } | ||
| 819 | 818 | ||||
| 820 | // handleIsFavorite checks if a URL is favorited | 819 | // handleIsFavorite checks if a URL is favorited | ||
| 821 | func (h *Handler) handleIsFavorite(msg Message) { | 820 | func (h *Handler) handleIsFavorite(msg Message) { | ||
| 822 | if h.webView == nil { | 821 | if h.webView == nil { | ||
| 823 | - | log.Printf("[messaging] Cannot check favorite - no webview available") | 822 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot check favorite - no webview available")) |
| 824 | return | 823 | return | ||
| 825 | } | 824 | } | ||
| 826 | 825 | ||||
| 827 | if h.webView.IsDestroyed() { | 826 | if h.webView.IsDestroyed() { | ||
| 828 | - | log.Printf("[messaging] Cannot check favorite - webview is destroyed") | 827 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot check favorite - webview is destroyed")) |
| 829 | return | 828 | return | ||
| 830 | } | 829 | } | ||
| 831 | 830 | ||||
| 832 | if msg.URL == "" { | 831 | if msg.URL == "" { | ||
| 833 | - | log.Printf("[messaging] Cannot check favorite - URL is empty") | 832 | + | logging.Warn(fmt.Sprintf("[messaging] Cannot check favorite - URL is empty")) |
| 834 | return | 833 | return | ||
| 835 | } | 834 | } | ||
| 836 | 835 | ||||
| 837 | // Check if the URL is favorited | 836 | // Check if the URL is favorited | ||
| 838 | isFavorite, err := h.browserService.IsFavorite(context.Background(), msg.URL) | 837 | isFavorite, err := h.browserService.IsFavorite(context.Background(), msg.URL) | ||
| 839 | if err != nil { | 838 | if err != nil { | ||
| 840 | - | log.Printf("[messaging] Failed to check if favorite for %s: %v", msg.URL, err) | 839 | + | logging.Error(fmt.Sprintf("[messaging] Failed to check if favorite for %s: %v", msg.URL, err)) |
| 841 | return | 840 | return | ||
| 842 | } | 841 | } | ||
| 843 | 842 | ||||
| @@ -848,7 +847,7 @@ func (h *Handler) handleIsFavorite(msg Message) { | |||||
| 848 | } | 847 | } | ||
| 849 | b, err := json.Marshal(result) | 848 | b, err := json.Marshal(result) | ||
| 850 | if err != nil { | 849 | if err != nil { | ||
| 851 | - | log.Printf("[messaging] Failed to marshal is_favorite result: %v", err) | 850 | + | logging.Error(fmt.Sprintf("[messaging] Failed to marshal is_favorite result: %v", err)) |
| 852 | return | 851 | return | ||
| 853 | } | 852 | } | ||
| 854 | 853 | ||||
| @@ -869,6 +868,6 @@ func (h *Handler) handleKeyboardBlocking(msg Message) { | |||||
| 869 | } | 868 | } | ||
| 870 | 869 | ||||
| 871 | if err := h.webView.InjectScript(script); err != nil { | 870 | if err := h.webView.InjectScript(script); err != nil { | ||
| 872 | - | log.Printf("[ERROR] Failed to inject keyboard blocking script: %v", err) | 871 | + | logging.Error(fmt.Sprintf("[ERROR] Failed to inject keyboard blocking script: %v", err)) |
| 873 | } | 872 | } | ||
| 874 | } | 873 | } | ||
M internal/cache/binary_format.go +2 -3
| @@ -4,7 +4,6 @@ package cache | |||||
| 4 | import ( | 4 | import ( | ||
| 5 | "encoding/binary" | 5 | "encoding/binary" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "os" | 7 | "os" | ||
| 9 | "syscall" | 8 | "syscall" | ||
| 10 | "unsafe" | 9 | "unsafe" | ||
| @@ -214,7 +213,7 @@ func (c *DmenuFuzzyCache) writeHeader(data []byte, offset int, totalSize int) in | |||||
| 214 | logging.Warn(fmt.Sprintf("too many entries for binary format: %d, truncating", len(c.entries))) | 213 | logging.Warn(fmt.Sprintf("too many entries for binary format: %d, truncating", len(c.entries))) | ||
| 215 | } | 214 | } | ||
| 216 | if indexOffset < 0 { | 215 | if indexOffset < 0 { | ||
| 217 | - | log.Printf("Warning: negative index offset: %d, using 0", indexOffset) | 216 | + | logging.Warn(fmt.Sprintf("Warning: negative index offset: %d, using 0", indexOffset)) |
| 218 | indexOffset = 0 | 217 | indexOffset = 0 | ||
| 219 | } | 218 | } | ||
| 220 | 219 | ||||
| @@ -446,7 +445,7 @@ func IsValidCacheFile(filename string) bool { | |||||
| 446 | } | 445 | } | ||
| 447 | defer func() { | 446 | defer func() { | ||
| 448 | if closeErr := file.Close(); closeErr != nil { | 447 | if closeErr := file.Close(); closeErr != nil { | ||
| 449 | - | log.Printf("Warning: failed to close cache validation file: %v", closeErr) | 448 | + | logging.Warn(fmt.Sprintf("Warning: failed to close cache validation file: %v", closeErr)) |
| 450 | } | 449 | } | ||
| 451 | }() | 450 | }() | ||
| 452 | 451 | ||||
M internal/cache/filter_cache.go +3 -4
| @@ -4,7 +4,6 @@ import ( | |||||
| 4 | "encoding/binary" | 4 | "encoding/binary" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | "io" | 6 | "io" | ||
| 7 | - | "log" | |||
| 8 | "os" | 7 | "os" | ||
| 9 | "syscall" | 8 | "syscall" | ||
| 10 | "time" | 9 | "time" | ||
| @@ -116,7 +115,7 @@ func (fc *FilterCache) LoadMapped() (*FilterData, error) { | |||||
| 116 | header, err := fc.parseHeader() | 115 | header, err := fc.parseHeader() | ||
| 117 | if err != nil { | 116 | if err != nil { | ||
| 118 | if unmapErr := fc.Unmap(); unmapErr != nil { | 117 | if unmapErr := fc.Unmap(); unmapErr != nil { | ||
| 119 | - | log.Printf("[filter-cache] failed to unmap after header parse error: %v", unmapErr) | 118 | + | logging.Warn(fmt.Sprintf("[filter-cache] failed to unmap after header parse error: %v", unmapErr)) |
| 120 | } | 119 | } | ||
| 121 | return nil, fmt.Errorf("invalid cache header: %w", err) | 120 | return nil, fmt.Errorf("invalid cache header: %w", err) | ||
| 122 | } | 121 | } | ||
| @@ -124,14 +123,14 @@ func (fc *FilterCache) LoadMapped() (*FilterData, error) { | |||||
| 124 | // Validate magic and version | 123 | // Validate magic and version | ||
| 125 | if header.Magic != cacheMagic { | 124 | if header.Magic != cacheMagic { | ||
| 126 | if unmapErr := fc.Unmap(); unmapErr != nil { | 125 | if unmapErr := fc.Unmap(); unmapErr != nil { | ||
| 127 | - | log.Printf("[filter-cache] failed to unmap after magic validation: %v", unmapErr) | 126 | + | logging.Warn(fmt.Sprintf("[filter-cache] failed to unmap after magic validation: %v", unmapErr)) |
| 128 | } | 127 | } | ||
| 129 | return nil, fmt.Errorf("invalid cache magic bytes") | 128 | return nil, fmt.Errorf("invalid cache magic bytes") | ||
| 130 | } | 129 | } | ||
| 131 | 130 | ||||
| 132 | if header.Version != cacheFormatVersion { | 131 | if header.Version != cacheFormatVersion { | ||
| 133 | if unmapErr := fc.Unmap(); unmapErr != nil { | 132 | if unmapErr := fc.Unmap(); unmapErr != nil { | ||
| 134 | - | log.Printf("[filter-cache] failed to unmap after version validation: %v", unmapErr) | 133 | + | logging.Warn(fmt.Sprintf("[filter-cache] failed to unmap after version validation: %v", unmapErr)) |
| 135 | } | 134 | } | ||
| 136 | return nil, fmt.Errorf("unsupported cache version: %d", header.Version) | 135 | return nil, fmt.Errorf("unsupported cache version: %d", header.Version) | ||
| 137 | } | 136 | } | ||
M internal/cache/generic/cache.go +5 -3
| @@ -2,8 +2,10 @@ package generic | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | "sync" | 6 | "sync" | ||
| 7 | + | ||||
| 8 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | ) | 9 | ) | ||
| 8 | 10 | ||||
| 9 | // Cache provides a generic interface for caching any type of data. | 11 | // Cache provides a generic interface for caching any type of data. | ||
| @@ -109,7 +111,7 @@ func (c *GenericCache[K, V]) Set(key K, value V) error { | |||||
| 109 | 111 | ||||
| 110 | ctx := context.Background() | 112 | ctx := context.Background() | ||
| 111 | if err := c.dbOps.Persist(ctx, key, value); err != nil { | 113 | if err := c.dbOps.Persist(ctx, key, value); err != nil { | ||
| 112 | - | log.Printf("Warning: async persist failed for key %v: %v", key, err) | 114 | + | logging.Warn(fmt.Sprintf("Warning: async persist failed for key %v: %v", key, err)) |
| 113 | } | 115 | } | ||
| 114 | }() | 116 | }() | ||
| 115 | 117 | ||||
| @@ -129,7 +131,7 @@ func (c *GenericCache[K, V]) Delete(key K) error { | |||||
| 129 | 131 | ||||
| 130 | ctx := context.Background() | 132 | ctx := context.Background() | ||
| 131 | if err := c.dbOps.Delete(ctx, key); err != nil { | 133 | if err := c.dbOps.Delete(ctx, key); err != nil { | ||
| 132 | - | log.Printf("Warning: async delete failed for key %v: %v", key, err) | 134 | + | logging.Warn(fmt.Sprintf("Warning: async delete failed for key %v: %v", key, err)) |
| 133 | } | 135 | } | ||
| 134 | }() | 136 | }() | ||
| 135 | 137 | ||||
M internal/db/init.go +5 -5
| @@ -3,10 +3,10 @@ package db | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "database/sql" | 4 | "database/sql" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "os" | 6 | "os" | ||
| 8 | "path/filepath" | 7 | "path/filepath" | ||
| 9 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | "github.com/bnema/dumber/internal/migrations" | 10 | "github.com/bnema/dumber/internal/migrations" | ||
| 11 | _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver | 11 | _ "github.com/ncruces/go-sqlite3/driver" // SQLite driver | ||
| 12 | _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite | 12 | _ "github.com/ncruces/go-sqlite3/embed" // Embed SQLite | ||
| @@ -32,7 +32,7 @@ func InitDB(dbPath string) (*sql.DB, error) { | |||||
| 32 | // Test the connection | 32 | // Test the connection | ||
| 33 | if err := database.Ping(); err != nil { | 33 | if err := database.Ping(); err != nil { | ||
| 34 | if err := database.Close(); err != nil { | 34 | if err := database.Close(); err != nil { | ||
| 35 | - | log.Printf("Warning: failed to close database: %v", err) | 35 | + | logging.Warn(fmt.Sprintf("Warning: failed to close database: %v", err)) |
| 36 | } | 36 | } | ||
| 37 | return nil, fmt.Errorf("failed to connect to database: %w", err) | 37 | return nil, fmt.Errorf("failed to connect to database: %w", err) | ||
| 38 | } | 38 | } | ||
| @@ -50,19 +50,19 @@ func InitDB(dbPath string) (*sql.DB, error) { | |||||
| 50 | for _, pragma := range pragmas { | 50 | for _, pragma := range pragmas { | ||
| 51 | if _, err := database.Exec(pragma); err != nil { | 51 | if _, err := database.Exec(pragma); err != nil { | ||
| 52 | if err := database.Close(); err != nil { | 52 | if err := database.Close(); err != nil { | ||
| 53 | - | log.Printf("Warning: failed to close database: %v", err) | 53 | + | logging.Warn(fmt.Sprintf("Warning: failed to close database: %v", err)) |
| 54 | } | 54 | } | ||
| 55 | return nil, fmt.Errorf("failed to set pragma %q: %w", pragma, err) | 55 | return nil, fmt.Errorf("failed to set pragma %q: %w", pragma, err) | ||
| 56 | } | 56 | } | ||
| 57 | } | 57 | } | ||
| 58 | 58 | ||||
| 59 | - | log.Printf("SQLite configured with WAL mode and performance optimizations") | 59 | + | logging.Info(fmt.Sprintf("SQLite configured with WAL mode and performance optimizations")) |
| 60 | 60 | ||||
| 61 | // Run embedded migrations - single source of truth for schema initialization | 61 | // Run embedded migrations - single source of truth for schema initialization | ||
| 62 | // RunEmbeddedMigrations is idempotent and checks if migrations are already applied | 62 | // RunEmbeddedMigrations is idempotent and checks if migrations are already applied | ||
| 63 | if err := migrations.RunEmbeddedMigrations(database); err != nil { | 63 | if err := migrations.RunEmbeddedMigrations(database); err != nil { | ||
| 64 | if err := database.Close(); err != nil { | 64 | if err := database.Close(); err != nil { | ||
| 65 | - | log.Printf("Warning: failed to close database: %v", err) | 65 | + | logging.Warn(fmt.Sprintf("Warning: failed to close database: %v", err)) |
| 66 | } | 66 | } | ||
| 67 | return nil, fmt.Errorf("failed to run database migrations: %w", err) | 67 | return nil, fmt.Errorf("failed to run database migrations: %w", err) | ||
| 68 | } | 68 | } | ||
M internal/filtering/updater.go +2 -3
| @@ -4,7 +4,6 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "crypto/sha256" | 5 | "crypto/sha256" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "net/http" | 7 | "net/http" | ||
| 9 | "strings" | 8 | "strings" | ||
| 10 | "sync" | 9 | "sync" | ||
| @@ -141,7 +140,7 @@ func (fu *FilterUpdater) checkSingleUpdate(ctx context.Context, url string) (*Fi | |||||
| 141 | } | 140 | } | ||
| 142 | defer func() { | 141 | defer func() { | ||
| 143 | if err := resp.Body.Close(); err != nil { | 142 | if err := resp.Body.Close(); err != nil { | ||
| 144 | - | log.Printf("warning: failed to close response body: %v", err) | 143 | + | logging.Warn(fmt.Sprintf("warning: failed to close response body: %v", err)) |
| 145 | } | 144 | } | ||
| 146 | }() | 145 | }() | ||
| 147 | 146 | ||||
| @@ -190,7 +189,7 @@ func (fu *FilterUpdater) downloadUpdate(ctx context.Context, url, lastModified, | |||||
| 190 | } | 189 | } | ||
| 191 | defer func() { | 190 | defer func() { | ||
| 192 | if err := resp.Body.Close(); err != nil { | 191 | if err := resp.Body.Close(); err != nil { | ||
| 193 | - | log.Printf("warning: failed to close response body: %v", err) | 192 | + | logging.Warn(fmt.Sprintf("warning: failed to close response body: %v", err)) |
| 194 | } | 193 | } | ||
| 195 | }() | 194 | }() | ||
| 196 | 195 | ||||
M internal/migrations/migrations.go +11 -10
| @@ -4,10 +4,11 @@ import ( | |||||
| 4 | "database/sql" | 4 | "database/sql" | ||
| 5 | "embed" | 5 | "embed" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | - | "log" | |||
| 8 | "sort" | 7 | "sort" | ||
| 9 | "strconv" | 8 | "strconv" | ||
| 10 | "strings" | 9 | "strings" | ||
| 10 | + | ||||
| 11 | + | "github.com/bnema/dumber/internal/logging" | |||
| 11 | ) | 12 | ) | ||
| 12 | 13 | ||||
| 13 | //go:embed *.sql | 14 | //go:embed *.sql | ||
| @@ -36,14 +37,14 @@ func GetMigrations() ([]Migration, error) { | |||||
| 36 | // Parse version from filename (e.g., "001_initial.sql" -> version 1) | 37 | // Parse version from filename (e.g., "001_initial.sql" -> version 1) | ||
| 37 | parts := strings.SplitN(entry.Name(), "_", 2) | 38 | parts := strings.SplitN(entry.Name(), "_", 2) | ||
| 38 | if len(parts) != 2 { | 39 | if len(parts) != 2 { | ||
| 39 | - | log.Printf("Warning: skipping migration file with invalid name format: %s", entry.Name()) | 40 | + | logging.Warn(fmt.Sprintf("Warning: skipping migration file with invalid name format: %s", entry.Name())) |
| 40 | continue | 41 | continue | ||
| 41 | } | 42 | } | ||
| 42 | 43 | ||||
| 43 | versionStr := parts[0] | 44 | versionStr := parts[0] | ||
| 44 | version, err := strconv.Atoi(versionStr) | 45 | version, err := strconv.Atoi(versionStr) | ||
| 45 | if err != nil { | 46 | if err != nil { | ||
| 46 | - | log.Printf("Warning: skipping migration file with invalid version: %s", entry.Name()) | 47 | + | logging.Warn(fmt.Sprintf("Warning: skipping migration file with invalid version: %s", entry.Name())) |
| 47 | continue | 48 | continue | ||
| 48 | } | 49 | } | ||
| 49 | 50 | ||||
| @@ -123,7 +124,7 @@ func applyMigration(db *sql.DB, migration Migration) error { | |||||
| 123 | return nil | 124 | return nil | ||
| 124 | } | 125 | } | ||
| 125 | 126 | ||||
| 126 | - | log.Printf("Applying migration %03d: %s", migration.Version, migration.Name) | 127 | + | logging.Info(fmt.Sprintf("Applying migration %03d: %s", migration.Version, migration.Name)) |
| 127 | 128 | ||||
| 128 | // Use transaction for safety | 129 | // Use transaction for safety | ||
| 129 | tx, err := db.Begin() | 130 | tx, err := db.Begin() | ||
| @@ -133,7 +134,7 @@ func applyMigration(db *sql.DB, migration Migration) error { | |||||
| 133 | defer func() { | 134 | defer func() { | ||
| 134 | if err != nil { | 135 | if err != nil { | ||
| 135 | if rollbackErr := tx.Rollback(); rollbackErr != nil { | 136 | if rollbackErr := tx.Rollback(); rollbackErr != nil { | ||
| 136 | - | log.Printf("failed to rollback migration transaction: %v", rollbackErr) | 137 | + | logging.Error(fmt.Sprintf("failed to rollback migration transaction: %v", rollbackErr)) |
| 137 | } | 138 | } | ||
| 138 | } | 139 | } | ||
| 139 | }() | 140 | }() | ||
| @@ -155,7 +156,7 @@ func applyMigration(db *sql.DB, migration Migration) error { | |||||
| 155 | return fmt.Errorf("failed to commit migration transaction: %w", err) | 156 | return fmt.Errorf("failed to commit migration transaction: %w", err) | ||
| 156 | } | 157 | } | ||
| 157 | 158 | ||||
| 158 | - | log.Printf("Successfully applied migration %03d: %s", migration.Version, migration.Name) | 159 | + | logging.Info(fmt.Sprintf("Successfully applied migration %03d: %s", migration.Version, migration.Name)) |
| 159 | return nil | 160 | return nil | ||
| 160 | } | 161 | } | ||
| 161 | 162 | ||||
| @@ -179,7 +180,7 @@ func GetAppliedMigrations(db *sql.DB) ([]int, error) { | |||||
| 179 | } | 180 | } | ||
| 180 | defer func() { | 181 | defer func() { | ||
| 181 | if err := rows.Close(); err != nil { | 182 | if err := rows.Close(); err != nil { | ||
| 182 | - | log.Printf("failed to close migration query rows: %v", err) | 183 | + | logging.Warn(fmt.Sprintf("failed to close migration query rows: %v", err)) |
| 183 | } | 184 | } | ||
| 184 | }() | 185 | }() | ||
| 185 | 186 | ||||
| @@ -230,13 +231,13 @@ func VerifyAllMigrationsApplied(db *sql.DB) error { | |||||
| 230 | 231 | ||||
| 231 | // Report results | 232 | // Report results | ||
| 232 | if len(missingMigrations) > 0 { | 233 | if len(missingMigrations) > 0 { | ||
| 233 | - | log.Printf("WARNING: %d migrations are not applied:", len(missingMigrations)) | 234 | + | logging.Warn(fmt.Sprintf("WARNING: %d migrations are not applied:", len(missingMigrations))) |
| 234 | for _, migration := range missingMigrations { | 235 | for _, migration := range missingMigrations { | ||
| 235 | - | log.Printf(" - Migration %03d: %s", migration.Version, migration.Name) | 236 | + | logging.Warn(fmt.Sprintf(" - Migration %03d: %s", migration.Version, migration.Name)) |
| 236 | } | 237 | } | ||
| 237 | return fmt.Errorf("%d migrations are not applied", len(missingMigrations)) | 238 | return fmt.Errorf("%d migrations are not applied", len(missingMigrations)) | ||
| 238 | } | 239 | } | ||
| 239 | 240 | ||||
| 240 | - | log.Printf("Migration verification: All %d migrations are applied", len(allMigrations)) | 241 | + | logging.Info(fmt.Sprintf("Migration verification: All %d migrations are applied", len(allMigrations))) |
| 241 | return nil | 242 | return nil | ||
| 242 | } | 243 | } | ||
M internal/services/browser.go +19 -19
| @@ -6,7 +6,6 @@ import ( | |||||
| 6 | "database/sql" | 6 | "database/sql" | ||
| 7 | "embed" | 7 | "embed" | ||
| 8 | "fmt" | 8 | "fmt" | ||
| 9 | - | "log" | |||
| 10 | "math" | 9 | "math" | ||
| 11 | neturl "net/url" | 10 | neturl "net/url" | ||
| 12 | "sync" | 11 | "sync" | ||
| @@ -15,6 +14,7 @@ import ( | |||||
| 15 | "github.com/bnema/dumber/internal/cache" | 14 | "github.com/bnema/dumber/internal/cache" | ||
| 16 | "github.com/bnema/dumber/internal/config" | 15 | "github.com/bnema/dumber/internal/config" | ||
| 17 | "github.com/bnema/dumber/internal/db" | 16 | "github.com/bnema/dumber/internal/db" | ||
| 17 | + | "github.com/bnema/dumber/internal/logging" | |||
| 18 | "github.com/bnema/dumber/internal/parser" | 18 | "github.com/bnema/dumber/internal/parser" | ||
| 19 | "github.com/bnema/dumber/pkg/webkit" | 19 | "github.com/bnema/dumber/pkg/webkit" | ||
| 20 | ) | 20 | ) | ||
| @@ -126,14 +126,14 @@ func (s *BrowserService) AttachWebView(view *webkit.WebView) { | |||||
| 126 | 126 | ||||
| 127 | // LoadGUIBundle loads the unified GUI bundle from assets | 127 | // LoadGUIBundle loads the unified GUI bundle from assets | ||
| 128 | func (s *BrowserService) LoadGUIBundle(assets embed.FS) error { | 128 | func (s *BrowserService) LoadGUIBundle(assets embed.FS) error { | ||
| 129 | - | log.Printf("[browser] Attempting to load GUI bundle from assets/gui/gui.min.js") | 129 | + | logging.Debug(fmt.Sprintf("[browser] Attempting to load GUI bundle from assets/gui/gui.min.js")) |
| 130 | bundleBytes, err := assets.ReadFile("assets/gui/gui.min.js") | 130 | bundleBytes, err := assets.ReadFile("assets/gui/gui.min.js") | ||
| 131 | if err != nil { | 131 | if err != nil { | ||
| 132 | - | log.Printf("[browser] ERROR: Failed to load GUI bundle: %v", err) | 132 | + | logging.Error(fmt.Sprintf("[browser] ERROR: Failed to load GUI bundle: %v", err)) |
| 133 | return fmt.Errorf("failed to load GUI bundle: %w", err) | 133 | return fmt.Errorf("failed to load GUI bundle: %w", err) | ||
| 134 | } | 134 | } | ||
| 135 | s.guiBundle = string(bundleBytes) | 135 | s.guiBundle = string(bundleBytes) | ||
| 136 | - | log.Printf("[browser] GUI bundle loaded successfully into browser service, size: %d bytes", len(bundleBytes)) | 136 | + | logging.Info(fmt.Sprintf("[browser] GUI bundle loaded successfully into browser service, size: %d bytes", len(bundleBytes))) |
| 137 | return nil | 137 | return nil | ||
| 138 | } | 138 | } | ||
| 139 | 139 | ||||
| @@ -438,7 +438,7 @@ func (s *BrowserService) recordHistory(ctx context.Context, url, title string) e | |||||
| 438 | return nil | 438 | return nil | ||
| 439 | default: | 439 | default: | ||
| 440 | // Queue full, write directly (fallback) | 440 | // Queue full, write directly (fallback) | ||
| 441 | - | log.Printf("Warning: history queue full, writing directly") | 441 | + | logging.Warn(fmt.Sprintf("Warning: history queue full, writing directly")) |
| 442 | return s.dbQueries.AddOrUpdateHistory(ctx, normalizedURL, titleNull) | 442 | return s.dbQueries.AddOrUpdateHistory(ctx, normalizedURL, titleNull) | ||
| 443 | } | 443 | } | ||
| 444 | } | 444 | } | ||
| @@ -463,11 +463,11 @@ func (s *BrowserService) processHistoryQueue() { | |||||
| 463 | // but we can still batch the operations to reduce overhead | 463 | // but we can still batch the operations to reduce overhead | ||
| 464 | for _, update := range batch { | 464 | for _, update := range batch { | ||
| 465 | if err := s.dbQueries.AddOrUpdateHistory(ctx, update.url, update.title); err != nil { | 465 | if err := s.dbQueries.AddOrUpdateHistory(ctx, update.url, update.title); err != nil { | ||
| 466 | - | log.Printf("Warning: failed to write history for %s: %v", update.url, err) | 466 | + | logging.Warn(fmt.Sprintf("Warning: failed to write history for %s: %v", update.url, err)) |
| 467 | } | 467 | } | ||
| 468 | } | 468 | } | ||
| 469 | 469 | ||||
| 470 | - | log.Printf("Flushed %d history updates to database", len(batch)) | 470 | + | logging.Debug(fmt.Sprintf("Flushed %d history updates to database", len(batch))) |
| 471 | 471 | ||||
| 472 | // Track history entries for fuzzy cache refresh | 472 | // Track history entries for fuzzy cache refresh | ||
| 473 | batchSize := len(batch) | 473 | batchSize := len(batch) | ||
| @@ -481,8 +481,8 @@ func (s *BrowserService) processHistoryQueue() { | |||||
| 481 | timeSinceRefresh := time.Since(s.lastCacheRefresh) | 481 | timeSinceRefresh := time.Since(s.lastCacheRefresh) | ||
| 482 | 482 | ||||
| 483 | if s.historySinceRefresh >= minEntries && timeSinceRefresh >= minInterval { | 483 | if s.historySinceRefresh >= minEntries && timeSinceRefresh >= minInterval { | ||
| 484 | - | log.Printf("[cache] Triggering async fuzzy cache refresh (%d new entries, %v since last refresh)", | 484 | + | logging.Debug(fmt.Sprintf("[cache] Triggering async fuzzy cache refresh (%d new entries, %v since last refresh)", |
| 485 | - | s.historySinceRefresh, timeSinceRefresh) | 485 | + | s.historySinceRefresh, timeSinceRefresh)) |
| 486 | 486 | ||||
| 487 | // Async refresh in background (non-blocking) | 487 | // Async refresh in background (non-blocking) | ||
| 488 | s.fuzzyCache.InvalidateAndRefresh(ctx) | 488 | s.fuzzyCache.InvalidateAndRefresh(ctx) | ||
| @@ -524,10 +524,10 @@ func (s *BrowserService) FlushHistoryQueue(ctx context.Context) error { | |||||
| 524 | // Wait for completion with context cancellation support | 524 | // Wait for completion with context cancellation support | ||
| 525 | select { | 525 | select { | ||
| 526 | case <-s.historyFlushDone: | 526 | case <-s.historyFlushDone: | ||
| 527 | - | log.Printf("History queue flushed and closed") | 527 | + | logging.Info(fmt.Sprintf("History queue flushed and closed")) |
| 528 | case <-ctx.Done(): | 528 | case <-ctx.Done(): | ||
| 529 | flushErr = fmt.Errorf("history queue flush cancelled: %w", ctx.Err()) | 529 | flushErr = fmt.Errorf("history queue flush cancelled: %w", ctx.Err()) | ||
| 530 | - | log.Printf("Warning: %v", flushErr) | 530 | + | logging.Warn(fmt.Sprintf("Warning: %v", flushErr)) |
| 531 | } | 531 | } | ||
| 532 | }) | 532 | }) | ||
| 533 | return flushErr | 533 | return flushErr | ||
| @@ -581,7 +581,7 @@ func (s *BrowserService) LoadZoomCacheFromDB(ctx context.Context) error { | |||||
| 581 | } | 581 | } | ||
| 582 | 582 | ||||
| 583 | loadedCount := len(s.zoomCache.List()) | 583 | loadedCount := len(s.zoomCache.List()) | ||
| 584 | - | log.Printf("Loaded %d zoom levels into cache", loadedCount) | 584 | + | logging.Info(fmt.Sprintf("Loaded %d zoom levels into cache", loadedCount)) |
| 585 | return nil | 585 | return nil | ||
| 586 | } | 586 | } | ||
| 587 | 587 | ||||
| @@ -593,7 +593,7 @@ func (s *BrowserService) LoadCertCacheFromDB(ctx context.Context) error { | |||||
| 593 | } | 593 | } | ||
| 594 | 594 | ||||
| 595 | loadedCount := len(s.certCache.List()) | 595 | loadedCount := len(s.certCache.List()) | ||
| 596 | - | log.Printf("Loaded %d certificate validations into cache", loadedCount) | 596 | + | logging.Info(fmt.Sprintf("Loaded %d certificate validations into cache", loadedCount)) |
| 597 | return nil | 597 | return nil | ||
| 598 | } | 598 | } | ||
| 599 | 599 | ||||
| @@ -607,8 +607,8 @@ func (s *BrowserService) LoadFuzzyCacheFromDB(ctx context.Context) error { | |||||
| 607 | } | 607 | } | ||
| 608 | 608 | ||||
| 609 | stats := s.fuzzyCache.Stats() | 609 | stats := s.fuzzyCache.Stats() | ||
| 610 | - | log.Printf("Loaded fuzzy cache: %d entries, %d trigrams", | 610 | + | logging.Info(fmt.Sprintf("Loaded fuzzy cache: %d entries, %d trigrams", |
| 611 | - | stats.EntryCount, stats.TrigramCount) | 611 | + | stats.EntryCount, stats.TrigramCount)) |
| 612 | return nil | 612 | return nil | ||
| 613 | } | 613 | } | ||
| 614 | 614 | ||||
| @@ -620,7 +620,7 @@ func (s *BrowserService) LoadFavoritesCacheFromDB(ctx context.Context) error { | |||||
| 620 | } | 620 | } | ||
| 621 | 621 | ||||
| 622 | loadedCount := len(s.favoritesCache.List()) | 622 | loadedCount := len(s.favoritesCache.List()) | ||
| 623 | - | log.Printf("Loaded %d favorites into cache", loadedCount) | 623 | + | logging.Info(fmt.Sprintf("Loaded %d favorites into cache", loadedCount)) |
| 624 | return nil | 624 | return nil | ||
| 625 | } | 625 | } | ||
| 626 | 626 | ||||
| @@ -676,7 +676,7 @@ func (s *BrowserService) ToggleFavorite(ctx context.Context, url, title, favicon | |||||
| 676 | if err := s.favoritesCache.Delete(url); err != nil { | 676 | if err := s.favoritesCache.Delete(url); err != nil { | ||
| 677 | return false, fmt.Errorf("failed to remove favorite: %w", err) | 677 | return false, fmt.Errorf("failed to remove favorite: %w", err) | ||
| 678 | } | 678 | } | ||
| 679 | - | log.Printf("Removed favorite: %s", url) | 679 | + | logging.Info(fmt.Sprintf("Removed favorite: %s", url)) |
| 680 | return false, nil | 680 | return false, nil | ||
| 681 | } | 681 | } | ||
| 682 | 682 | ||||
| @@ -691,7 +691,7 @@ func (s *BrowserService) ToggleFavorite(ctx context.Context, url, title, favicon | |||||
| 691 | return false, fmt.Errorf("failed to add favorite: %w", err) | 691 | return false, fmt.Errorf("failed to add favorite: %w", err) | ||
| 692 | } | 692 | } | ||
| 693 | 693 | ||||
| 694 | - | log.Printf("Added favorite: %s", url) | 694 | + | logging.Info(fmt.Sprintf("Added favorite: %s", url)) |
| 695 | return true, nil | 695 | return true, nil | ||
| 696 | } | 696 | } | ||
| 697 | 697 | ||||
| @@ -721,7 +721,7 @@ func (s *BrowserService) FlushAllCaches(ctx context.Context) error { | |||||
| 721 | return fmt.Errorf("failed to flush history queue: %w", err) | 721 | return fmt.Errorf("failed to flush history queue: %w", err) | ||
| 722 | } | 722 | } | ||
| 723 | 723 | ||||
| 724 | - | log.Printf("All caches flushed successfully") | 724 | + | logging.Info(fmt.Sprintf("All caches flushed successfully")) |
| 725 | return nil | 725 | return nil | ||
| 726 | } | 726 | } | ||
| 727 | 727 | ||||
M internal/services/favicon_service.go +13 -13
| @@ -6,13 +6,13 @@ import ( | |||||
| 6 | "database/sql" | 6 | "database/sql" | ||
| 7 | "errors" | 7 | "errors" | ||
| 8 | "fmt" | 8 | "fmt" | ||
| 9 | - | "log" | |||
| 10 | "os" | 9 | "os" | ||
| 11 | "path/filepath" | 10 | "path/filepath" | ||
| 12 | "sync" | 11 | "sync" | ||
| 13 | "time" | 12 | "time" | ||
| 14 | 13 | ||||
| 15 | "github.com/bnema/dumber/internal/db" | 14 | "github.com/bnema/dumber/internal/db" | ||
| 15 | + | "github.com/bnema/dumber/internal/logging" | |||
| 16 | "github.com/bnema/dumber/internal/parser" | 16 | "github.com/bnema/dumber/internal/parser" | ||
| 17 | "github.com/bnema/dumber/pkg/webkit" | 17 | "github.com/bnema/dumber/pkg/webkit" | ||
| 18 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 18 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| @@ -83,11 +83,11 @@ func NewFaviconService(faviconDB *webkit.FaviconDatabase, queries db.DatabaseQue | |||||
| 83 | // The FaviconDatabase is shared across all WebViews (at NetworkSession level) | 83 | // The FaviconDatabase is shared across all WebViews (at NetworkSession level) | ||
| 84 | faviconDB.ConnectFaviconChanged(func(pageURI, faviconURI string) { | 84 | faviconDB.ConnectFaviconChanged(func(pageURI, faviconURI string) { | ||
| 85 | if err := fs.OnFaviconChanged(pageURI, faviconURI); err != nil { | 85 | if err := fs.OnFaviconChanged(pageURI, faviconURI); err != nil { | ||
| 86 | - | log.Printf("[favicon] Handler error for %s: %v", pageURI, err) | 86 | + | logging.Error(fmt.Sprintf("[favicon] Handler error for %s: %v", pageURI, err)) |
| 87 | } | 87 | } | ||
| 88 | }) | 88 | }) | ||
| 89 | 89 | ||||
| 90 | - | log.Printf("[favicon] Registered single shared favicon handler at FaviconService level") | 90 | + | logging.Info(fmt.Sprintf("[favicon] Registered single shared favicon handler at FaviconService level")) |
| 91 | 91 | ||||
| 92 | return fs, nil | 92 | return fs, nil | ||
| 93 | } | 93 | } | ||
| @@ -133,14 +133,14 @@ func (fs *FaviconService) OnFaviconChanged(pageURL, faviconURI string) error { | |||||
| 133 | } | 133 | } | ||
| 134 | 134 | ||||
| 135 | // NEW favicon detected (first time or actual change) | 135 | // NEW favicon detected (first time or actual change) | ||
| 136 | - | log.Printf("[favicon] Favicon changed for %s: %s", pageURL, faviconURI) | 136 | + | logging.Info(fmt.Sprintf("[favicon] Favicon changed for %s: %s", pageURL, faviconURI)) |
| 137 | 137 | ||||
| 138 | ctx := context.Background() | 138 | ctx := context.Background() | ||
| 139 | 139 | ||||
| 140 | // Update our history database with the favicon URI | 140 | // Update our history database with the favicon URI | ||
| 141 | nullString := sql.NullString{String: faviconURI, Valid: faviconURI != ""} | 141 | nullString := sql.NullString{String: faviconURI, Valid: faviconURI != ""} | ||
| 142 | if err := fs.dbQueries.UpdateHistoryFavicon(ctx, nullString, canonicalURL); err != nil { | 142 | if err := fs.dbQueries.UpdateHistoryFavicon(ctx, nullString, canonicalURL); err != nil { | ||
| 143 | - | log.Printf("[favicon] Failed to update favicon URI in database for %s: %v", pageURL, err) | 143 | + | logging.Error(fmt.Sprintf("[favicon] Failed to update favicon URI in database for %s: %v", pageURL, err)) |
| 144 | return fmt.Errorf("failed to update favicon in database: %w", err) | 144 | return fmt.Errorf("failed to update favicon in database: %w", err) | ||
| 145 | } | 145 | } | ||
| 146 | 146 | ||||
| @@ -182,7 +182,7 @@ func (fs *FaviconService) propagateFaviconToDomain(pageURL, faviconURI string) { | |||||
| 182 | domainPattern := sql.NullString{String: "%://" + parsedURL.Host + "%", Valid: true} | 182 | domainPattern := sql.NullString{String: "%://" + parsedURL.Host + "%", Valid: true} | ||
| 183 | entries, err := fs.dbQueries.SearchHistory(ctx, domainPattern, sql.NullString{}, 1000) | 183 | entries, err := fs.dbQueries.SearchHistory(ctx, domainPattern, sql.NullString{}, 1000) | ||
| 184 | if err != nil { | 184 | if err != nil { | ||
| 185 | - | log.Printf("[favicon] Failed to search history for domain %s: %v", parsedURL.Host, err) | 185 | + | logging.Error(fmt.Sprintf("[favicon] Failed to search history for domain %s: %v", parsedURL.Host, err)) |
| 186 | return | 186 | return | ||
| 187 | } | 187 | } | ||
| 188 | 188 | ||||
| @@ -205,9 +205,9 @@ func (fs *FaviconService) propagateFaviconToDomain(pageURL, faviconURI string) { | |||||
| 205 | for _, entry := range entries { | 205 | for _, entry := range entries { | ||
| 206 | if entry.FaviconUrl.String == "" { | 206 | if entry.FaviconUrl.String == "" { | ||
| 207 | if err := fs.dbQueries.UpdateHistoryFavicon(ctx, faviconNull, entry.Url); err != nil { | 207 | if err := fs.dbQueries.UpdateHistoryFavicon(ctx, faviconNull, entry.Url); err != nil { | ||
| 208 | - | log.Printf("[favicon] Failed to propagate favicon to %s: %v", entry.Url, err) | 208 | + | logging.Error(fmt.Sprintf("[favicon] Failed to propagate favicon to %s: %v", entry.Url, err)) |
| 209 | } else { | 209 | } else { | ||
| 210 | - | log.Printf("[favicon] Propagated favicon to %s", entry.Url) | 210 | + | logging.Debug(fmt.Sprintf("[favicon] Propagated favicon to %s", entry.Url)) |
| 211 | } | 211 | } | ||
| 212 | } | 212 | } | ||
| 213 | } | 213 | } | ||
| @@ -222,12 +222,12 @@ func (fs *FaviconService) preloadFavicon(pageURL, canonicalURL, faviconURI strin | |||||
| 222 | 222 | ||||
| 223 | texture, err := fs.renderFaviconTexture(ctx, pageURL, faviconURI) | 223 | texture, err := fs.renderFaviconTexture(ctx, pageURL, faviconURI) | ||
| 224 | if err != nil { | 224 | if err != nil { | ||
| 225 | - | log.Printf("[favicon] Preload failed for %s: %v", pageURL, err) | 225 | + | logging.Warn(fmt.Sprintf("[favicon] Preload failed for %s: %v", pageURL, err)) |
| 226 | return | 226 | return | ||
| 227 | } | 227 | } | ||
| 228 | 228 | ||||
| 229 | fs.maybeExportTexture(canonicalURL, texture) | 229 | fs.maybeExportTexture(canonicalURL, texture) | ||
| 230 | - | log.Printf("[favicon] Preloaded and exported favicon for %s", pageURL) | 230 | + | logging.Debug(fmt.Sprintf("[favicon] Preloaded and exported favicon for %s", pageURL)) |
| 231 | } | 231 | } | ||
| 232 | 232 | ||||
| 233 | // GetFaviconTexture loads a favicon texture asynchronously and calls the callback with the result. | 233 | // GetFaviconTexture loads a favicon texture asynchronously and calls the callback with the result. | ||
| @@ -374,7 +374,7 @@ func (fs *FaviconService) lookupFaviconURI(ctx context.Context, canonicalURL, or | |||||
| 374 | continue | 374 | continue | ||
| 375 | } | 375 | } | ||
| 376 | if !errors.Is(err, sql.ErrNoRows) { | 376 | if !errors.Is(err, sql.ErrNoRows) { | ||
| 377 | - | log.Printf("[favicon] failed to query history entry for %s: %v", candidate, err) | 377 | + | logging.Error(fmt.Sprintf("[favicon] failed to query history entry for %s: %v", candidate, err)) |
| 378 | } | 378 | } | ||
| 379 | } | 379 | } | ||
| 380 | 380 | ||||
| @@ -417,7 +417,7 @@ func (fs *FaviconService) maybeExportTexture(pageURL string, texture *gdk.Textur | |||||
| 417 | 417 | ||||
| 418 | _ = webkit.IdleAdd(func() bool { | 418 | _ = webkit.IdleAdd(func() bool { | ||
| 419 | if err := webkit.SaveTextureAsPNG(texture, exportPath, fs.targetSize); err != nil { | 419 | if err := webkit.SaveTextureAsPNG(texture, exportPath, fs.targetSize); err != nil { | ||
| 420 | - | log.Printf("[favicon] Failed to export favicon PNG for %s: %v", pageURL, err) | 420 | + | logging.Error(fmt.Sprintf("[favicon] Failed to export favicon PNG for %s: %v", pageURL, err)) |
| 421 | } | 421 | } | ||
| 422 | return false | 422 | return false | ||
| 423 | }) | 423 | }) | ||
| @@ -473,7 +473,7 @@ func (fs *FaviconService) getExportPath(pageURL string) string { | |||||
| 473 | func (fs *FaviconService) shouldProcessURL(pageURL string) bool { | 473 | func (fs *FaviconService) shouldProcessURL(pageURL string) bool { | ||
| 474 | parsedURL, err := webkit.ParseURL(pageURL) | 474 | parsedURL, err := webkit.ParseURL(pageURL) | ||
| 475 | if err != nil { | 475 | if err != nil { | ||
| 476 | - | log.Printf("[favicon] Invalid page URL: %s", pageURL) | 476 | + | logging.Warn(fmt.Sprintf("[favicon] Invalid page URL: %s", pageURL)) |
| 477 | return false | 477 | return false | ||
| 478 | } | 478 | } | ||
| 479 | 479 | ||||
M main.go +6 -4
| @@ -2,7 +2,7 @@ package main | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "embed" | 4 | "embed" | ||
| 5 | - | "log" | 5 | + | "fmt" |
| 6 | "os" | 6 | "os" | ||
| 7 | "runtime" | 7 | "runtime" | ||
| 8 | 8 | ||||
| @@ -35,7 +35,8 @@ func main() { | |||||
| 35 | 35 | ||||
| 36 | // Load config file first, then initialize logging with actual config | 36 | // Load config file first, then initialize logging with actual config | ||
| 37 | if err := config.Init(); err != nil { | 37 | if err := config.Init(); err != nil { | ||
| 38 | - | log.Printf("Warning: failed to load config: %v, using defaults", err) | 38 | + | // Use fmt.Printf for early errors before logging is initialized |
| 39 | + | fmt.Printf("Warning: failed to load config: %v, using defaults\n", err) | |||
| 39 | } | 40 | } | ||
| 40 | cfg := config.Get() | 41 | cfg := config.Get() | ||
| 41 | 42 | ||||
| @@ -49,10 +50,11 @@ func main() { | |||||
| 49 | cfg.Logging.MaxAge, | 50 | cfg.Logging.MaxAge, | ||
| 50 | cfg.Logging.Compress, | 51 | cfg.Logging.Compress, | ||
| 51 | ); err != nil { | 52 | ); err != nil { | ||
| 52 | - | log.Printf("Warning: failed to initialize logging: %v", err) | 53 | + | // Use fmt.Printf for early errors before logging is initialized |
| 54 | + | fmt.Printf("Warning: failed to initialize logging: %v\n", err) | |||
| 53 | } | 55 | } | ||
| 54 | 56 | ||||
| 55 | - | log.Printf("Starting dumber browser v%s (commit: %s, built: %s)", version, commit, buildDate) | 57 | + | logging.Info(fmt.Sprintf("Starting dumber browser v%s (commit: %s, built: %s)", version, commit, buildDate)) |
| 56 | 58 | ||||
| 57 | // GTK requires all UI calls to run on the main OS thread | 59 | // GTK requires all UI calls to run on the main OS thread | ||
| 58 | if webkit.IsNativeAvailable() { | 60 | if webkit.IsNativeAvailable() { | ||
M pkg/webkit/content_blocking.go +12 -12
| @@ -3,10 +3,10 @@ package webkit | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "path/filepath" | 6 | "path/filepath" | ||
| 8 | "sync" | 7 | "sync" | ||
| 9 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/logging" | |||
| 10 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 10 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 11 | "github.com/diamondburned/gotk4/pkg/gio/v2" | 11 | "github.com/diamondburned/gotk4/pkg/gio/v2" | ||
| 12 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 12 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| @@ -31,7 +31,7 @@ func NewContentBlockingManager(storagePath string) (*ContentBlockingManager, err | |||||
| 31 | return nil, fmt.Errorf("failed to create UserContentFilterStore") | 31 | return nil, fmt.Errorf("failed to create UserContentFilterStore") | ||
| 32 | } | 32 | } | ||
| 33 | 33 | ||||
| 34 | - | log.Printf("[webkit] Created UserContentFilterStore at: %s", storagePath) | 34 | + | logging.Debug(fmt.Sprintf("[webkit] Created UserContentFilterStore at: %s", storagePath)) |
| 35 | 35 | ||||
| 36 | return &ContentBlockingManager{ | 36 | return &ContentBlockingManager{ | ||
| 37 | filterStore: filterStore, | 37 | filterStore: filterStore, | ||
| @@ -49,7 +49,7 @@ func (cbm *ContentBlockingManager) CompileFilters(identifier string, jsonRules [ | |||||
| 49 | return | 49 | return | ||
| 50 | } | 50 | } | ||
| 51 | 51 | ||||
| 52 | - | log.Printf("[webkit] Compiling content filters (identifier: %s, rules size: %d bytes)", identifier, len(jsonRules)) | 52 | + | logging.Debug(fmt.Sprintf("[webkit] Compiling content filters (identifier: %s, rules size: %d bytes)", identifier, len(jsonRules))) |
| 53 | 53 | ||||
| 54 | // Store identifier for later use | 54 | // Store identifier for later use | ||
| 55 | cbm.mu.Lock() | 55 | cbm.mu.Lock() | ||
| @@ -64,7 +64,7 @@ func (cbm *ContentBlockingManager) CompileFilters(identifier string, jsonRules [ | |||||
| 64 | cbm.filterStore.Save(ctx, identifier, gBytes, func(result gio.AsyncResulter) { | 64 | cbm.filterStore.Save(ctx, identifier, gBytes, func(result gio.AsyncResulter) { | ||
| 65 | filter, err := cbm.filterStore.SaveFinish(result) | 65 | filter, err := cbm.filterStore.SaveFinish(result) | ||
| 66 | if err != nil { | 66 | if err != nil { | ||
| 67 | - | log.Printf("[webkit] Failed to compile content filter: %v", err) | 67 | + | logging.Error(fmt.Sprintf("[webkit] Failed to compile content filter: %v", err)) |
| 68 | if onComplete != nil { | 68 | if onComplete != nil { | ||
| 69 | onComplete(fmt.Errorf("failed to compile filter: %w", err)) | 69 | onComplete(fmt.Errorf("failed to compile filter: %w", err)) | ||
| 70 | } | 70 | } | ||
| @@ -72,7 +72,7 @@ func (cbm *ContentBlockingManager) CompileFilters(identifier string, jsonRules [ | |||||
| 72 | } | 72 | } | ||
| 73 | 73 | ||||
| 74 | if filter == nil { | 74 | if filter == nil { | ||
| 75 | - | log.Printf("[webkit] Filter compilation returned nil") | 75 | + | logging.Error(fmt.Sprintf("[webkit] Filter compilation returned nil")) |
| 76 | if onComplete != nil { | 76 | if onComplete != nil { | ||
| 77 | onComplete(fmt.Errorf("filter compilation returned nil")) | 77 | onComplete(fmt.Errorf("filter compilation returned nil")) | ||
| 78 | } | 78 | } | ||
| @@ -84,7 +84,7 @@ func (cbm *ContentBlockingManager) CompileFilters(identifier string, jsonRules [ | |||||
| 84 | cbm.compiledFilter = filter | 84 | cbm.compiledFilter = filter | ||
| 85 | cbm.mu.Unlock() | 85 | cbm.mu.Unlock() | ||
| 86 | 86 | ||||
| 87 | - | log.Printf("[webkit] Filter compilation complete: %s", identifier) | 87 | + | logging.Debug(fmt.Sprintf("[webkit] Filter compilation complete: %s", identifier)) |
| 88 | if onComplete != nil { | 88 | if onComplete != nil { | ||
| 89 | onComplete(nil) | 89 | onComplete(nil) | ||
| 90 | } | 90 | } | ||
| @@ -131,7 +131,7 @@ func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentM | |||||
| 131 | filter := cbm.compiledFilter | 131 | filter := cbm.compiledFilter | ||
| 132 | cbm.mu.RUnlock() | 132 | cbm.mu.RUnlock() | ||
| 133 | ucm.AddFilter(filter) | 133 | ucm.AddFilter(filter) | ||
| 134 | - | log.Printf("[webkit] Applied cached content filter to UCM") | 134 | + | logging.Debug(fmt.Sprintf("[webkit] Applied cached content filter to UCM")) |
| 135 | return nil | 135 | return nil | ||
| 136 | } | 136 | } | ||
| 137 | cbm.mu.RUnlock() | 137 | cbm.mu.RUnlock() | ||
| @@ -140,7 +140,7 @@ func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentM | |||||
| 140 | return fmt.Errorf("empty filter rules") | 140 | return fmt.Errorf("empty filter rules") | ||
| 141 | } | 141 | } | ||
| 142 | 142 | ||||
| 143 | - | log.Printf("[webkit] Applying content filters (identifier: %s, rules size: %d bytes)", identifier, len(jsonRules)) | 143 | + | logging.Debug(fmt.Sprintf("[webkit] Applying content filters (identifier: %s, rules size: %d bytes)", identifier, len(jsonRules))) |
| 144 | 144 | ||||
| 145 | // Convert []byte to *glib.Bytes for WebKit API | 145 | // Convert []byte to *glib.Bytes for WebKit API | ||
| 146 | gBytes := glib.NewBytesWithGo(jsonRules) | 146 | gBytes := glib.NewBytesWithGo(jsonRules) | ||
| @@ -151,13 +151,13 @@ func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentM | |||||
| 151 | cbm.filterStore.Save(ctx, identifier, gBytes, func(result gio.AsyncResulter) { | 151 | cbm.filterStore.Save(ctx, identifier, gBytes, func(result gio.AsyncResulter) { | ||
| 152 | filter, err := cbm.filterStore.SaveFinish(result) | 152 | filter, err := cbm.filterStore.SaveFinish(result) | ||
| 153 | if err != nil { | 153 | if err != nil { | ||
| 154 | - | log.Printf("[webkit] Failed to compile content filter: %v", err) | 154 | + | logging.Error(fmt.Sprintf("[webkit] Failed to compile content filter: %v", err)) |
| 155 | done <- fmt.Errorf("failed to compile filter: %w", err) | 155 | done <- fmt.Errorf("failed to compile filter: %w", err) | ||
| 156 | return | 156 | return | ||
| 157 | } | 157 | } | ||
| 158 | 158 | ||||
| 159 | if filter == nil { | 159 | if filter == nil { | ||
| 160 | - | log.Printf("[webkit] Filter compilation returned nil") | 160 | + | logging.Error(fmt.Sprintf("[webkit] Filter compilation returned nil")) |
| 161 | done <- fmt.Errorf("filter compilation returned nil") | 161 | done <- fmt.Errorf("filter compilation returned nil") | ||
| 162 | return | 162 | return | ||
| 163 | } | 163 | } | ||
| @@ -170,7 +170,7 @@ func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentM | |||||
| 170 | 170 | ||||
| 171 | RunOnMainThread(func() { | 171 | RunOnMainThread(func() { | ||
| 172 | ucm.AddFilter(filter) | 172 | ucm.AddFilter(filter) | ||
| 173 | - | log.Printf("[webkit] Successfully added content filter: %s", identifier) | 173 | + | logging.Debug(fmt.Sprintf("[webkit] Successfully added content filter: %s", identifier)) |
| 174 | done <- nil | 174 | done <- nil | ||
| 175 | }) | 175 | }) | ||
| 176 | }) | 176 | }) | ||
| @@ -182,7 +182,7 @@ func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentM | |||||
| 182 | func (cbm *ContentBlockingManager) RemoveAllFilters(ucm *webkit.UserContentManager) { | 182 | func (cbm *ContentBlockingManager) RemoveAllFilters(ucm *webkit.UserContentManager) { | ||
| 183 | if ucm != nil { | 183 | if ucm != nil { | ||
| 184 | ucm.RemoveAllFilters() | 184 | ucm.RemoveAllFilters() | ||
| 185 | - | log.Printf("[webkit] Removed all content filters") | 185 | + | logging.Debug(fmt.Sprintf("[webkit] Removed all content filters")) |
| 186 | } | 186 | } | ||
| 187 | } | 187 | } | ||
| 188 | 188 | ||||
M pkg/webkit/keyboard_bridge.go +10 -10
| @@ -3,8 +3,8 @@ package webkit | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 8 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 8 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 9 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 9 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 10 | ) | 10 | ) | ||
| @@ -221,7 +221,7 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 221 | handler := w.onPaneModeShortcut | 221 | handler := w.onPaneModeShortcut | ||
| 222 | w.mu.RUnlock() | 222 | w.mu.RUnlock() | ||
| 223 | if handler != nil && handler(actionEnter) { | 223 | if handler != nil && handler(actionEnter) { | ||
| 224 | - | log.Printf("[keyboard-bridge] Pane mode: enter") | 224 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Pane mode: enter")) |
| 225 | return true | 225 | return true | ||
| 226 | } | 226 | } | ||
| 227 | return true // Block even if no handler | 227 | return true // Block even if no handler | ||
| @@ -233,7 +233,7 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 233 | handler := w.onPaneModeShortcut | 233 | handler := w.onPaneModeShortcut | ||
| 234 | w.mu.RUnlock() | 234 | w.mu.RUnlock() | ||
| 235 | if handler != nil && handler(actionExit) { | 235 | if handler != nil && handler(actionExit) { | ||
| 236 | - | log.Printf("[keyboard-bridge] Escape: exited mode") | 236 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Escape: exited mode")) |
| 237 | return true | 237 | return true | ||
| 238 | } | 238 | } | ||
| 239 | return false // Pass through to DOM if no active mode | 239 | return false // Pass through to DOM if no active mode | ||
| @@ -247,7 +247,7 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 247 | handler := w.onWorkspaceNavigation | 247 | handler := w.onWorkspaceNavigation | ||
| 248 | w.mu.RUnlock() | 248 | w.mu.RUnlock() | ||
| 249 | if handler != nil && handler(action) { | 249 | if handler != nil && handler(action) { | ||
| 250 | - | log.Printf("[keyboard-bridge] Navigation: %s", action) | 250 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Navigation: %s", action)) |
| 251 | return true | 251 | return true | ||
| 252 | } | 252 | } | ||
| 253 | return true // Block anyway to prevent scrolling | 253 | return true // Block anyway to prevent scrolling | ||
| @@ -258,7 +258,7 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 258 | handler := w.onPaneModeShortcut | 258 | handler := w.onPaneModeShortcut | ||
| 259 | w.mu.RUnlock() | 259 | w.mu.RUnlock() | ||
| 260 | if handler != nil && handler(action) { | 260 | if handler != nil && handler(action) { | ||
| 261 | - | log.Printf("[keyboard-bridge] Pane mode action: %s", action) | 261 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Pane mode action: %s", action)) |
| 262 | return true | 262 | return true | ||
| 263 | } | 263 | } | ||
| 264 | return false // Not in pane mode, pass through | 264 | return false // Not in pane mode, pass through | ||
| @@ -266,26 +266,26 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 266 | 266 | ||||
| 267 | // Pane mode active: block all other shortcuts | 267 | // Pane mode active: block all other shortcuts | ||
| 268 | if isPaneModeActive { | 268 | if isPaneModeActive { | ||
| 269 | - | log.Printf("[keyboard-bridge] Blocking '%s' during pane mode", shortcut) | 269 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Blocking '%s' during pane mode", shortcut)) |
| 270 | return true | 270 | return true | ||
| 271 | } | 271 | } | ||
| 272 | 272 | ||||
| 273 | // Forward to JavaScript using proper JSON marshaling to prevent any potential injection | 273 | // Forward to JavaScript using proper JSON marshaling to prevent any potential injection | ||
| 274 | shortcutJSON, err := json.Marshal(shortcut) | 274 | shortcutJSON, err := json.Marshal(shortcut) | ||
| 275 | if err != nil { | 275 | if err != nil { | ||
| 276 | - | log.Printf("[keyboard-bridge] Failed to marshal shortcut: %v", err) | 276 | + | logging.Error(fmt.Sprintf("[keyboard-bridge] Failed to marshal shortcut: %v", err)) |
| 277 | return false | 277 | return false | ||
| 278 | } | 278 | } | ||
| 279 | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:%s}}));`, shortcutJSON) | 279 | script := fmt.Sprintf(`document.dispatchEvent(new CustomEvent('dumber:key',{detail:{shortcut:%s}}));`, shortcutJSON) | ||
| 280 | if err := w.InjectScript(script); err != nil { | 280 | if err := w.InjectScript(script); err != nil { | ||
| 281 | - | log.Printf("[keyboard-bridge] Failed to dispatch: %v", err) | 281 | + | logging.Error(fmt.Sprintf("[keyboard-bridge] Failed to dispatch: %v", err)) |
| 282 | } | 282 | } | ||
| 283 | return false // Allow WebKit to handle | 283 | return false // Allow WebKit to handle | ||
| 284 | } | 284 | } | ||
| 285 | 285 | ||||
| 286 | // Pane mode active: block ALL non-shortcut keys | 286 | // Pane mode active: block ALL non-shortcut keys | ||
| 287 | if isPaneModeActive { | 287 | if isPaneModeActive { | ||
| 288 | - | log.Printf("[keyboard-bridge] Blocking key during pane mode: keyval=%d", keyval) | 288 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] Blocking key during pane mode: keyval=%d", keyval)) |
| 289 | return true // Block all other keys | 289 | return true // Block all other keys | ||
| 290 | } | 290 | } | ||
| 291 | 291 | ||||
| @@ -295,5 +295,5 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 295 | 295 | ||||
| 296 | // Attach controller to WebView | 296 | // Attach controller to WebView | ||
| 297 | w.view.AddController(keyController) | 297 | w.view.AddController(keyController) | ||
| 298 | - | log.Printf("[keyboard-bridge] EventControllerKey attached to WebView ID %d", w.id) | 298 | + | logging.Debug(fmt.Sprintf("[keyboard-bridge] EventControllerKey attached to WebView ID %d", w.id)) |
| 299 | } | 299 | } | ||
M pkg/webkit/mouse_gestures.go +8 -7
| @@ -1,8 +1,9 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 7 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| @@ -17,7 +18,7 @@ const ( | |||||
| 17 | // This enables hardware mouse button navigation (back/forward buttons) | 18 | // This enables hardware mouse button navigation (back/forward buttons) | ||
| 18 | func (w *WebView) AttachMouseGestures() { | 19 | func (w *WebView) AttachMouseGestures() { | ||
| 19 | if w.view == nil { | 20 | if w.view == nil { | ||
| 20 | - | log.Printf("[mouse-gestures] Cannot attach: WebView is nil") | 21 | + | logging.Error(fmt.Sprintf("[mouse-gestures] Cannot attach: WebView is nil")) |
| 21 | return | 22 | return | ||
| 22 | } | 23 | } | ||
| 23 | 24 | ||||
| @@ -43,18 +44,18 @@ func (w *WebView) AttachMouseGestures() { | |||||
| 43 | switch button { | 44 | switch button { | ||
| 44 | case mouseButtonBack: | 45 | case mouseButtonBack: | ||
| 45 | if w.view.CanGoBack() { | 46 | if w.view.CanGoBack() { | ||
| 46 | - | log.Printf("[mouse-gestures] Back button clicked - navigating backward") | 47 | + | logging.Debug(fmt.Sprintf("[mouse-gestures] Back button clicked - navigating backward")) |
| 47 | w.view.GoBack() | 48 | w.view.GoBack() | ||
| 48 | } else { | 49 | } else { | ||
| 49 | - | log.Printf("[mouse-gestures] Back button clicked but cannot go back") | 50 | + | logging.Debug(fmt.Sprintf("[mouse-gestures] Back button clicked but cannot go back")) |
| 50 | } | 51 | } | ||
| 51 | 52 | ||||
| 52 | case mouseButtonForward: | 53 | case mouseButtonForward: | ||
| 53 | if w.view.CanGoForward() { | 54 | if w.view.CanGoForward() { | ||
| 54 | - | log.Printf("[mouse-gestures] Forward button clicked - navigating forward") | 55 | + | logging.Debug(fmt.Sprintf("[mouse-gestures] Forward button clicked - navigating forward")) |
| 55 | w.view.GoForward() | 56 | w.view.GoForward() | ||
| 56 | } else { | 57 | } else { | ||
| 57 | - | log.Printf("[mouse-gestures] Forward button clicked but cannot go forward") | 58 | + | logging.Debug(fmt.Sprintf("[mouse-gestures] Forward button clicked but cannot go forward")) |
| 58 | } | 59 | } | ||
| 59 | } | 60 | } | ||
| 60 | }) | 61 | }) | ||
| @@ -62,5 +63,5 @@ func (w *WebView) AttachMouseGestures() { | |||||
| 62 | // Attach controller to WebView | 63 | // Attach controller to WebView | ||
| 63 | w.view.AddController(gestureClick) | 64 | w.view.AddController(gestureClick) | ||
| 64 | 65 | ||||
| 65 | - | log.Printf("[mouse-gestures] Mouse gesture controller attached to WebView ID %d", w.id) | 66 | + | logging.Debug(fmt.Sprintf("[mouse-gestures] Mouse gesture controller attached to WebView ID %d", w.id)) |
| 66 | } | 67 | } | ||
M pkg/webkit/network_session.go +7 -7
| @@ -2,8 +2,8 @@ package webkit | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "log" | |||
| 6 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 7 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| @@ -25,7 +25,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 25 | // If session already exists, return success (idempotent) | 25 | // If session already exists, return success (idempotent) | ||
| 26 | // This ensures we only create ONE session for the entire application | 26 | // This ensures we only create ONE session for the entire application | ||
| 27 | if globalNetworkSession != nil { | 27 | if globalNetworkSession != nil { | ||
| 28 | - | log.Printf("[webkit] Using existing persistent network session") | 28 | + | logging.Debug(fmt.Sprintf("[webkit] Using existing persistent network session")) |
| 29 | return nil | 29 | return nil | ||
| 30 | } | 30 | } | ||
| 31 | 31 | ||||
| @@ -40,7 +40,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 40 | // This is CRITICAL - if the session is GC'd, WebKit falls back to ephemeral storage | 40 | // This is CRITICAL - if the session is GC'd, WebKit falls back to ephemeral storage | ||
| 41 | globalNetworkSession = session | 41 | globalNetworkSession = session | ||
| 42 | 42 | ||||
| 43 | - | log.Printf("[webkit] Persistent network session created: data=%s, cache=%s", dataDir, cacheDir) | 43 | + | logging.Debug(fmt.Sprintf("[webkit] Persistent network session created: data=%s, cache=%s", dataDir, cacheDir)) |
| 44 | 44 | ||||
| 45 | // Verify the session is not ephemeral | 45 | // Verify the session is not ephemeral | ||
| 46 | if session.IsEphemeral() { | 46 | if session.IsEphemeral() { | ||
| @@ -55,7 +55,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 55 | if websiteDataManager.IsEphemeral() { | 55 | if websiteDataManager.IsEphemeral() { | ||
| 56 | return fmt.Errorf("website data manager is ephemeral despite providing data directories") | 56 | return fmt.Errorf("website data manager is ephemeral despite providing data directories") | ||
| 57 | } | 57 | } | ||
| 58 | - | log.Printf("[webkit] WebsiteDataManager verified as non-ephemeral") | 58 | + | logging.Debug(fmt.Sprintf("[webkit] WebsiteDataManager verified as non-ephemeral")) |
| 59 | 59 | ||||
| 60 | // Configure CookieManager for persistent cookie storage | 60 | // Configure CookieManager for persistent cookie storage | ||
| 61 | // This is REQUIRED - without this, cookies are not persisted to disk | 61 | // This is REQUIRED - without this, cookies are not persisted to disk | ||
| @@ -72,7 +72,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 72 | // Set cookie accept policy to no third-party (default) | 72 | // Set cookie accept policy to no third-party (default) | ||
| 73 | cookieManager.SetAcceptPolicy(webkit.CookiePolicyAcceptNoThirdParty) | 73 | cookieManager.SetAcceptPolicy(webkit.CookiePolicyAcceptNoThirdParty) | ||
| 74 | 74 | ||||
| 75 | - | log.Printf("[webkit] Cookie manager configured: storage=%s, policy=no-third-party", cookiePath) | 75 | + | logging.Debug(fmt.Sprintf("[webkit] Cookie manager configured: storage=%s, policy=no-third-party", cookiePath)) |
| 76 | 76 | ||||
| 77 | // Enable persistent credential storage (HTTP auth, etc.) | 77 | // Enable persistent credential storage (HTTP auth, etc.) | ||
| 78 | session.SetPersistentCredentialStorageEnabled(true) | 78 | session.SetPersistentCredentialStorageEnabled(true) | ||
| @@ -80,7 +80,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 80 | // Configure TLS errors to emit signals instead of failing silently | 80 | // Configure TLS errors to emit signals instead of failing silently | ||
| 81 | // This enables the load-failed-with-tls-errors signal | 81 | // This enables the load-failed-with-tls-errors signal | ||
| 82 | session.SetTLSErrorsPolicy(webkit.TLSErrorsPolicyFail) | 82 | session.SetTLSErrorsPolicy(webkit.TLSErrorsPolicyFail) | ||
| 83 | - | log.Printf("[webkit] TLS errors policy set to FAIL (signals enabled)") | 83 | + | logging.Debug(fmt.Sprintf("[webkit] TLS errors policy set to FAIL (signals enabled)")) |
| 84 | 84 | ||||
| 85 | // Verify this is indeed the default session now | 85 | // Verify this is indeed the default session now | ||
| 86 | defaultSession := webkit.NetworkSessionGetDefault() | 86 | defaultSession := webkit.NetworkSessionGetDefault() | ||
| @@ -93,7 +93,7 @@ func InitPersistentSession(dataDir, cacheDir string) error { | |||||
| 93 | return fmt.Errorf("default network session is ephemeral after creating persistent session") | 93 | return fmt.Errorf("default network session is ephemeral after creating persistent session") | ||
| 94 | } | 94 | } | ||
| 95 | 95 | ||||
| 96 | - | log.Printf("[webkit] Network session verified as persistent and set as default") | 96 | + | logging.Debug(fmt.Sprintf("[webkit] Network session verified as persistent and set as default")) |
| 97 | return nil | 97 | return nil | ||
| 98 | } | 98 | } | ||
| 99 | 99 | ||||
M pkg/webkit/shortcuts.go +6 -5
| @@ -1,9 +1,10 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "strings" | 5 | "strings" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 8 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 8 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 9 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| 9 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 10 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| @@ -62,7 +63,7 @@ func (ws *WindowShortcuts) RegisterShortcut(accelerator string, handler func()) | |||||
| 62 | // Convert to GTK format (e.g., "ctrl+plus" -> "<Control>plus") | 63 | // Convert to GTK format (e.g., "ctrl+plus" -> "<Control>plus") | ||
| 63 | gtkFormat := convertToGtkFormat(accelerator) | 64 | gtkFormat := convertToGtkFormat(accelerator) | ||
| 64 | if gtkFormat == "" { | 65 | if gtkFormat == "" { | ||
| 65 | - | log.Printf("[shortcuts] Invalid key format: %s", accelerator) | 66 | + | logging.Warn(fmt.Sprintf("[shortcuts] Invalid key format: %s", accelerator)) |
| 66 | return | 67 | return | ||
| 67 | } | 68 | } | ||
| 68 | 69 | ||||
| @@ -70,7 +71,7 @@ func (ws *WindowShortcuts) RegisterShortcut(accelerator string, handler func()) | |||||
| 70 | // ShortcutFunc signature: func(widget Widgetter, args *glib.Variant) (ok bool) | 71 | // ShortcutFunc signature: func(widget Widgetter, args *glib.Variant) (ok bool) | ||
| 71 | action := gtk.NewCallbackAction(func(widget gtk.Widgetter, args *glib.Variant) bool { | 72 | action := gtk.NewCallbackAction(func(widget gtk.Widgetter, args *glib.Variant) bool { | ||
| 72 | if handler != nil { | 73 | if handler != nil { | ||
| 73 | - | log.Printf("[shortcuts] Shortcut triggered: %s", accelerator) | 74 | + | logging.Debug(fmt.Sprintf("[shortcuts] Shortcut triggered: %s", accelerator)) |
| 74 | handler() | 75 | handler() | ||
| 75 | } | 76 | } | ||
| 76 | // Return false to allow event propagation to WebView | 77 | // Return false to allow event propagation to WebView | ||
| @@ -82,7 +83,7 @@ func (ws *WindowShortcuts) RegisterShortcut(accelerator string, handler func()) | |||||
| 82 | // GTK4 uses gtk.ShortcutTrigger for key combinations | 83 | // GTK4 uses gtk.ShortcutTrigger for key combinations | ||
| 83 | trigger := gtk.NewShortcutTriggerParseString(gtkFormat) | 84 | trigger := gtk.NewShortcutTriggerParseString(gtkFormat) | ||
| 84 | if trigger == nil { | 85 | if trigger == nil { | ||
| 85 | - | log.Printf("[shortcuts] Failed to parse trigger: %s", gtkFormat) | 86 | + | logging.Error(fmt.Sprintf("[shortcuts] Failed to parse trigger: %s", gtkFormat)) |
| 86 | return | 87 | return | ||
| 87 | } | 88 | } | ||
| 88 | 89 | ||||
| @@ -95,7 +96,7 @@ func (ws *WindowShortcuts) RegisterShortcut(accelerator string, handler func()) | |||||
| 95 | // Store the shortcut for potential cleanup | 96 | // Store the shortcut for potential cleanup | ||
| 96 | ws.shortcuts[accelerator] = shortcut | 97 | ws.shortcuts[accelerator] = shortcut | ||
| 97 | 98 | ||||
| 98 | - | log.Printf("[shortcuts] Registered: %s -> %s", accelerator, gtkFormat) | 99 | + | logging.Debug(fmt.Sprintf("[shortcuts] Registered: %s -> %s", accelerator, gtkFormat)) |
| 99 | } | 100 | } | ||
| 100 | 101 | ||||
| 101 | // convertToGtkFormat converts common key formats to GTK shortcut format | 102 | // convertToGtkFormat converts common key formats to GTK shortcut format | ||
M pkg/webkit/tls_handler.go +29 -29
| @@ -5,13 +5,13 @@ import ( | |||||
| 5 | "database/sql" | 5 | "database/sql" | ||
| 6 | "errors" | 6 | "errors" | ||
| 7 | "fmt" | 7 | "fmt" | ||
| 8 | - | "log" | |||
| 9 | "net/url" | 8 | "net/url" | ||
| 10 | "strings" | 9 | "strings" | ||
| 11 | "time" | 10 | "time" | ||
| 12 | 11 | ||||
| 13 | "github.com/bnema/dumber/internal/config" | 12 | "github.com/bnema/dumber/internal/config" | ||
| 14 | "github.com/bnema/dumber/internal/db" | 13 | "github.com/bnema/dumber/internal/db" | ||
| 14 | + | "github.com/bnema/dumber/internal/logging" | |||
| 15 | "github.com/diamondburned/gotk4/pkg/gio/v2" | 15 | "github.com/diamondburned/gotk4/pkg/gio/v2" | ||
| 16 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 16 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 17 | ) | 17 | ) | ||
| @@ -29,12 +29,12 @@ type TLSCertificateInfo struct { | |||||
| 29 | // setupTLSErrorHandler sets up the TLS error handler for the WebView | 29 | // setupTLSErrorHandler sets up the TLS error handler for the WebView | ||
| 30 | func (wv *WebView) setupTLSErrorHandler() { | 30 | func (wv *WebView) setupTLSErrorHandler() { | ||
| 31 | wv.view.ConnectLoadFailedWithTLSErrors(func(failingUri string, certificate gio.TLSCertificater, errors gio.TLSCertificateFlags) bool { | 31 | wv.view.ConnectLoadFailedWithTLSErrors(func(failingUri string, certificate gio.TLSCertificater, errors gio.TLSCertificateFlags) bool { | ||
| 32 | - | log.Printf("[tls] Load failed with TLS errors for: %s, errors: %v", failingUri, errors) | 32 | + | logging.Warn(fmt.Sprintf("[tls] Load failed with TLS errors for: %s, errors: %v", failingUri, errors)) |
| 33 | 33 | ||||
| 34 | // Extract hostname from URI | 34 | // Extract hostname from URI | ||
| 35 | hostname := extractHostname(failingUri) | 35 | hostname := extractHostname(failingUri) | ||
| 36 | if hostname == "" { | 36 | if hostname == "" { | ||
| 37 | - | log.Printf("[tls] Failed to extract hostname from URI: %s", failingUri) | 37 | + | logging.Error(fmt.Sprintf("[tls] Failed to extract hostname from URI: %s", failingUri)) |
| 38 | return false // Let WebKit handle it | 38 | return false // Let WebKit handle it | ||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| @@ -44,17 +44,17 @@ func (wv *WebView) setupTLSErrorHandler() { | |||||
| 44 | decision, exists := checkStoredCertificateDecision(hostname) | 44 | decision, exists := checkStoredCertificateDecision(hostname) | ||
| 45 | if exists { | 45 | if exists { | ||
| 46 | if decision == "accepted" { | 46 | if decision == "accepted" { | ||
| 47 | - | log.Printf("[tls] Certificate previously accepted for %s, allowing", hostname) | 47 | + | logging.Debug(fmt.Sprintf("[tls] Certificate previously accepted for %s, allowing", hostname)) |
| 48 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | 48 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | ||
| 49 | return true // Signal handled | 49 | return true // Signal handled | ||
| 50 | } else { | 50 | } else { | ||
| 51 | - | log.Printf("[tls] Certificate previously rejected for %s, blocking", hostname) | 51 | + | logging.Debug(fmt.Sprintf("[tls] Certificate previously rejected for %s, blocking", hostname)) |
| 52 | return false // Let WebKit show error | 52 | return false // Let WebKit show error | ||
| 53 | } | 53 | } | ||
| 54 | } | 54 | } | ||
| 55 | 55 | ||||
| 56 | // No stored decision - show dialog to user | 56 | // No stored decision - show dialog to user | ||
| 57 | - | log.Printf("[tls] No stored decision for %s, showing dialog", hostname) | 57 | + | logging.Debug(fmt.Sprintf("[tls] No stored decision for %s, showing dialog", hostname)) |
| 58 | wv.showTLSErrorDialog(failingUri, hostname, certificate, errors) | 58 | wv.showTLSErrorDialog(failingUri, hostname, certificate, errors) | ||
| 59 | 59 | ||||
| 60 | return true // Signal handled - we're managing the error | 60 | return true // Signal handled - we're managing the error | ||
| @@ -87,24 +87,24 @@ func extractHostname(uri string) string { | |||||
| 87 | func checkStoredCertificateDecision(hostname string) (string, bool) { | 87 | func checkStoredCertificateDecision(hostname string) (string, bool) { | ||
| 88 | cfg := config.Get() | 88 | cfg := config.Get() | ||
| 89 | if cfg == nil { | 89 | if cfg == nil { | ||
| 90 | - | log.Printf("[tls] Config not available, cannot check stored decisions") | 90 | + | logging.Error(fmt.Sprintf("[tls] Config not available, cannot check stored decisions")) |
| 91 | return "", false | 91 | return "", false | ||
| 92 | } | 92 | } | ||
| 93 | 93 | ||||
| 94 | dbPath := cfg.Database.Path | 94 | dbPath := cfg.Database.Path | ||
| 95 | if dbPath == "" { | 95 | if dbPath == "" { | ||
| 96 | - | log.Printf("[tls] Database path not configured") | 96 | + | logging.Error(fmt.Sprintf("[tls] Database path not configured")) |
| 97 | return "", false | 97 | return "", false | ||
| 98 | } | 98 | } | ||
| 99 | 99 | ||||
| 100 | database, err := sql.Open("sqlite3", dbPath) | 100 | database, err := sql.Open("sqlite3", dbPath) | ||
| 101 | if err != nil { | 101 | if err != nil { | ||
| 102 | - | log.Printf("[tls] Failed to open database: %v", err) | 102 | + | logging.Error(fmt.Sprintf("[tls] Failed to open database: %v", err)) |
| 103 | return "", false | 103 | return "", false | ||
| 104 | } | 104 | } | ||
| 105 | defer func() { | 105 | defer func() { | ||
| 106 | if err := database.Close(); err != nil { | 106 | if err := database.Close(); err != nil { | ||
| 107 | - | log.Printf("[tls] warning: failed to close database: %v", err) | 107 | + | logging.Warn(fmt.Sprintf("[tls] warning: failed to close database: %v", err)) |
| 108 | } | 108 | } | ||
| 109 | }() | 109 | }() | ||
| 110 | 110 | ||||
| @@ -117,7 +117,7 @@ func checkStoredCertificateDecision(hostname string) (string, bool) { | |||||
| 117 | if errors.Is(err, sql.ErrNoRows) { | 117 | if errors.Is(err, sql.ErrNoRows) { | ||
| 118 | return "", false | 118 | return "", false | ||
| 119 | } | 119 | } | ||
| 120 | - | log.Printf("[tls] Error checking certificate validation: %v", err) | 120 | + | logging.Error(fmt.Sprintf("[tls] Error checking certificate validation: %v", err)) |
| 121 | return "", false | 121 | return "", false | ||
| 122 | } | 122 | } | ||
| 123 | 123 | ||||
| @@ -142,23 +142,23 @@ func storeCertificateDecision(hostname, decision string, expiresAt sql.NullTime) | |||||
| 142 | } | 142 | } | ||
| 143 | defer func() { | 143 | defer func() { | ||
| 144 | if err := database.Close(); err != nil { | 144 | if err := database.Close(); err != nil { | ||
| 145 | - | log.Printf("[tls] warning: failed to close database: %v", err) | 145 | + | logging.Warn(fmt.Sprintf("[tls] warning: failed to close database: %v", err)) |
| 146 | } | 146 | } | ||
| 147 | }() | 147 | }() | ||
| 148 | 148 | ||||
| 149 | queries := db.New(database) | 149 | queries := db.New(database) | ||
| 150 | ctx := context.Background() | 150 | ctx := context.Background() | ||
| 151 | 151 | ||||
| 152 | - | log.Printf("[tls] Saving certificate decision to database: hostname=%s, decision=%s, expiresAt=%v", | 152 | + | logging.Debug(fmt.Sprintf("[tls] Saving certificate decision to database: hostname=%s, decision=%s, expiresAt=%v", |
| 153 | - | hostname, decision, expiresAt) | 153 | + | hostname, decision, expiresAt)) |
| 154 | 154 | ||||
| 155 | // Use empty string for cert hash since we're doing hostname-only matching | 155 | // Use empty string for cert hash since we're doing hostname-only matching | ||
| 156 | if err := queries.StoreCertificateValidation(ctx, hostname, "", decision, expiresAt); err != nil { | 156 | if err := queries.StoreCertificateValidation(ctx, hostname, "", decision, expiresAt); err != nil { | ||
| 157 | - | log.Printf("[tls] Failed to save certificate decision to database: %v", err) | 157 | + | logging.Error(fmt.Sprintf("[tls] Failed to save certificate decision to database: %v", err)) |
| 158 | return err | 158 | return err | ||
| 159 | } | 159 | } | ||
| 160 | 160 | ||||
| 161 | - | log.Printf("[tls] Certificate decision saved successfully in database") | 161 | + | logging.Debug(fmt.Sprintf("[tls] Certificate decision saved successfully in database")) |
| 162 | return nil | 162 | return nil | ||
| 163 | } | 163 | } | ||
| 164 | 164 | ||||
| @@ -222,7 +222,7 @@ func (wv *WebView) showTLSErrorDialog(failingUri, hostname string, certificate g | |||||
| 222 | // Create AlertDialog using CGO wrapper | 222 | // Create AlertDialog using CGO wrapper | ||
| 223 | dialog := newAlertDialog(fmt.Sprintf("Certificate Error for %s", hostname)) | 223 | dialog := newAlertDialog(fmt.Sprintf("Certificate Error for %s", hostname)) | ||
| 224 | if dialog == nil { | 224 | if dialog == nil { | ||
| 225 | - | log.Printf("[tls] Failed to create alert dialog") | 225 | + | logging.Error(fmt.Sprintf("[tls] Failed to create alert dialog")) |
| 226 | return | 226 | return | ||
| 227 | } | 227 | } | ||
| 228 | 228 | ||||
| @@ -245,32 +245,32 @@ func (wv *WebView) showTLSErrorDialog(failingUri, hostname string, certificate g | |||||
| 245 | dialog.Choose(context.Background(), parentWindow, func(result gio.AsyncResulter) { | 245 | dialog.Choose(context.Background(), parentWindow, func(result gio.AsyncResulter) { | ||
| 246 | buttonIndex, err := dialog.ChooseFinish(result) | 246 | buttonIndex, err := dialog.ChooseFinish(result) | ||
| 247 | if err != nil { | 247 | if err != nil { | ||
| 248 | - | log.Printf("[tls] Dialog error: %v", err) | 248 | + | logging.Error(fmt.Sprintf("[tls] Dialog error: %v", err)) |
| 249 | return | 249 | return | ||
| 250 | } | 250 | } | ||
| 251 | 251 | ||||
| 252 | - | log.Printf("[tls] User chose button %d for %s", buttonIndex, hostname) | 252 | + | logging.Debug(fmt.Sprintf("[tls] User chose button %d for %s", buttonIndex, hostname)) |
| 253 | 253 | ||||
| 254 | switch buttonIndex { | 254 | switch buttonIndex { | ||
| 255 | case 0: // Go Back | 255 | case 0: // Go Back | ||
| 256 | - | log.Printf("[tls] User chose to go back") | 256 | + | logging.Debug(fmt.Sprintf("[tls] User chose to go back")) |
| 257 | // Do nothing - let the load fail | 257 | // Do nothing - let the load fail | ||
| 258 | 258 | ||||
| 259 | case 1: // Proceed Once | 259 | case 1: // Proceed Once | ||
| 260 | - | log.Printf("[tls] User chose to proceed once") | 260 | + | logging.Debug(fmt.Sprintf("[tls] User chose to proceed once")) |
| 261 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | 261 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | ||
| 262 | 262 | ||||
| 263 | case 2: // Always Accept This Site | 263 | case 2: // Always Accept This Site | ||
| 264 | - | log.Printf("[tls] User chose to always accept for %s", hostname) | 264 | + | logging.Debug(fmt.Sprintf("[tls] User chose to always accept for %s", hostname)) |
| 265 | // Store decision in database (expires in 30 days) | 265 | // Store decision in database (expires in 30 days) | ||
| 266 | expiresAt := sql.NullTime{ | 266 | expiresAt := sql.NullTime{ | ||
| 267 | Time: time.Now().Add(30 * 24 * time.Hour), | 267 | Time: time.Now().Add(30 * 24 * time.Hour), | ||
| 268 | Valid: true, | 268 | Valid: true, | ||
| 269 | } | 269 | } | ||
| 270 | if err := storeCertificateDecision(hostname, "accepted", expiresAt); err != nil { | 270 | if err := storeCertificateDecision(hostname, "accepted", expiresAt); err != nil { | ||
| 271 | - | log.Printf("[tls] Failed to store certificate decision: %v", err) | 271 | + | logging.Error(fmt.Sprintf("[tls] Failed to store certificate decision: %v", err)) |
| 272 | } else { | 272 | } else { | ||
| 273 | - | log.Printf("[tls] Stored certificate acceptance for %s (expires in 30 days)", hostname) | 273 | + | logging.Debug(fmt.Sprintf("[tls] Stored certificate acceptance for %s (expires in 30 days)", hostname)) |
| 274 | } | 274 | } | ||
| 275 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | 275 | wv.allowCertificateAndReload(certificate, hostname, failingUri) | ||
| 276 | } | 276 | } | ||
| @@ -284,17 +284,17 @@ func (wv *WebView) allowCertificateAndReload(certificate gio.TLSCertificater, ho | |||||
| 284 | // Get the network session | 284 | // Get the network session | ||
| 285 | session := wv.view.NetworkSession() | 285 | session := wv.view.NetworkSession() | ||
| 286 | if session == nil { | 286 | if session == nil { | ||
| 287 | - | log.Printf("[tls] Failed to get network session") | 287 | + | logging.Error(fmt.Sprintf("[tls] Failed to get network session")) |
| 288 | return | 288 | return | ||
| 289 | } | 289 | } | ||
| 290 | 290 | ||||
| 291 | // Allow the certificate for this host | 291 | // Allow the certificate for this host | ||
| 292 | session.AllowTLSCertificateForHost(certificate, hostname) | 292 | session.AllowTLSCertificateForHost(certificate, hostname) | ||
| 293 | - | log.Printf("[tls] Certificate exception added for host: %s", hostname) | 293 | + | logging.Debug(fmt.Sprintf("[tls] Certificate exception added for host: %s", hostname)) |
| 294 | 294 | ||||
| 295 | // Reload the page | 295 | // Reload the page | ||
| 296 | wv.view.LoadURI(uri) | 296 | wv.view.LoadURI(uri) | ||
| 297 | - | log.Printf("[tls] Reloading %s with certificate exception", uri) | 297 | + | logging.Debug(fmt.Sprintf("[tls] Reloading %s with certificate exception", uri)) |
| 298 | }) | 298 | }) | ||
| 299 | } | 299 | } | ||
| 300 | 300 | ||||
| @@ -317,7 +317,7 @@ func CleanupExpiredCertificateValidations() error { | |||||
| 317 | } | 317 | } | ||
| 318 | defer func() { | 318 | defer func() { | ||
| 319 | if err := database.Close(); err != nil { | 319 | if err := database.Close(); err != nil { | ||
| 320 | - | log.Printf("[tls] warning: failed to close database: %v", err) | 320 | + | logging.Warn(fmt.Sprintf("[tls] warning: failed to close database: %v", err)) |
| 321 | } | 321 | } | ||
| 322 | }() | 322 | }() | ||
| 323 | 323 | ||||
| @@ -328,6 +328,6 @@ func CleanupExpiredCertificateValidations() error { | |||||
| 328 | return fmt.Errorf("failed to delete expired validations: %w", err) | 328 | return fmt.Errorf("failed to delete expired validations: %w", err) | ||
| 329 | } | 329 | } | ||
| 330 | 330 | ||||
| 331 | - | log.Printf("[tls] Cleaned up expired certificate validations") | 331 | + | logging.Debug(fmt.Sprintf("[tls] Cleaned up expired certificate validations")) |
| 332 | return nil | 332 | return nil | ||
| 333 | } | 333 | } | ||
M pkg/webkit/touchpad_gestures.go +10 -9
| @@ -1,9 +1,10 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | "math" | 5 | "math" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 8 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| @@ -17,7 +18,7 @@ const ( | |||||
| 17 | // This enables two-finger swipe navigation (left/right for back/forward) | 18 | // This enables two-finger swipe navigation (left/right for back/forward) | ||
| 18 | func (w *WebView) AttachTouchpadGestures() { | 19 | func (w *WebView) AttachTouchpadGestures() { | ||
| 19 | if w.view == nil { | 20 | if w.view == nil { | ||
| 20 | - | log.Printf("[touchpad-gestures] Cannot attach: WebView is nil") | 21 | + | logging.Error(fmt.Sprintf("[touchpad-gestures] Cannot attach: WebView is nil")) |
| 21 | return | 22 | return | ||
| 22 | } | 23 | } | ||
| 23 | 24 | ||||
| @@ -40,13 +41,13 @@ func (w *WebView) AttachTouchpadGestures() { | |||||
| 40 | 41 | ||||
| 41 | // Require horizontal velocity to be dominant | 42 | // Require horizontal velocity to be dominant | ||
| 42 | if absVelocityY > absVelocityX { | 43 | if absVelocityY > absVelocityX { | ||
| 43 | - | log.Printf("[touchpad-gestures] Swipe is more vertical (X:%.1f Y:%.1f), ignoring", velocityX, velocityY) | 44 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe is more vertical (X:%.1f Y:%.1f), ignoring", velocityX, velocityY)) |
| 44 | return | 45 | return | ||
| 45 | } | 46 | } | ||
| 46 | 47 | ||||
| 47 | // Check if swipe velocity meets threshold | 48 | // Check if swipe velocity meets threshold | ||
| 48 | if absVelocityX < swipeVelocityThreshold { | 49 | if absVelocityX < swipeVelocityThreshold { | ||
| 49 | - | log.Printf("[touchpad-gestures] Swipe velocity too low (%.1f < %.1f), ignoring", absVelocityX, swipeVelocityThreshold) | 50 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe velocity too low (%.1f < %.1f), ignoring", absVelocityX, swipeVelocityThreshold)) |
| 50 | return | 51 | return | ||
| 51 | } | 52 | } | ||
| 52 | 53 | ||||
| @@ -54,23 +55,23 @@ func (w *WebView) AttachTouchpadGestures() { | |||||
| 54 | if velocityX > 0 { | 55 | if velocityX > 0 { | ||
| 55 | // Swipe right (positive velocity) - go back | 56 | // Swipe right (positive velocity) - go back | ||
| 56 | if w.view.CanGoBack() { | 57 | if w.view.CanGoBack() { | ||
| 57 | - | log.Printf("[touchpad-gestures] Swipe right detected (velocity: %.1f) - navigating backward", velocityX) | 58 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe right detected (velocity: %.1f) - navigating backward", velocityX)) |
| 58 | w.view.GoBack() | 59 | w.view.GoBack() | ||
| 59 | } else { | 60 | } else { | ||
| 60 | - | log.Printf("[touchpad-gestures] Swipe right detected but cannot go back") | 61 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe right detected but cannot go back")) |
| 61 | } | 62 | } | ||
| 62 | } else { | 63 | } else { | ||
| 63 | // Swipe left (negative velocity) - go forward | 64 | // Swipe left (negative velocity) - go forward | ||
| 64 | if w.view.CanGoForward() { | 65 | if w.view.CanGoForward() { | ||
| 65 | - | log.Printf("[touchpad-gestures] Swipe left detected (velocity: %.1f) - navigating forward", velocityX) | 66 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe left detected (velocity: %.1f) - navigating forward", velocityX)) |
| 66 | w.view.GoForward() | 67 | w.view.GoForward() | ||
| 67 | } else { | 68 | } else { | ||
| 68 | - | log.Printf("[touchpad-gestures] Swipe left detected but cannot go forward") | 69 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Swipe left detected but cannot go forward")) |
| 69 | } | 70 | } | ||
| 70 | } | 71 | } | ||
| 71 | }) | 72 | }) | ||
| 72 | 73 | ||||
| 73 | // Attach controller to WebView | 74 | // Attach controller to WebView | ||
| 74 | w.view.AddController(gestureSwipe) | 75 | w.view.AddController(gestureSwipe) | ||
| 75 | - | log.Printf("[touchpad-gestures] Touchpad gesture controller attached to WebView ID %d", w.id) | 76 | + | logging.Debug(fmt.Sprintf("[touchpad-gestures] Touchpad gesture controller attached to WebView ID %d", w.id)) |
| 76 | } | 77 | } | ||
M pkg/webkit/types.go +7 -7
| @@ -70,13 +70,13 @@ type Config struct { | |||||
| 70 | // GetDefaultConfig returns a Config with sensible defaults | 70 | // GetDefaultConfig returns a Config with sensible defaults | ||
| 71 | func GetDefaultConfig() *Config { | 71 | func GetDefaultConfig() *Config { | ||
| 72 | return &Config{ | 72 | return &Config{ | ||
| 73 | - | UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", // WebKitGTK default (same as Epiphany) | 73 | + | UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15", // WebKitGTK default (same as Epiphany) |
| 74 | - | EnableJavaScript: true, | 74 | + | EnableJavaScript: true, |
| 75 | - | EnableWebGL: true, | 75 | + | EnableWebGL: true, |
| 76 | - | EnableMediaStream: true, | 76 | + | EnableMediaStream: true, |
| 77 | - | HardwareAcceleration: true, | 77 | + | HardwareAcceleration: true, |
| 78 | - | DefaultFontSize: 16, | 78 | + | DefaultFontSize: 16, |
| 79 | - | MinimumFontSize: 8, | 79 | + | MinimumFontSize: 8, |
| 80 | EnablePageCache: true, // Instant back/forward navigation | 80 | EnablePageCache: true, // Instant back/forward navigation | ||
| 81 | EnableSmoothScrolling: true, // Better UX | 81 | EnableSmoothScrolling: true, // Better UX | ||
| 82 | CreateWindow: true, // Default to standalone window | 82 | CreateWindow: true, // Default to standalone window | ||
M pkg/webkit/uri_scheme.go +3 -3
| @@ -2,8 +2,8 @@ package webkit | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "log" | |||
| 6 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 7 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 7 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| @@ -25,7 +25,7 @@ var ( | |||||
| 25 | // In WebKitGTK 6.0, this must be called BEFORE creating any WebViews | 25 | // In WebKitGTK 6.0, this must be called BEFORE creating any WebViews | ||
| 26 | // The actual registration happens in InitPersistentSession | 26 | // The actual registration happens in InitPersistentSession | ||
| 27 | func RegisterURIScheme(scheme string, callback URISchemeRequestCallback) { | 27 | func RegisterURIScheme(scheme string, callback URISchemeRequestCallback) { | ||
| 28 | - | log.Printf("[webkit] Registering URI scheme handler for: %s", scheme) | 28 | + | logging.Debug(fmt.Sprintf("[webkit] Registering URI scheme handler for: %s", scheme)) |
| 29 | pendingURISchemeHandlers = append(pendingURISchemeHandlers, &URISchemeHandler{ | 29 | pendingURISchemeHandlers = append(pendingURISchemeHandlers, &URISchemeHandler{ | ||
| 30 | scheme: scheme, | 30 | scheme: scheme, | ||
| 31 | callback: callback, | 31 | callback: callback, | ||
| @@ -45,7 +45,7 @@ func ApplyURISchemeHandlers(view *webkit.WebView) error { | |||||
| 45 | } | 45 | } | ||
| 46 | 46 | ||||
| 47 | for _, handler := range pendingURISchemeHandlers { | 47 | for _, handler := range pendingURISchemeHandlers { | ||
| 48 | - | log.Printf("[webkit] Registering URI scheme on WebContext: %s", handler.scheme) | 48 | + | logging.Debug(fmt.Sprintf("[webkit] Registering URI scheme on WebContext: %s", handler.scheme)) |
| 49 | 49 | ||||
| 50 | // Capture handler in closure | 50 | // Capture handler in closure | ||
| 51 | callback := handler.callback | 51 | callback := handler.callback | ||
M pkg/webkit/user_content.go +14 -14
| @@ -2,10 +2,10 @@ package webkit | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "log" | |||
| 6 | 5 | ||||
| 7 | "github.com/bnema/dumber/assets" | 6 | "github.com/bnema/dumber/assets" | ||
| 8 | "github.com/bnema/dumber/internal/config" | 7 | "github.com/bnema/dumber/internal/config" | ||
| 8 | + | "github.com/bnema/dumber/internal/logging" | |||
| 9 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 9 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 10 | ) | 10 | ) | ||
| 11 | 11 | ||||
| @@ -26,7 +26,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 26 | // Get the UserContentManager from the WebView | 26 | // Get the UserContentManager from the WebView | ||
| 27 | ucm := view.UserContentManager() | 27 | ucm := view.UserContentManager() | ||
| 28 | if ucm == nil { | 28 | if ucm == nil { | ||
| 29 | - | log.Printf("[webkit] UserContentManager is nil, skipping script injection") | 29 | + | logging.Warn(fmt.Sprintf("[webkit] UserContentManager is nil, skipping script injection")) |
| 30 | return nil | 30 | return nil | ||
| 31 | } | 31 | } | ||
| 32 | 32 | ||||
| @@ -56,7 +56,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 56 | nil, | 56 | nil, | ||
| 57 | nil, | 57 | nil, | ||
| 58 | )) | 58 | )) | ||
| 59 | - | log.Printf("[webkit] Injected webview ID script for ID: %d (main + isolated world)", webviewID) | 59 | + | logging.Debug(fmt.Sprintf("[webkit] Injected webview ID script for ID: %d (main + isolated world)", webviewID)) |
| 60 | 60 | ||||
| 61 | // Inject GTK theme detection in BOTH worlds | 61 | // Inject GTK theme detection in BOTH worlds | ||
| 62 | // The color-scheme.ts expects window.__dumber_gtk_prefers_dark to be set | 62 | // The color-scheme.ts expects window.__dumber_gtk_prefers_dark to be set | ||
| @@ -66,14 +66,14 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 66 | switch cfg.Appearance.ColorScheme { | 66 | switch cfg.Appearance.ColorScheme { | ||
| 67 | case "prefer-dark": | 67 | case "prefer-dark": | ||
| 68 | prefersDark = true | 68 | prefersDark = true | ||
| 69 | - | log.Printf("[webkit] Using config-forced dark theme (color_scheme: prefer-dark)") | 69 | + | logging.Debug(fmt.Sprintf("[webkit] Using config-forced dark theme (color_scheme: prefer-dark)")) |
| 70 | case "prefer-light": | 70 | case "prefer-light": | ||
| 71 | prefersDark = false | 71 | prefersDark = false | ||
| 72 | - | log.Printf("[webkit] Using config-forced light theme (color_scheme: prefer-light)") | 72 | + | logging.Debug(fmt.Sprintf("[webkit] Using config-forced light theme (color_scheme: prefer-light)")) |
| 73 | default: | 73 | default: | ||
| 74 | // "default" or empty - follow system GTK preference | 74 | // "default" or empty - follow system GTK preference | ||
| 75 | prefersDark = PrefersDarkTheme() | 75 | prefersDark = PrefersDarkTheme() | ||
| 76 | - | log.Printf("[webkit] Using system GTK theme preference (color_scheme: %s)", cfg.Appearance.ColorScheme) | 76 | + | logging.Debug(fmt.Sprintf("[webkit] Using system GTK theme preference (color_scheme: %s)", cfg.Appearance.ColorScheme)) |
| 77 | } | 77 | } | ||
| 78 | 78 | ||||
| 79 | gtkThemeScript := fmt.Sprintf(`window.__dumber_gtk_prefers_dark = %t;`, prefersDark) | 79 | gtkThemeScript := fmt.Sprintf(`window.__dumber_gtk_prefers_dark = %t;`, prefersDark) | ||
| @@ -96,7 +96,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 96 | nil, | 96 | nil, | ||
| 97 | nil, | 97 | nil, | ||
| 98 | )) | 98 | )) | ||
| 99 | - | log.Printf("[webkit] Injected theme preference: prefersDark=%t (main + isolated world)", prefersDark) | 99 | + | logging.Debug(fmt.Sprintf("[webkit] Injected theme preference: prefersDark=%t (main + isolated world)", prefersDark)) |
| 100 | 100 | ||||
| 101 | // Inject palette config in BOTH worlds | 101 | // Inject palette config in BOTH worlds | ||
| 102 | // The GUI expects window.__dumber_palette = { "light": {...}, "dark": {...} } | 102 | // The GUI expects window.__dumber_palette = { "light": {...}, "dark": {...} } | ||
| @@ -121,7 +121,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 121 | nil, | 121 | nil, | ||
| 122 | nil, | 122 | nil, | ||
| 123 | )) | 123 | )) | ||
| 124 | - | log.Printf("[webkit] Injected palette config (%d bytes, main + isolated world)", len(paletteScript)) | 124 | + | logging.Debug(fmt.Sprintf("[webkit] Injected palette config (%d bytes, main + isolated world)", len(paletteScript))) |
| 125 | } | 125 | } | ||
| 126 | 126 | ||||
| 127 | // Inject color-scheme script in BOTH worlds | 127 | // Inject color-scheme script in BOTH worlds | ||
| @@ -146,7 +146,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 146 | nil, | 146 | nil, | ||
| 147 | nil, | 147 | nil, | ||
| 148 | )) | 148 | )) | ||
| 149 | - | log.Printf("[webkit] Injected color-scheme script (%d bytes, main + isolated world)", len(assets.ColorSchemeScript)) | 149 | + | logging.Debug(fmt.Sprintf("[webkit] Injected color-scheme script (%d bytes, main + isolated world)", len(assets.ColorSchemeScript))) |
| 150 | } | 150 | } | ||
| 151 | 151 | ||||
| 152 | // Inject main-world script in MAIN world | 152 | // Inject main-world script in MAIN world | ||
| @@ -160,7 +160,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 160 | nil, | 160 | nil, | ||
| 161 | nil, | 161 | nil, | ||
| 162 | )) | 162 | )) | ||
| 163 | - | log.Printf("[webkit] Injected main-world script (%d bytes, main world)", len(assets.MainWorldScript)) | 163 | + | logging.Debug(fmt.Sprintf("[webkit] Injected main-world script (%d bytes, main world)", len(assets.MainWorldScript))) |
| 164 | } | 164 | } | ||
| 165 | 165 | ||||
| 166 | // Inject GUI controls script in ISOLATED world at document-start | 166 | // Inject GUI controls script in ISOLATED world at document-start | ||
| @@ -175,7 +175,7 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 175 | nil, | 175 | nil, | ||
| 176 | nil, | 176 | nil, | ||
| 177 | )) | 177 | )) | ||
| 178 | - | log.Printf("[webkit] Injected GUI controls script (%d bytes, isolated world, document-start)", len(assets.GUIScript)) | 178 | + | logging.Debug(fmt.Sprintf("[webkit] Injected GUI controls script (%d bytes, isolated world, document-start)", len(assets.GUIScript))) |
| 179 | } | 179 | } | ||
| 180 | 180 | ||||
| 181 | // Inject component CSS styles as a JavaScript variable in ISOLATED world at document-start | 181 | // Inject component CSS styles as a JavaScript variable in ISOLATED world at document-start | ||
| @@ -194,16 +194,16 @@ func SetupUserContentManager(view *webkit.WebView, appearanceConfigJSON string, | |||||
| 194 | nil, | 194 | nil, | ||
| 195 | nil, | 195 | nil, | ||
| 196 | )) | 196 | )) | ||
| 197 | - | log.Printf("[webkit] Injected component styles string (%d bytes, isolated world, document-start)", len(assets.ComponentStyles)) | 197 | + | logging.Debug(fmt.Sprintf("[webkit] Injected component styles string (%d bytes, isolated world, document-start)", len(assets.ComponentStyles))) |
| 198 | } | 198 | } | ||
| 199 | 199 | ||||
| 200 | // Register script message handler "dumber" in the MAIN world | 200 | // Register script message handler "dumber" in the MAIN world | ||
| 201 | // Isolated world GUI scripts dispatch CustomEvents to main world, which forwards to this handler | 201 | // Isolated world GUI scripts dispatch CustomEvents to main world, which forwards to this handler | ||
| 202 | // This architecture is required because webkit.messageHandlers is only available in main world | 202 | // This architecture is required because webkit.messageHandlers is only available in main world | ||
| 203 | if !ucm.RegisterScriptMessageHandler("dumber", "") { | 203 | if !ucm.RegisterScriptMessageHandler("dumber", "") { | ||
| 204 | - | log.Printf("[webkit] Warning: failed to register 'dumber' script message handler in main world") | 204 | + | logging.Warn(fmt.Sprintf("[webkit] Warning: failed to register 'dumber' script message handler in main world")) |
| 205 | } else { | 205 | } else { | ||
| 206 | - | log.Printf("[webkit] Registered 'dumber' script message handler in main world (default)") | 206 | + | logging.Debug(fmt.Sprintf("[webkit] Registered 'dumber' script message handler in main world (default)")) |
| 207 | } | 207 | } | ||
| 208 | 208 | ||||
| 209 | return nil | 209 | return nil | ||
M pkg/webkit/webview.go +21 -21
| @@ -3,11 +3,11 @@ package webkit | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "encoding/json" | 4 | "encoding/json" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | - | "log" | |||
| 7 | "strconv" | 6 | "strconv" | ||
| 8 | "sync" | 7 | "sync" | ||
| 9 | "sync/atomic" | 8 | "sync/atomic" | ||
| 10 | 9 | ||||
| 10 | + | "github.com/bnema/dumber/internal/logging" | |||
| 11 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 11 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 12 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | 12 | "github.com/diamondburned/gotk4/pkg/gdk/v4" | ||
| 13 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 13 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| @@ -61,7 +61,7 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||||
| 61 | 61 | ||||
| 62 | // Generate unique ID immediately | 62 | // Generate unique ID immediately | ||
| 63 | id := atomic.AddUint64(&viewIDCounter, 1) | 63 | id := atomic.AddUint64(&viewIDCounter, 1) | ||
| 64 | - | log.Printf("[webkit] Generated WebView ID: %d (CreateWindow=%v)", id, cfg.CreateWindow) | 64 | + | logging.Debug(fmt.Sprintf("[webkit] Generated WebView ID: %d (CreateWindow=%v)", id, cfg.CreateWindow)) |
| 65 | 65 | ||||
| 66 | InitMainThread() | 66 | InitMainThread() | ||
| 67 | 67 | ||||
| @@ -166,9 +166,9 @@ func NewWebView(cfg *Config) (*WebView, error) { | |||||
| 166 | wv.window = window | 166 | wv.window = window | ||
| 167 | // Add container as child of window | 167 | // Add container as child of window | ||
| 168 | window.SetChild(container) | 168 | window.SetChild(container) | ||
| 169 | - | log.Printf("[webkit] Created standalone window for WebView ID %d", id) | 169 | + | logging.Debug(fmt.Sprintf("[webkit] Created standalone window for WebView ID %d", id)) |
| 170 | } else { | 170 | } else { | ||
| 171 | - | log.Printf("[webkit] Created WebView ID %d without window (for workspace embedding)", id) | 171 | + | logging.Debug(fmt.Sprintf("[webkit] Created WebView ID %d without window (for workspace embedding)", id)) |
| 172 | } | 172 | } | ||
| 173 | 173 | ||||
| 174 | // Register in global registry | 174 | // Register in global registry | ||
| @@ -279,22 +279,22 @@ func (w *WebView) setupEventHandlers() { | |||||
| 279 | 279 | ||||
| 280 | // Create signal - for popup lifecycle management | 280 | // Create signal - for popup lifecycle management | ||
| 281 | w.view.ConnectCreate(func(navigationAction *webkit.NavigationAction) gtk.Widgetter { | 281 | w.view.ConnectCreate(func(navigationAction *webkit.NavigationAction) gtk.Widgetter { | ||
| 282 | - | log.Printf("[webkit] ConnectCreate callback fired for parent WebView ID=%d", w.id) | 282 | + | logging.Debug(fmt.Sprintf("[webkit] ConnectCreate callback fired for parent WebView ID=%d", w.id)) |
| 283 | if w.onPopupCreate != nil { | 283 | if w.onPopupCreate != nil { | ||
| 284 | - | log.Printf("[webkit] Calling onPopupCreate handler") | 284 | + | logging.Debug(fmt.Sprintf("[webkit] Calling onPopupCreate handler")) |
| 285 | newWebView := w.onPopupCreate(navigationAction) | 285 | newWebView := w.onPopupCreate(navigationAction) | ||
| 286 | if newWebView != nil { | 286 | if newWebView != nil { | ||
| 287 | - | log.Printf("[webkit] onPopupCreate returned WebView wrapper ID=%d", newWebView.id) | 287 | + | logging.Debug(fmt.Sprintf("[webkit] onPopupCreate returned WebView wrapper ID=%d", newWebView.id)) |
| 288 | - | log.Printf("[webkit] Extracting underlying gotk4 WebView from wrapper") | 288 | + | logging.Debug(fmt.Sprintf("[webkit] Extracting underlying gotk4 WebView from wrapper")) |
| 289 | underlyingView := newWebView.view | 289 | underlyingView := newWebView.view | ||
| 290 | - | log.Printf("[webkit] Extracted gotk4 WebView=%p, about to return to WebKit", underlyingView) | 290 | + | logging.Debug(fmt.Sprintf("[webkit] Extracted gotk4 WebView=%p, about to return to WebKit", underlyingView)) |
| 291 | // Return the underlying WebView widget to WebKit | 291 | // Return the underlying WebView widget to WebKit | ||
| 292 | return underlyingView | 292 | return underlyingView | ||
| 293 | } | 293 | } | ||
| 294 | - | log.Printf("[webkit] onPopupCreate returned nil, blocking popup") | 294 | + | logging.Debug(fmt.Sprintf("[webkit] onPopupCreate returned nil, blocking popup")) |
| 295 | } | 295 | } | ||
| 296 | // Return nil to cancel popup creation | 296 | // Return nil to cancel popup creation | ||
| 297 | - | log.Printf("[webkit] No onPopupCreate handler, blocking popup") | 297 | + | logging.Debug(fmt.Sprintf("[webkit] No onPopupCreate handler, blocking popup")) |
| 298 | return nil | 298 | return nil | ||
| 299 | }) | 299 | }) | ||
| 300 | 300 | ||||
| @@ -379,7 +379,7 @@ func (w *WebView) setupNavigationPolicyHandler() { | |||||
| 379 | if isCtrlClick { | 379 | if isCtrlClick { | ||
| 380 | clickType = "Ctrl+click" | 380 | clickType = "Ctrl+click" | ||
| 381 | } | 381 | } | ||
| 382 | - | log.Printf("[navigation-policy] Detected %s on link: %s", clickType, linkURL) | 382 | + | logging.Debug(fmt.Sprintf("[navigation-policy] Detected %s on link: %s", clickType, linkURL)) |
| 383 | 383 | ||||
| 384 | // Call the registered handler | 384 | // Call the registered handler | ||
| 385 | w.mu.RLock() | 385 | w.mu.RLock() | ||
| @@ -389,7 +389,7 @@ func (w *WebView) setupNavigationPolicyHandler() { | |||||
| 389 | if handler != nil { | 389 | if handler != nil { | ||
| 390 | handled := handler(linkURL) | 390 | handled := handler(linkURL) | ||
| 391 | if handled { | 391 | if handled { | ||
| 392 | - | log.Printf("[navigation-policy] %s handled by workspace, blocking navigation", clickType) | 392 | + | logging.Debug(fmt.Sprintf("[navigation-policy] %s handled by workspace, blocking navigation", clickType)) |
| 393 | // Block the navigation by calling Ignore() | 393 | // Block the navigation by calling Ignore() | ||
| 394 | navDecision.Ignore() | 394 | navDecision.Ignore() | ||
| 395 | return true // Prevent default behavior | 395 | return true // Prevent default behavior | ||
| @@ -399,7 +399,7 @@ func (w *WebView) setupNavigationPolicyHandler() { | |||||
| 399 | return false // Let WebKit handle if not handled | 399 | return false // Let WebKit handle if not handled | ||
| 400 | }) | 400 | }) | ||
| 401 | 401 | ||||
| 402 | - | log.Printf("[webkit] Navigation policy handler attached to WebView ID %d", w.id) | 402 | + | logging.Debug(fmt.Sprintf("[webkit] Navigation policy handler attached to WebView ID %d", w.id)) |
| 403 | } | 403 | } | ||
| 404 | 404 | ||||
| 405 | // LoadURL loads the given URL in the WebView | 405 | // LoadURL loads the given URL in the WebView | ||
| @@ -800,7 +800,7 @@ func (w *WebView) DispatchCustomEvent(eventName string, data interface{}) error | |||||
| 800 | return fmt.Errorf("failed to marshal event data: %w", err) | 800 | return fmt.Errorf("failed to marshal event data: %w", err) | ||
| 801 | } | 801 | } | ||
| 802 | 802 | ||||
| 803 | - | log.Printf("[webkit] Dispatching event '%s' to WebView ID %d with data: %s", eventName, w.id, string(jsonData)) | 803 | + | logging.Debug(fmt.Sprintf("[webkit] Dispatching event '%s' to WebView ID %d with data: %s", eventName, w.id, string(jsonData))) |
| 804 | 804 | ||||
| 805 | // Use document.dispatchEvent (not window) so events cross JavaScript world boundaries | 805 | // Use document.dispatchEvent (not window) so events cross JavaScript world boundaries | ||
| 806 | // The GUI scripts run in an isolated world but share the same Document object | 806 | // The GUI scripts run in an isolated world but share the same Document object | ||
| @@ -925,7 +925,7 @@ func (w *WebView) UpdateContentFilters(rules string) error { | |||||
| 925 | 925 | ||||
| 926 | // Note: Content filtering is handled via InitializeContentBlocking() | 926 | // Note: Content filtering is handled via InitializeContentBlocking() | ||
| 927 | // This method is kept for backward compatibility but is deprecated | 927 | // This method is kept for backward compatibility but is deprecated | ||
| 928 | - | log.Printf("[webkit] UpdateContentFilters called but is deprecated - use InitializeContentBlocking instead") | 928 | + | logging.Warn(fmt.Sprintf("[webkit] UpdateContentFilters called but is deprecated - use InitializeContentBlocking instead")) |
| 929 | return nil | 929 | return nil | ||
| 930 | } | 930 | } | ||
| 931 | 931 | ||||
| @@ -979,21 +979,21 @@ func (w *WebView) setupFaviconHandlers() { | |||||
| 979 | // Get the NetworkSession from the WebView | 979 | // Get the NetworkSession from the WebView | ||
| 980 | session := w.view.NetworkSession() | 980 | session := w.view.NetworkSession() | ||
| 981 | if session == nil { | 981 | if session == nil { | ||
| 982 | - | log.Printf("[webkit] Warning: No NetworkSession available for favicon handling") | 982 | + | logging.Warn(fmt.Sprintf("[webkit] Warning: No NetworkSession available for favicon handling")) |
| 983 | return | 983 | return | ||
| 984 | } | 984 | } | ||
| 985 | 985 | ||||
| 986 | // Get the WebsiteDataManager from the NetworkSession | 986 | // Get the WebsiteDataManager from the NetworkSession | ||
| 987 | dataManager := session.WebsiteDataManager() | 987 | dataManager := session.WebsiteDataManager() | ||
| 988 | if dataManager == nil { | 988 | if dataManager == nil { | ||
| 989 | - | log.Printf("[webkit] Warning: No WebsiteDataManager available for favicon handling") | 989 | + | logging.Warn(fmt.Sprintf("[webkit] Warning: No WebsiteDataManager available for favicon handling")) |
| 990 | return | 990 | return | ||
| 991 | } | 991 | } | ||
| 992 | 992 | ||||
| 993 | // Enable favicons if not already enabled | 993 | // Enable favicons if not already enabled | ||
| 994 | if !dataManager.FaviconsEnabled() { | 994 | if !dataManager.FaviconsEnabled() { | ||
| 995 | dataManager.SetFaviconsEnabled(true) | 995 | dataManager.SetFaviconsEnabled(true) | ||
| 996 | - | log.Printf("[webkit] Enabled favicons for WebView ID %d", w.id) | 996 | + | logging.Debug(fmt.Sprintf("[webkit] Enabled favicons for WebView ID %d", w.id)) |
| 997 | } | 997 | } | ||
| 998 | 998 | ||||
| 999 | } | 999 | } | ||
| @@ -1016,7 +1016,7 @@ func WrapBareWebView(bareView *webkit.WebView) *WebView { | |||||
| 1016 | 1016 | ||||
| 1017 | // Generate unique ID | 1017 | // Generate unique ID | ||
| 1018 | id := atomic.AddUint64(&viewIDCounter, 1) | 1018 | id := atomic.AddUint64(&viewIDCounter, 1) | ||
| 1019 | - | log.Printf("[webkit] Created minimal wrapper for bare WebView (ID: %d)", id) | 1019 | + | logging.Debug(fmt.Sprintf("[webkit] Created minimal wrapper for bare WebView (ID: %d)", id)) |
| 1020 | 1020 | ||||
| 1021 | // Create minimal wrapper - NO initialization yet | 1021 | // Create minimal wrapper - NO initialization yet | ||
| 1022 | wv := &WebView{ | 1022 | wv := &WebView{ | ||
| @@ -1093,7 +1093,7 @@ func (w *WebView) InitializeFromBare(cfg *Config) error { | |||||
| 1093 | // Attach touchpad gesture controls | 1093 | // Attach touchpad gesture controls | ||
| 1094 | w.AttachTouchpadGestures() | 1094 | w.AttachTouchpadGestures() | ||
| 1095 | 1095 | ||||
| 1096 | - | log.Printf("[webkit] Completed initialization for WebView ID %d", w.id) | 1096 | + | logging.Debug(fmt.Sprintf("[webkit] Completed initialization for WebView ID %d", w.id)) |
| 1097 | 1097 | ||||
| 1098 | return nil | 1098 | return nil | ||
| 1099 | } | 1099 | } | ||
M pkg/webkit/webview_related.go +18 -17
| @@ -19,10 +19,11 @@ static inline WebKitWebView* create_related_web_view(WebKitWebView* parent) { | |||||
| 19 | */ | 19 | */ | ||
| 20 | import "C" | 20 | import "C" | ||
| 21 | import ( | 21 | import ( | ||
| 22 | - | "log" | 22 | + | "fmt" |
| 23 | "runtime" | 23 | "runtime" | ||
| 24 | "unsafe" | 24 | "unsafe" | ||
| 25 | 25 | ||||
| 26 | + | "github.com/bnema/dumber/internal/logging" | |||
| 26 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | 27 | webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6" | ||
| 27 | coreglib "github.com/diamondburned/gotk4/pkg/core/glib" | 28 | coreglib "github.com/diamondburned/gotk4/pkg/core/glib" | ||
| 28 | "github.com/diamondburned/gotk4/pkg/glib/v2" | 29 | "github.com/diamondburned/gotk4/pkg/glib/v2" | ||
| @@ -37,39 +38,39 @@ import ( | |||||
| 37 | // we manually construct the WebView struct hierarchy using CGO. | 38 | // we manually construct the WebView struct hierarchy using CGO. | ||
| 38 | func NewBareRelatedWebView(parentView *webkit.WebView) *webkit.WebView { | 39 | func NewBareRelatedWebView(parentView *webkit.WebView) *webkit.WebView { | ||
| 39 | if parentView == nil { | 40 | if parentView == nil { | ||
| 40 | - | log.Printf("[webkit] NewBareRelatedWebView: parent is nil, creating unrelated WebView") | 41 | + | logging.Warn(fmt.Sprintf("[webkit] NewBareRelatedWebView: parent is nil, creating unrelated WebView")) |
| 41 | return webkit.NewWebView() | 42 | return webkit.NewWebView() | ||
| 42 | } | 43 | } | ||
| 43 | 44 | ||||
| 44 | // Get the native C pointer from the gotk4 WebView | 45 | // Get the native C pointer from the gotk4 WebView | ||
| 45 | parentObj := glib.BaseObject(parentView) | 46 | parentObj := glib.BaseObject(parentView) | ||
| 46 | if parentObj == nil { | 47 | if parentObj == nil { | ||
| 47 | - | log.Printf("[webkit] NewBareRelatedWebView: failed to get parent object, creating unrelated WebView") | 48 | + | logging.Error(fmt.Sprintf("[webkit] NewBareRelatedWebView: failed to get parent object, creating unrelated WebView")) |
| 48 | return webkit.NewWebView() | 49 | return webkit.NewWebView() | ||
| 49 | } | 50 | } | ||
| 50 | parentNative := (*C.WebKitWebView)(unsafe.Pointer(parentObj.Native())) | 51 | parentNative := (*C.WebKitWebView)(unsafe.Pointer(parentObj.Native())) | ||
| 51 | if parentNative == nil { | 52 | if parentNative == nil { | ||
| 52 | - | log.Printf("[webkit] NewBareRelatedWebView: parent native pointer is nil, creating unrelated WebView") | 53 | + | logging.Error(fmt.Sprintf("[webkit] NewBareRelatedWebView: parent native pointer is nil, creating unrelated WebView")) |
| 53 | return webkit.NewWebView() | 54 | return webkit.NewWebView() | ||
| 54 | } | 55 | } | ||
| 55 | 56 | ||||
| 56 | - | log.Printf("[webkit] Creating bare WebView with related-view property (parent=%p)", parentNative) | 57 | + | logging.Debug(fmt.Sprintf("[webkit] Creating bare WebView with related-view property (parent=%p)", parentNative)) |
| 57 | 58 | ||||
| 58 | // Create related WebView using CGO helper with g_object_new | 59 | // Create related WebView using CGO helper with g_object_new | ||
| 59 | - | log.Printf("[webkit] Step 3: About to call C.create_related_web_view") | 60 | + | logging.Debug(fmt.Sprintf("[webkit] Step 3: About to call C.create_related_web_view")) |
| 60 | webViewNative := C.create_related_web_view(parentNative) | 61 | webViewNative := C.create_related_web_view(parentNative) | ||
| 61 | - | log.Printf("[webkit] Step 4: C.create_related_web_view returned: %p", webViewNative) | 62 | + | logging.Debug(fmt.Sprintf("[webkit] Step 4: C.create_related_web_view returned: %p", webViewNative)) |
| 62 | 63 | ||||
| 63 | if webViewNative == nil { | 64 | if webViewNative == nil { | ||
| 64 | - | log.Printf("[webkit] NewBareRelatedWebView: C.create_related_web_view returned nil") | 65 | + | logging.Error(fmt.Sprintf("[webkit] NewBareRelatedWebView: C.create_related_web_view returned nil")) |
| 65 | return nil | 66 | return nil | ||
| 66 | } | 67 | } | ||
| 67 | 68 | ||||
| 68 | // Wrap the C object in a coreglib.Object | 69 | // Wrap the C object in a coreglib.Object | ||
| 69 | // This takes ownership of the floating reference | 70 | // This takes ownership of the floating reference | ||
| 70 | - | log.Printf("[webkit] Step 5: About to call coreglib.Take") | 71 | + | logging.Debug(fmt.Sprintf("[webkit] Step 5: About to call coreglib.Take")) |
| 71 | obj := coreglib.Take(unsafe.Pointer(webViewNative)) | 72 | obj := coreglib.Take(unsafe.Pointer(webViewNative)) | ||
| 72 | - | log.Printf("[webkit] Step 6: coreglib.Take succeeded, obj=%p", obj) | 73 | + | logging.Debug(fmt.Sprintf("[webkit] Step 6: coreglib.Take succeeded, obj=%p", obj)) |
| 73 | 74 | ||||
| 74 | // Manually construct the WebView struct hierarchy | 75 | // Manually construct the WebView struct hierarchy | ||
| 75 | // WebView -> WebViewBase -> gtk.Widget -> coreglib.InitiallyUnowned -> coreglib.Object | 76 | // WebView -> WebViewBase -> gtk.Widget -> coreglib.InitiallyUnowned -> coreglib.Object | ||
| @@ -78,7 +79,7 @@ func NewBareRelatedWebView(parentView *webkit.WebView) *webkit.WebView { | |||||
| 78 | // manually construct the full struct hierarchy using the same pattern | 79 | // manually construct the full struct hierarchy using the same pattern | ||
| 79 | // that gotk4 uses internally. | 80 | // that gotk4 uses internally. | ||
| 80 | 81 | ||||
| 81 | - | log.Printf("[webkit] Step 7: About to create gtk.Widget base") | 82 | + | logging.Debug(fmt.Sprintf("[webkit] Step 7: About to create gtk.Widget base")) |
| 82 | // Create the gtk.Widget base | 83 | // Create the gtk.Widget base | ||
| 83 | widget := gtk.Widget{ | 84 | widget := gtk.Widget{ | ||
| 84 | InitiallyUnowned: coreglib.InitiallyUnowned{ | 85 | InitiallyUnowned: coreglib.InitiallyUnowned{ | ||
| @@ -95,25 +96,25 @@ func NewBareRelatedWebView(parentView *webkit.WebView) *webkit.WebView { | |||||
| 95 | Object: obj, | 96 | Object: obj, | ||
| 96 | }, | 97 | }, | ||
| 97 | } | 98 | } | ||
| 98 | - | log.Printf("[webkit] Step 8: Created gtk.Widget base") | 99 | + | logging.Debug(fmt.Sprintf("[webkit] Step 8: Created gtk.Widget base")) |
| 99 | 100 | ||||
| 100 | // Create the WebViewBase | 101 | // Create the WebViewBase | ||
| 101 | - | log.Printf("[webkit] Step 9: About to create WebViewBase") | 102 | + | logging.Debug(fmt.Sprintf("[webkit] Step 9: About to create WebViewBase")) |
| 102 | webViewBase := webkit.WebViewBase{ | 103 | webViewBase := webkit.WebViewBase{ | ||
| 103 | Widget: widget, | 104 | Widget: widget, | ||
| 104 | } | 105 | } | ||
| 105 | - | log.Printf("[webkit] Step 10: Created WebViewBase") | 106 | + | logging.Debug(fmt.Sprintf("[webkit] Step 10: Created WebViewBase")) |
| 106 | 107 | ||||
| 107 | // Create the final WebView | 108 | // Create the final WebView | ||
| 108 | - | log.Printf("[webkit] Step 11: About to create final WebView") | 109 | + | logging.Debug(fmt.Sprintf("[webkit] Step 11: About to create final WebView")) |
| 109 | relatedView := &webkit.WebView{ | 110 | relatedView := &webkit.WebView{ | ||
| 110 | WebViewBase: webViewBase, | 111 | WebViewBase: webViewBase, | ||
| 111 | } | 112 | } | ||
| 112 | - | log.Printf("[webkit] Step 12: Created final WebView: %p", relatedView) | 113 | + | logging.Debug(fmt.Sprintf("[webkit] Step 12: Created final WebView: %p", relatedView)) |
| 113 | 114 | ||||
| 114 | // Ensure parentView is kept alive until after the CGO call completes | 115 | // Ensure parentView is kept alive until after the CGO call completes | ||
| 115 | runtime.KeepAlive(parentView) | 116 | runtime.KeepAlive(parentView) | ||
| 116 | 117 | ||||
| 117 | - | log.Printf("[webkit] Created bare related WebView (parent=%p)", parentNative) | 118 | + | logging.Debug(fmt.Sprintf("[webkit] Created bare related WebView (parent=%p)", parentNative)) |
| 118 | return relatedView | 119 | return relatedView | ||
| 119 | } | 120 | } | ||
M pkg/webkit/window.go +3 -2
| @@ -1,8 +1,9 @@ | |||||
| 1 | package webkit | 1 | package webkit | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "log" | 4 | + | "fmt" |
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/logging" | |||
| 6 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | 7 | "github.com/diamondburned/gotk4/pkg/gtk/v4" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| @@ -26,7 +27,7 @@ func NewWindow(title string) (*Window, error) { | |||||
| 26 | // Connect close-request signal to handle Cmd+Q and window close button | 27 | // Connect close-request signal to handle Cmd+Q and window close button | ||
| 27 | // Returning true prevents the default close behavior and allows graceful shutdown | 28 | // Returning true prevents the default close behavior and allows graceful shutdown | ||
| 28 | win.ConnectCloseRequest(func() bool { | 29 | win.ConnectCloseRequest(func() bool { | ||
| 29 | - | log.Printf("[window] Close request received (Cmd+Q or close button) - initiating graceful shutdown") | 30 | + | logging.Info(fmt.Sprintf("[window] Close request received (Cmd+Q or close button) - initiating graceful shutdown")) |
| 30 | // Quit the main loop gracefully, which will trigger cleanup | 31 | // Quit the main loop gracefully, which will trigger cleanup | ||
| 31 | QuitMainLoop() | 32 | QuitMainLoop() | ||
| 32 | // Return true to prevent GTK from destroying the window immediately | 33 | // Return true to prevent GTK from destroying the window immediately | ||