detached 5163049

refactor(M2): replace any with typed interfaces in Engine port

- Replace HandlerDependencies.{HistoryUC,FavoritesUC} any → HomepageHistory/HomepageFavorites
- Replace HandlerDependencies.ConfigGetter func() any → AutoCopyConfig AutoCopyConfig
- Replace RegisterAccentHandlers handler any → AccentKeyHandler
- Replace ConfigureDownloads prepareUC any → DownloadPreparer
- Replace UpdateSettings cfg any → EngineSettingsUpdate{Raw any} (documented wrapper)
- Update webkit/engine.go, engine_init.go, download_handler.go to match new signatures
- Simplify bootstrap/engine.go: remove type assertions now that deps are typed
- Add configAutoCopyAdapter in ui/app.go to satisfy port.AutoCopyConfig
- Only EngineSettingsUpdate.Raw and ApplyWebProcessSettings remain any (documented)
commit
516304980718d3f7219fe61ac06f0844571f4d9c
author
brice · · 2026-03-17 10:17 UTC
parent
d0d5c96
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +60 -63
M internal/application/port/engine.go +16 -6
@@ -127,10 +127,10 @@ type Engine interface {
127127 RegisterHandlers(ctx context.Context, deps HandlerDependencies) error
128128
129129 // RegisterAccentHandlers registers accent/dead-key input handlers.
130- RegisterAccentHandlers(ctx context.Context, handler any) error
130+ RegisterAccentHandlers(ctx context.Context, handler AccentKeyHandler) error
131131
132132 // ConfigureDownloads sets up download handling for this engine.
133- ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler DownloadEventHandler, prepareUC any) error
133+ ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler DownloadEventHandler, preparer DownloadPreparer) error
134134
135135 // OnToolkitReady is called after the UI toolkit has initialized.
136136 OnToolkitReady(ctx context.Context) error
@@ -139,15 +139,25 @@ type Engine interface {
139139 UpdateAppearance(ctx context.Context, r, g, b, alpha float64) error
140140
141141 // UpdateSettings applies runtime config changes to engine internals.
142- UpdateSettings(ctx context.Context, cfg any) error
142+ UpdateSettings(ctx context.Context, update EngineSettingsUpdate) error
143+}
144+
145+// EngineSettingsUpdate carries a runtime config change to the engine.
146+// Raw holds the implementation-specific config object; the engine type-asserts
147+// to its concrete type (e.g. *config.Config for the WebKit engine).
148+// This is the only intentional use of `any` in the Engine port.
149+type EngineSettingsUpdate struct {
150+ // Raw holds the implementation-specific config.
151+ // WebKit engine expects *config.Config.
152+ Raw any
143153 }
144154
145155 // HandlerDependencies holds use cases needed by WebUI message handlers.
146156 type HandlerDependencies struct {
147- HistoryUC any
148- FavoritesUC any
157+ HistoryUC HomepageHistory
158+ FavoritesUC HomepageFavorites
149159 Clipboard Clipboard
150- ConfigGetter func() any
160+ AutoCopyConfig AutoCopyConfig
151161 OnClipboardCopied func(textLen int)
152162 }
153163
@@ -127,10 +127,10 @@ type Engine interface {
127 RegisterHandlers(ctx context.Context, deps HandlerDependencies) error127 RegisterHandlers(ctx context.Context, deps HandlerDependencies) error
128 128
129 // RegisterAccentHandlers registers accent/dead-key input handlers.129 // RegisterAccentHandlers registers accent/dead-key input handlers.
130- RegisterAccentHandlers(ctx context.Context, handler any) error130+ RegisterAccentHandlers(ctx context.Context, handler AccentKeyHandler) error
131 131
132 // ConfigureDownloads sets up download handling for this engine.132 // ConfigureDownloads sets up download handling for this engine.
133- ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler DownloadEventHandler, prepareUC any) error133+ ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler DownloadEventHandler, preparer DownloadPreparer) error
134 134
135 // OnToolkitReady is called after the UI toolkit has initialized.135 // OnToolkitReady is called after the UI toolkit has initialized.
136 OnToolkitReady(ctx context.Context) error136 OnToolkitReady(ctx context.Context) error
@@ -139,15 +139,25 @@ type Engine interface {
139 UpdateAppearance(ctx context.Context, r, g, b, alpha float64) error139 UpdateAppearance(ctx context.Context, r, g, b, alpha float64) error
140 140
141 // UpdateSettings applies runtime config changes to engine internals.141 // UpdateSettings applies runtime config changes to engine internals.
142- UpdateSettings(ctx context.Context, cfg any) error142+ UpdateSettings(ctx context.Context, update EngineSettingsUpdate) error
143+}
144+
145+// EngineSettingsUpdate carries a runtime config change to the engine.
146+// Raw holds the implementation-specific config object; the engine type-asserts
147+// to its concrete type (e.g. *config.Config for the WebKit engine).
148+// This is the only intentional use of `any` in the Engine port.
149+type EngineSettingsUpdate struct {
150+ // Raw holds the implementation-specific config.
151+ // WebKit engine expects *config.Config.
152+ Raw any
143 }153 }
144 154
145 // HandlerDependencies holds use cases needed by WebUI message handlers.155 // HandlerDependencies holds use cases needed by WebUI message handlers.
146 type HandlerDependencies struct {156 type HandlerDependencies struct {
147- HistoryUC any157+ HistoryUC HomepageHistory
148- FavoritesUC any158+ FavoritesUC HomepageFavorites
149 Clipboard Clipboard159 Clipboard Clipboard
150- ConfigGetter func() any160+ AutoCopyConfig AutoCopyConfig
151 OnClipboardCopied func(textLen int)161 OnClipboardCopied func(textLen int)
152 }162 }
153 163
M internal/bootstrap/engine.go +5 -21
@@ -54,34 +54,18 @@ func BuildEngine(input EngineInput) (port.Engine, error) {
5454 input.Ctx, cfg, opts, wkCfg,
5555 input.ThemeManager, input.ColorResolver, input.Logger,
5656 func(ctx context.Context, router *webkit.MessageRouter, deps port.HandlerDependencies) error {
57- var historyUC port.HomepageHistory
58- if deps.HistoryUC != nil {
59- historyUC, _ = deps.HistoryUC.(port.HomepageHistory)
60- }
61- var favoritesUC port.HomepageFavorites
62- if deps.FavoritesUC != nil {
63- favoritesUC, _ = deps.FavoritesUC.(port.HomepageFavorites)
64- }
65- var autoCopyConfig port.AutoCopyConfig
66- if deps.ConfigGetter != nil {
67- autoCopyConfig, _ = deps.ConfigGetter().(port.AutoCopyConfig)
68- }
6957 return handlers.RegisterAll(ctx, router, handlers.Config{
70- HistoryUC: historyUC,
71- FavoritesUC: favoritesUC,
58+ HistoryUC: deps.HistoryUC,
59+ FavoritesUC: deps.FavoritesUC,
7260 Clipboard: deps.Clipboard,
73- AutoCopyConfig: autoCopyConfig,
61+ AutoCopyConfig: deps.AutoCopyConfig,
7462 SaveConfig: saveConfigFunc,
7563 KeybindingsHandler: keybindingsHandler,
7664 OnClipboardCopied: deps.OnClipboardCopied,
7765 })
7866 },
79- func(ctx context.Context, router *webkit.MessageRouter, handler any) error {
80- accentHandler, ok := handler.(port.AccentKeyHandler)
81- if !ok {
82- return fmt.Errorf("handler does not implement port.AccentKeyHandler")
83- }
84- return handlers.RegisterAccentHandlers(ctx, router, accentHandler)
67+ func(ctx context.Context, router *webkit.MessageRouter, handler port.AccentKeyHandler) error {
68+ return handlers.RegisterAccentHandlers(ctx, router, handler)
8569 },
8670 )
8771 case "cef":
@@ -54,34 +54,18 @@ func BuildEngine(input EngineInput) (port.Engine, error) {
54 input.Ctx, cfg, opts, wkCfg,54 input.Ctx, cfg, opts, wkCfg,
55 input.ThemeManager, input.ColorResolver, input.Logger,55 input.ThemeManager, input.ColorResolver, input.Logger,
56 func(ctx context.Context, router *webkit.MessageRouter, deps port.HandlerDependencies) error {56 func(ctx context.Context, router *webkit.MessageRouter, deps port.HandlerDependencies) error {
57- var historyUC port.HomepageHistory
58- if deps.HistoryUC != nil {
59- historyUC, _ = deps.HistoryUC.(port.HomepageHistory)
60- }
61- var favoritesUC port.HomepageFavorites
62- if deps.FavoritesUC != nil {
63- favoritesUC, _ = deps.FavoritesUC.(port.HomepageFavorites)
64- }
65- var autoCopyConfig port.AutoCopyConfig
66- if deps.ConfigGetter != nil {
67- autoCopyConfig, _ = deps.ConfigGetter().(port.AutoCopyConfig)
68- }
69 return handlers.RegisterAll(ctx, router, handlers.Config{57 return handlers.RegisterAll(ctx, router, handlers.Config{
70- HistoryUC: historyUC,58+ HistoryUC: deps.HistoryUC,
71- FavoritesUC: favoritesUC,59+ FavoritesUC: deps.FavoritesUC,
72 Clipboard: deps.Clipboard,60 Clipboard: deps.Clipboard,
73- AutoCopyConfig: autoCopyConfig,61+ AutoCopyConfig: deps.AutoCopyConfig,
74 SaveConfig: saveConfigFunc,62 SaveConfig: saveConfigFunc,
75 KeybindingsHandler: keybindingsHandler,63 KeybindingsHandler: keybindingsHandler,
76 OnClipboardCopied: deps.OnClipboardCopied,64 OnClipboardCopied: deps.OnClipboardCopied,
77 })65 })
78 },66 },
79- func(ctx context.Context, router *webkit.MessageRouter, handler any) error {67+ func(ctx context.Context, router *webkit.MessageRouter, handler port.AccentKeyHandler) error {
80- accentHandler, ok := handler.(port.AccentKeyHandler)68+ return handlers.RegisterAccentHandlers(ctx, router, handler)
81- if !ok {
82- return fmt.Errorf("handler does not implement port.AccentKeyHandler")
83- }
84- return handlers.RegisterAccentHandlers(ctx, router, accentHandler)
85 },69 },
86 )70 )
87 case "cef":71 case "cef":
M internal/infrastructure/webkit/download_handler.go +14 -15
@@ -7,7 +7,6 @@ import (
77 "sync"
88
99 "github.com/bnema/dumber/internal/application/port"
10- "github.com/bnema/dumber/internal/application/usecase"
1110 downloadutil "github.com/bnema/dumber/internal/domain/download"
1211 "github.com/bnema/dumber/internal/logging"
1312 "github.com/bnema/puregotk-webkit/webkit"
@@ -20,26 +19,26 @@ const (
2019
2120 // DownloadHandler manages WebKit downloads and notifies the UI layer.
2221 type DownloadHandler struct {
23- downloadPath string
24- eventHandler port.DownloadEventHandler
25- prepareDownloadUC *usecase.PrepareDownloadUseCase
26- mu sync.RWMutex
22+ downloadPath string
23+ eventHandler port.DownloadEventHandler
24+ preparer port.DownloadPreparer
25+ mu sync.RWMutex
2726 }
2827
2928 // NewDownloadHandler creates a new download handler.
30-// Panics if prepareDownloadUC is nil (fail fast on misconfiguration).
29+// Panics if preparer is nil (fail fast on misconfiguration).
3130 func NewDownloadHandler(
3231 downloadPath string,
3332 handler port.DownloadEventHandler,
34- prepareDownloadUC *usecase.PrepareDownloadUseCase,
33+ preparer port.DownloadPreparer,
3534 ) *DownloadHandler {
36- if prepareDownloadUC == nil {
37- panic("prepareDownloadUC is required")
35+ if preparer == nil {
36+ panic("preparer is required")
3837 }
3938 return &DownloadHandler{
40- downloadPath: downloadPath,
41- eventHandler: handler,
42- prepareDownloadUC: prepareDownloadUC,
39+ downloadPath: downloadPath,
40+ eventHandler: handler,
41+ preparer: preparer,
4342 }
4443 }
4544
@@ -57,7 +56,7 @@ func (h *DownloadHandler) HandleDownload(ctx context.Context, download *webkit.D
5756 h.mu.RLock()
5857 downloadPath := h.downloadPath
5958 eventHandler := h.eventHandler
60- prepareDownloadUC := h.prepareDownloadUC
59+ preparer := h.preparer
6160 h.mu.RUnlock()
6261
6362 // Track download state in a dedicated struct to ensure proper isolation.
@@ -80,8 +79,8 @@ func (h *DownloadHandler) HandleDownload(ctx context.Context, download *webkit.D
8079 response = &uriResponseAdapter{resp: resp}
8180 }
8281
83- // Use PrepareDownloadUseCase to resolve filename
84- output := prepareDownloadUC.Execute(ctx, usecase.PrepareDownloadInput{
82+ // Use DownloadPreparer to resolve filename
83+ output := preparer.Execute(ctx, port.DownloadPrepareInput{
8584 SuggestedFilename: suggestedFilename,
8685 Response: response,
8786 DownloadDir: downloadPath,
@@ -7,7 +7,6 @@ import (
7 "sync"7 "sync"
8 8
9 "github.com/bnema/dumber/internal/application/port"9 "github.com/bnema/dumber/internal/application/port"
10- "github.com/bnema/dumber/internal/application/usecase"
11 downloadutil "github.com/bnema/dumber/internal/domain/download"10 downloadutil "github.com/bnema/dumber/internal/domain/download"
12 "github.com/bnema/dumber/internal/logging"11 "github.com/bnema/dumber/internal/logging"
13 "github.com/bnema/puregotk-webkit/webkit"12 "github.com/bnema/puregotk-webkit/webkit"
@@ -20,26 +19,26 @@ const (
20 19
21 // DownloadHandler manages WebKit downloads and notifies the UI layer.20 // DownloadHandler manages WebKit downloads and notifies the UI layer.
22 type DownloadHandler struct {21 type DownloadHandler struct {
23- downloadPath string22+ downloadPath string
24- eventHandler port.DownloadEventHandler23+ eventHandler port.DownloadEventHandler
25- prepareDownloadUC *usecase.PrepareDownloadUseCase24+ preparer port.DownloadPreparer
26- mu sync.RWMutex25+ mu sync.RWMutex
27 }26 }
28 27
29 // NewDownloadHandler creates a new download handler.28 // NewDownloadHandler creates a new download handler.
30-// Panics if prepareDownloadUC is nil (fail fast on misconfiguration).29+// Panics if preparer is nil (fail fast on misconfiguration).
31 func NewDownloadHandler(30 func NewDownloadHandler(
32 downloadPath string,31 downloadPath string,
33 handler port.DownloadEventHandler,32 handler port.DownloadEventHandler,
34- prepareDownloadUC *usecase.PrepareDownloadUseCase,33+ preparer port.DownloadPreparer,
35 ) *DownloadHandler {34 ) *DownloadHandler {
36- if prepareDownloadUC == nil {35+ if preparer == nil {
37- panic("prepareDownloadUC is required")36+ panic("preparer is required")
38 }37 }
39 return &DownloadHandler{38 return &DownloadHandler{
40- downloadPath: downloadPath,39+ downloadPath: downloadPath,
41- eventHandler: handler,40+ eventHandler: handler,
42- prepareDownloadUC: prepareDownloadUC,41+ preparer: preparer,
43 }42 }
44 }43 }
45 44
@@ -57,7 +56,7 @@ func (h *DownloadHandler) HandleDownload(ctx context.Context, download *webkit.D
57 h.mu.RLock()56 h.mu.RLock()
58 downloadPath := h.downloadPath57 downloadPath := h.downloadPath
59 eventHandler := h.eventHandler58 eventHandler := h.eventHandler
60- prepareDownloadUC := h.prepareDownloadUC59+ preparer := h.preparer
61 h.mu.RUnlock()60 h.mu.RUnlock()
62 61
63 // Track download state in a dedicated struct to ensure proper isolation.62 // Track download state in a dedicated struct to ensure proper isolation.
@@ -80,8 +79,8 @@ func (h *DownloadHandler) HandleDownload(ctx context.Context, download *webkit.D
80 response = &uriResponseAdapter{resp: resp}79 response = &uriResponseAdapter{resp: resp}
81 }80 }
82 81
83- // Use PrepareDownloadUseCase to resolve filename82+ // Use DownloadPreparer to resolve filename
84- output := prepareDownloadUC.Execute(ctx, usecase.PrepareDownloadInput{83+ output := preparer.Execute(ctx, port.DownloadPrepareInput{
85 SuggestedFilename: suggestedFilename,84 SuggestedFilename: suggestedFilename,
86 Response: response,85 Response: response,
87 DownloadDir: downloadPath,86 DownloadDir: downloadPath,
M internal/infrastructure/webkit/engine.go +8 -13
@@ -5,7 +5,6 @@ import (
55 "fmt"
66
77 "github.com/bnema/dumber/internal/application/port"
8- "github.com/bnema/dumber/internal/application/usecase"
98 "github.com/bnema/dumber/internal/infrastructure/config"
109 "github.com/bnema/dumber/internal/infrastructure/filtering"
1110 "github.com/rs/zerolog"
@@ -27,7 +26,7 @@ type Engine struct {
2726
2827 // Handler registrars - injected at construction to avoid import cycles.
2928 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error
30- accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler any) error
29+ accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler port.AccentKeyHandler) error
3130 }
3231
3332 // Compile-time check that Engine implements port.Engine.
@@ -112,7 +111,7 @@ func (e *Engine) RegisterHandlers(ctx context.Context, deps port.HandlerDependen
112111 }
113112
114113 // RegisterAccentHandlers registers accent/dead-key input handlers.
115-func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler any) error {
114+func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler port.AccentKeyHandler) error {
116115 if e.messageRouter == nil {
117116 return fmt.Errorf("message router not initialized")
118117 }
@@ -123,15 +122,11 @@ func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler any) error
123122 }
124123
125124 // ConfigureDownloads sets up download handling.
126-func (e *Engine) ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler port.DownloadEventHandler, prepareUC any) error {
125+func (e *Engine) ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler port.DownloadEventHandler, preparer port.DownloadPreparer) error {
127126 if e.wkCtx == nil {
128127 return fmt.Errorf("webkit context not initialized")
129128 }
130- uc, ok := prepareUC.(*usecase.PrepareDownloadUseCase)
131- if !ok {
132- return fmt.Errorf("prepareUC does not implement PrepareDownloadUseCase")
133- }
134- handler := NewDownloadHandler(downloadPath, eventHandler, uc)
129+ handler := NewDownloadHandler(downloadPath, eventHandler, preparer)
135130 e.wkCtx.SetDownloadHandler(ctx, handler)
136131 return nil
137132 }
@@ -156,13 +151,13 @@ func (e *Engine) UpdateAppearance(_ context.Context, r, g, b, alpha float64) err
156151 }
157152
158153 // UpdateSettings applies runtime config changes to engine settings.
159-func (e *Engine) UpdateSettings(ctx context.Context, cfg any) error {
160- configCfg, ok := cfg.(*config.Config)
154+func (e *Engine) UpdateSettings(ctx context.Context, update port.EngineSettingsUpdate) error {
155+ cfg, ok := update.Raw.(*config.Config)
161156 if !ok {
162- return fmt.Errorf("expected *config.Config, got %T", cfg)
157+ return fmt.Errorf("UpdateSettings: expected *config.Config, got %T", update.Raw)
163158 }
164159 if e.settings != nil {
165- e.settings.UpdateFromConfig(ctx, configCfg)
160+ e.settings.UpdateFromConfig(ctx, cfg)
166161 }
167162 return nil
168163 }
@@ -5,7 +5,6 @@ import (
5 "fmt"5 "fmt"
6 6
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8- "github.com/bnema/dumber/internal/application/usecase"
9 "github.com/bnema/dumber/internal/infrastructure/config"8 "github.com/bnema/dumber/internal/infrastructure/config"
10 "github.com/bnema/dumber/internal/infrastructure/filtering"9 "github.com/bnema/dumber/internal/infrastructure/filtering"
11 "github.com/rs/zerolog"10 "github.com/rs/zerolog"
@@ -27,7 +26,7 @@ type Engine struct {
27 26
28 // Handler registrars - injected at construction to avoid import cycles.27 // Handler registrars - injected at construction to avoid import cycles.
29 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error28 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error
30- accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler any) error29+ accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler port.AccentKeyHandler) error
31 }30 }
32 31
33 // Compile-time check that Engine implements port.Engine.32 // Compile-time check that Engine implements port.Engine.
@@ -112,7 +111,7 @@ func (e *Engine) RegisterHandlers(ctx context.Context, deps port.HandlerDependen
112 }111 }
113 112
114 // RegisterAccentHandlers registers accent/dead-key input handlers.113 // RegisterAccentHandlers registers accent/dead-key input handlers.
115-func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler any) error {114+func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler port.AccentKeyHandler) error {
116 if e.messageRouter == nil {115 if e.messageRouter == nil {
117 return fmt.Errorf("message router not initialized")116 return fmt.Errorf("message router not initialized")
118 }117 }
@@ -123,15 +122,11 @@ func (e *Engine) RegisterAccentHandlers(ctx context.Context, handler any) error
123 }122 }
124 123
125 // ConfigureDownloads sets up download handling.124 // ConfigureDownloads sets up download handling.
126-func (e *Engine) ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler port.DownloadEventHandler, prepareUC any) error {125+func (e *Engine) ConfigureDownloads(ctx context.Context, downloadPath string, eventHandler port.DownloadEventHandler, preparer port.DownloadPreparer) error {
127 if e.wkCtx == nil {126 if e.wkCtx == nil {
128 return fmt.Errorf("webkit context not initialized")127 return fmt.Errorf("webkit context not initialized")
129 }128 }
130- uc, ok := prepareUC.(*usecase.PrepareDownloadUseCase)129+ handler := NewDownloadHandler(downloadPath, eventHandler, preparer)
131- if !ok {
132- return fmt.Errorf("prepareUC does not implement PrepareDownloadUseCase")
133- }
134- handler := NewDownloadHandler(downloadPath, eventHandler, uc)
135 e.wkCtx.SetDownloadHandler(ctx, handler)130 e.wkCtx.SetDownloadHandler(ctx, handler)
136 return nil131 return nil
137 }132 }
@@ -156,13 +151,13 @@ func (e *Engine) UpdateAppearance(_ context.Context, r, g, b, alpha float64) err
156 }151 }
157 152
158 // UpdateSettings applies runtime config changes to engine settings.153 // UpdateSettings applies runtime config changes to engine settings.
159-func (e *Engine) UpdateSettings(ctx context.Context, cfg any) error {154+func (e *Engine) UpdateSettings(ctx context.Context, update port.EngineSettingsUpdate) error {
160- configCfg, ok := cfg.(*config.Config)155+ cfg, ok := update.Raw.(*config.Config)
161 if !ok {156 if !ok {
162- return fmt.Errorf("expected *config.Config, got %T", cfg)157+ return fmt.Errorf("UpdateSettings: expected *config.Config, got %T", update.Raw)
163 }158 }
164 if e.settings != nil {159 if e.settings != nil {
165- e.settings.UpdateFromConfig(ctx, configCfg)160+ e.settings.UpdateFromConfig(ctx, cfg)
166 }161 }
167 return nil162 return nil
168 }163 }
M internal/infrastructure/webkit/engine_init.go +1 -1
@@ -27,7 +27,7 @@ func NewEngine(
2727 colorResolver port.ColorSchemeResolver,
2828 logger zerolog.Logger,
2929 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error,
30- accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler any) error,
30+ accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler port.AccentKeyHandler) error,
3131 ) (*Engine, error) {
3232 // --- Hardware survey and performance profile resolution ---
3333 perfSettings := engineSurveyHardwareAndResolveProfile(ctx, cfg, logger)
@@ -27,7 +27,7 @@ func NewEngine(
27 colorResolver port.ColorSchemeResolver,27 colorResolver port.ColorSchemeResolver,
28 logger zerolog.Logger,28 logger zerolog.Logger,
29 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error,29 handlerRegistrar func(ctx context.Context, router *MessageRouter, deps port.HandlerDependencies) error,
30- accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler any) error,30+ accentHandlerRegistrar func(ctx context.Context, router *MessageRouter, handler port.AccentKeyHandler) error,
31 ) (*Engine, error) {31 ) (*Engine, error) {
32 // --- Hardware survey and performance profile resolution ---32 // --- Hardware survey and performance profile resolution ---
33 perfSettings := engineSurveyHardwareAndResolveProfile(ctx, cfg, logger)33 perfSettings := engineSurveyHardwareAndResolveProfile(ctx, cfg, logger)
M internal/ui/app.go +16 -7
@@ -181,12 +181,10 @@ func New(deps *Dependencies) (*App, error) {
181181 }
182182 // Register message handlers through the engine.
183183 if err := deps.Engine.RegisterHandlers(ctx, port.HandlerDependencies{
184- HistoryUC: deps.HistoryUC,
185- FavoritesUC: deps.FavoritesUC,
186- Clipboard: deps.Clipboard,
187- ConfigGetter: func() any {
188- return config.Get()
189- },
184+ HistoryUC: deps.HistoryUC,
185+ FavoritesUC: deps.FavoritesUC,
186+ Clipboard: deps.Clipboard,
187+ AutoCopyConfig: &configAutoCopyAdapter{},
190188 OnClipboardCopied: func(textLen int) {
191189 cb := glib.SourceFunc(func(_ uintptr) bool {
192190 if app.appToaster != nil {
@@ -621,6 +619,17 @@ func (d *downloadEventAdapter) OnDownloadEvent(ctx context.Context, event port.D
621619 glib.IdleAdd(&cb, 0)
622620 }
623621
622+// configAutoCopyAdapter implements port.AutoCopyConfig by reading the live config.
623+type configAutoCopyAdapter struct{}
624+
625+func (a *configAutoCopyAdapter) IsAutoCopyEnabled() bool {
626+ cfg := config.Get()
627+ if cfg == nil {
628+ return false
629+ }
630+ return cfg.Clipboard.AutoCopyOnSelection
631+}
632+
624633 func (a *App) initKeyboardHandler(ctx context.Context) {
625634 log := logging.FromContext(ctx)
626635
@@ -3063,7 +3072,7 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
30633072 }
30643073
30653074 if a.engine != nil {
3066- _ = a.engine.UpdateSettings(ctx, cfg)
3075+ _ = a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{Raw: cfg})
30673076 }
30683077
30693078 // Apply settings to existing webviews via coordinator
@@ -181,12 +181,10 @@ func New(deps *Dependencies) (*App, error) {
181 }181 }
182 // Register message handlers through the engine.182 // Register message handlers through the engine.
183 if err := deps.Engine.RegisterHandlers(ctx, port.HandlerDependencies{183 if err := deps.Engine.RegisterHandlers(ctx, port.HandlerDependencies{
184- HistoryUC: deps.HistoryUC,184+ HistoryUC: deps.HistoryUC,
185- FavoritesUC: deps.FavoritesUC,185+ FavoritesUC: deps.FavoritesUC,
186- Clipboard: deps.Clipboard,186+ Clipboard: deps.Clipboard,
187- ConfigGetter: func() any {187+ AutoCopyConfig: &configAutoCopyAdapter{},
188- return config.Get()
189- },
190 OnClipboardCopied: func(textLen int) {188 OnClipboardCopied: func(textLen int) {
191 cb := glib.SourceFunc(func(_ uintptr) bool {189 cb := glib.SourceFunc(func(_ uintptr) bool {
192 if app.appToaster != nil {190 if app.appToaster != nil {
@@ -621,6 +619,17 @@ func (d *downloadEventAdapter) OnDownloadEvent(ctx context.Context, event port.D
621 glib.IdleAdd(&cb, 0)619 glib.IdleAdd(&cb, 0)
622 }620 }
623 621
622+// configAutoCopyAdapter implements port.AutoCopyConfig by reading the live config.
623+type configAutoCopyAdapter struct{}
624+
625+func (a *configAutoCopyAdapter) IsAutoCopyEnabled() bool {
626+ cfg := config.Get()
627+ if cfg == nil {
628+ return false
629+ }
630+ return cfg.Clipboard.AutoCopyOnSelection
631+}
632+
624 func (a *App) initKeyboardHandler(ctx context.Context) {633 func (a *App) initKeyboardHandler(ctx context.Context) {
625 log := logging.FromContext(ctx)634 log := logging.FromContext(ctx)
626 635
@@ -3063,7 +3072,7 @@ func (a *App) applyAppearanceConfig(ctx context.Context, cfg *config.Config) {
3063 }3072 }
3064 3073
3065 if a.engine != nil {3074 if a.engine != nil {
3066- _ = a.engine.UpdateSettings(ctx, cfg)3075+ _ = a.engine.UpdateSettings(ctx, port.EngineSettingsUpdate{Raw: cfg})
3067 }3076 }
3068 3077
3069 // Apply settings to existing webviews via coordinator3078 // Apply settings to existing webviews via coordinator

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