detached 2912397

feat(webkit): add WebView state snapshot and callback registration API

Add two new public methods to WebView:
- State(): Returns current WebView state (URI, title, loading status,
  progress, navigation state, zoom level) as a snapshot
- SetCallbacks(): Registers or clears WebView event callbacks (load changed,
  title changed, URI changed, progress changed, close, create)

Add compile-time interface check (var _ port.WebView = (*WebView)(nil))
to ensure WebView implements the port.WebView interface.

These additions support the coordinator architecture and enable cleaner
event handling patterns.
commit
2912397dff14fe354fd50226c2c1833bf491d261
author
brice · · 2025-12-15 17:03 UTC
committer
brice · · 2025-12-15 21:06 UTC
parent
33d4791
signature
unsigned
1 file · +61 -0
M internal/infrastructure/webkit/webview.go +61 -0
@@ -14,6 +14,9 @@ import (
1414 "github.com/rs/zerolog"
1515 )
1616
17+// Compile-time interface check: WebView must implement port.WebView.
18+var _ port.WebView = (*WebView)(nil)
19+
1720 // WebViewID is an alias to port.WebViewID for clean architecture compliance.
1821 // Infrastructure layer uses the type defined in the application port.
1922 type WebViewID = port.WebViewID
@@ -426,6 +429,64 @@ func (wv *WebView) GetZoomLevel() float64 {
426429 return wv.inner.GetZoomLevel()
427430 }
428431
432+// State returns the current WebView state as a snapshot.
433+func (wv *WebView) State() port.WebViewState {
434+ return port.WebViewState{
435+ URI: wv.uri,
436+ Title: wv.title,
437+ IsLoading: wv.isLoading,
438+ Progress: wv.progress,
439+ CanGoBack: wv.canGoBack,
440+ CanGoFwd: wv.canGoFwd,
441+ ZoomLevel: wv.GetZoomLevel(),
442+ }
443+}
444+
445+// SetCallbacks registers callback handlers for WebView events.
446+// Pass nil to clear all callbacks.
447+func (wv *WebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
448+ if callbacks == nil {
449+ wv.OnLoadChanged = nil
450+ wv.OnTitleChanged = nil
451+ wv.OnURIChanged = nil
452+ wv.OnProgressChanged = nil
453+ wv.OnClose = nil
454+ wv.OnCreate = nil
455+ return
456+ }
457+
458+ // Map port callbacks to webkit callbacks
459+ if callbacks.OnLoadChanged != nil {
460+ wv.OnLoadChanged = func(e LoadEvent) {
461+ callbacks.OnLoadChanged(port.LoadEvent(e))
462+ }
463+ }
464+ wv.OnTitleChanged = callbacks.OnTitleChanged
465+ wv.OnURIChanged = callbacks.OnURIChanged
466+ wv.OnProgressChanged = callbacks.OnProgressChanged
467+ wv.OnClose = callbacks.OnClose
468+ if callbacks.OnCreate != nil {
469+ wv.OnCreate = func(req PopupRequest) *WebView {
470+ portReq := port.PopupRequest{
471+ TargetURI: req.TargetURI,
472+ FrameName: req.FrameName,
473+ IsUserGesture: req.IsUserGesture,
474+ ParentViewID: req.ParentID,
475+ }
476+ result := callbacks.OnCreate(portReq)
477+ if result == nil {
478+ return nil
479+ }
480+ // The callback returns port.WebView, we need to convert back
481+ // This assumes the returned WebView is actually a *webkit.WebView
482+ if wkView, ok := result.(*WebView); ok {
483+ return wkView
484+ }
485+ return nil
486+ }
487+ }
488+}
489+
429490 // ShowDevTools opens the WebKit inspector/developer tools.
430491 func (wv *WebView) ShowDevTools() error {
431492 if wv.destroyed.Load() {
@@ -14,6 +14,9 @@ import (
14 "github.com/rs/zerolog"14 "github.com/rs/zerolog"
15 )15 )
16 16
17+// Compile-time interface check: WebView must implement port.WebView.
18+var _ port.WebView = (*WebView)(nil)
19+
17 // WebViewID is an alias to port.WebViewID for clean architecture compliance.20 // WebViewID is an alias to port.WebViewID for clean architecture compliance.
18 // Infrastructure layer uses the type defined in the application port.21 // Infrastructure layer uses the type defined in the application port.
19 type WebViewID = port.WebViewID22 type WebViewID = port.WebViewID
@@ -426,6 +429,64 @@ func (wv *WebView) GetZoomLevel() float64 {
426 return wv.inner.GetZoomLevel()429 return wv.inner.GetZoomLevel()
427 }430 }
428 431
432+// State returns the current WebView state as a snapshot.
433+func (wv *WebView) State() port.WebViewState {
434+ return port.WebViewState{
435+ URI: wv.uri,
436+ Title: wv.title,
437+ IsLoading: wv.isLoading,
438+ Progress: wv.progress,
439+ CanGoBack: wv.canGoBack,
440+ CanGoFwd: wv.canGoFwd,
441+ ZoomLevel: wv.GetZoomLevel(),
442+ }
443+}
444+
445+// SetCallbacks registers callback handlers for WebView events.
446+// Pass nil to clear all callbacks.
447+func (wv *WebView) SetCallbacks(callbacks *port.WebViewCallbacks) {
448+ if callbacks == nil {
449+ wv.OnLoadChanged = nil
450+ wv.OnTitleChanged = nil
451+ wv.OnURIChanged = nil
452+ wv.OnProgressChanged = nil
453+ wv.OnClose = nil
454+ wv.OnCreate = nil
455+ return
456+ }
457+
458+ // Map port callbacks to webkit callbacks
459+ if callbacks.OnLoadChanged != nil {
460+ wv.OnLoadChanged = func(e LoadEvent) {
461+ callbacks.OnLoadChanged(port.LoadEvent(e))
462+ }
463+ }
464+ wv.OnTitleChanged = callbacks.OnTitleChanged
465+ wv.OnURIChanged = callbacks.OnURIChanged
466+ wv.OnProgressChanged = callbacks.OnProgressChanged
467+ wv.OnClose = callbacks.OnClose
468+ if callbacks.OnCreate != nil {
469+ wv.OnCreate = func(req PopupRequest) *WebView {
470+ portReq := port.PopupRequest{
471+ TargetURI: req.TargetURI,
472+ FrameName: req.FrameName,
473+ IsUserGesture: req.IsUserGesture,
474+ ParentViewID: req.ParentID,
475+ }
476+ result := callbacks.OnCreate(portReq)
477+ if result == nil {
478+ return nil
479+ }
480+ // The callback returns port.WebView, we need to convert back
481+ // This assumes the returned WebView is actually a *webkit.WebView
482+ if wkView, ok := result.(*WebView); ok {
483+ return wkView
484+ }
485+ return nil
486+ }
487+ }
488+}
489+
429 // ShowDevTools opens the WebKit inspector/developer tools.490 // ShowDevTools opens the WebKit inspector/developer tools.
430 func (wv *WebView) ShowDevTools() error {491 func (wv *WebView) ShowDevTools() error {
431 if wv.destroyed.Load() {492 if wv.destroyed.Load() {

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