v0.7.0 31c7fae

zoom.go RawHistoryFolder

1package control
2
3import (
4	"context"
5	"fmt"
6	"log"
7
8	"github.com/bnema/dumber/pkg/webkit"
9	"github.com/bnema/dumber/services"
10)
11
12// ZoomController manages zoom functionality for the WebView
13type ZoomController struct {
14	currentURL         string
15	lastZoomDomain     string
16	programmaticChange bool
17	browserService     *services.BrowserService
18	webView            *webkit.WebView
19}
20
21// WebViewInterface defines the interface for WebView zoom operations
22type WebViewInterface interface {
23	GetCurrentURL() string
24	SetZoom(level float64) error
25	InjectScript(script string) error
26	RegisterZoomChangedHandler(handler func(float64))
27	RegisterURIChangedHandler(handler func(string))
28}
29
30// NewZoomController creates a new zoom controller
31func NewZoomController(browserService *services.BrowserService, webView *webkit.WebView) *ZoomController {
32	return &ZoomController{
33		currentURL:     "dumb://homepage",
34		browserService: browserService,
35		webView:        webView,
36	}
37}
38
39// RegisterHandlers sets up the zoom-related event handlers
40func (z *ZoomController) RegisterHandlers() {
41	z.webView.RegisterZoomChangedHandler(z.handleZoomChange)
42	z.webView.RegisterURIChangedHandler(z.handleURIChange)
43}
44
45// handleURIChange responds to URL changes and applies saved zoom levels
46func (z *ZoomController) handleURIChange(url string) {
47	z.currentURL = url
48	if url == "" {
49		return
50	}
51	ctx := context.Background()
52	currentDomain := services.ZoomKeyForLog(url)
53
54	if zoomLevel, err := z.browserService.GetZoomLevel(ctx, url); err == nil {
55		z.programmaticChange = true
56		if err := z.webView.SetZoom(zoomLevel); err == nil {
57			log.Printf("[zoom] loaded %.2f for %s", zoomLevel, currentDomain)
58
59			// Only show toast when entering a new domain (not on first load)
60			if currentDomain != z.lastZoomDomain && z.lastZoomDomain != "" {
61				z.showZoomToast(zoomLevel)
62			}
63			z.lastZoomDomain = currentDomain
64		}
65		z.programmaticChange = false
66	}
67}
68
69// handleZoomChange responds to zoom level changes and persists them
70func (z *ZoomController) handleZoomChange(level float64) {
71	url := z.webView.GetCurrentURL()
72	if url == "" {
73		return
74	}
75	ctx := context.Background()
76	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		return
79	}
80	key := services.ZoomKeyForLog(url)
81	log.Printf("[zoom] saved %.2f for %s", level, key)
82
83	// Only show toast for user-initiated zoom changes, not programmatic ones
84	if !z.programmaticChange {
85		z.showZoomToast(level)
86	}
87}
88
89// showZoomToast displays a zoom level notification
90func (z *ZoomController) showZoomToast(level float64) {
91	js := fmt.Sprintf(`try { 
92		if (typeof window.__dumber_showZoomToast === 'function') {
93			window.__dumber_showZoomToast(%f);
94		} else {
95			console.warn('[dumber] Zoom toast function not available yet');
96		}
97	} catch(e) { 
98		console.error('[dumber] Zoom toast error:', e); 
99	}`, level)
100	if err := z.webView.InjectScript(js); err != nil {
101		log.Printf("[zoom] failed to show toast: %v", err)
102	}
103}
104
105// ApplyInitialZoom sets the initial zoom level for the current URL
106func (z *ZoomController) ApplyInitialZoom() {
107	ctx := context.Background()
108	url := z.webView.GetCurrentURL()
109	if url == "" {
110		url = "dumb://homepage"
111	}
112	if zoomLevel, err := z.browserService.GetZoomLevel(ctx, url); err == nil {
113		z.programmaticChange = true
114		if err := z.webView.SetZoom(zoomLevel); err != nil {
115			log.Printf("Warning: failed to set initial zoom: %v", err)
116		} else {
117			key := services.ZoomKeyForLog(url)
118			log.Printf("[zoom] loaded %.2f for %s", zoomLevel, key)
119		}
120		z.programmaticChange = false
121	}
122}
123
124// ApplyZoomForURL applies zoom for a specific URL (used for navigation)
125func (z *ZoomController) ApplyZoomForURL(url string) {
126	ctx := context.Background()
127	if zoomLevel, err := z.browserService.GetZoomLevel(ctx, url); err == nil {
128		z.programmaticChange = true
129		if err := z.webView.SetZoom(zoomLevel); err != nil {
130			log.Printf("Warning: failed to set zoom: %v", err)
131		} else {
132			key := services.ZoomKeyForLog(url)
133			log.Printf("[zoom] loaded %.2f for %s", zoomLevel, key)
134		}
135		z.programmaticChange = false
136	}
137}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close