v0.21.0 73bf644

webkit_stack.go RawHistoryFolder

1package bootstrap
2
3import (
4	"context"
5	"path/filepath"
6
7	"github.com/bnema/dumber/assets"
8	"github.com/bnema/dumber/internal/application/port"
9	"github.com/bnema/dumber/internal/application/usecase"
10	"github.com/bnema/dumber/internal/infrastructure/config"
11	"github.com/bnema/dumber/internal/infrastructure/env"
12	"github.com/bnema/dumber/internal/infrastructure/filtering"
13	"github.com/bnema/dumber/internal/infrastructure/webkit"
14	"github.com/bnema/dumber/internal/ui/theme"
15	"github.com/rs/zerolog"
16)
17
18type WebKitStack struct {
19	Context       *webkit.WebKitContext
20	Settings      *webkit.SettingsManager
21	Injector      *webkit.ContentInjector
22	MessageRouter *webkit.MessageRouter
23	Pool          *webkit.WebViewPool
24	FilterManager *filtering.Manager
25}
26
27func BuildWebKitStack(
28	ctx context.Context,
29	cfg *config.Config,
30	dataDir string,
31	cacheDir string,
32	themeManager *theme.Manager,
33	logger zerolog.Logger,
34) WebKitStack {
35	// CRITICAL: Configure rendering environment BEFORE WebKit/GTK initialization.
36	// Environment variables must be set before GTK/WebKit/GStreamer initializes.
37	renderEnv := env.NewManager()
38	gpuVendor := renderEnv.DetectGPUVendor(ctx)
39	renderSettings := port.RenderingEnvSettings{
40		// GStreamer settings
41		ForceVSync:          cfg.Media.ForceVSync,
42		GLRenderingMode:     string(cfg.Media.GLRenderingMode),
43		GStreamerDebugLevel: cfg.Media.GStreamerDebugLevel,
44		VideoBufferSizeMB:   cfg.Media.VideoBufferSizeMB,
45		QueueBufferTimeSec:  cfg.Media.QueueBufferTimeSec,
46
47		// WebKit compositor settings
48		DisableDMABufRenderer:  cfg.Rendering.DisableDMABufRenderer,
49		ForceCompositingMode:   cfg.Rendering.ForceCompositingMode,
50		DisableCompositingMode: cfg.Rendering.DisableCompositingMode,
51
52		// GTK/GSK settings
53		GSKRenderer:    string(cfg.Rendering.GSKRenderer),
54		DisableMipmaps: cfg.Rendering.DisableMipmaps,
55		PreferGL:       cfg.Rendering.PreferGL,
56
57		// Debug settings
58		ShowFPS:      cfg.Rendering.ShowFPS,
59		SampleMemory: cfg.Rendering.SampleMemory,
60		DebugFrames:  cfg.Rendering.DebugFrames,
61	}
62	if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil {
63		logger.Warn().Err(err).Msg("failed to apply rendering environment")
64	}
65	logger.Info().
66		Str("gpu", string(gpuVendor)).
67		Interface("vars", renderEnv.GetAppliedVars()).
68		Msg("rendering environment configured")
69
70	wkCtx, err := webkit.NewWebKitContext(ctx, dataDir, cacheDir)
71	if err != nil {
72		logger.Fatal().Err(err).Msg("failed to initialize WebKit context")
73	}
74
75	filterStoreDir := filepath.Join(dataDir, "filters", "store")
76	filterJSONDir := filepath.Join(dataDir, "filters", "json")
77	filterManager, err := filtering.NewManager(filtering.ManagerConfig{
78		StoreDir:   filterStoreDir,
79		JSONDir:    filterJSONDir,
80		Enabled:    cfg.ContentFiltering.Enabled,
81		AutoUpdate: cfg.ContentFiltering.AutoUpdate,
82	})
83	if err != nil {
84		logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering")
85	} else {
86		if err := filterManager.Initialize(ctx); err != nil {
87			logger.Warn().Err(err).Msg("failed to initialize filters, will load async")
88		}
89	}
90
91	schemeHandler := webkit.NewDumbSchemeHandler(ctx)
92	schemeHandler.SetAssets(assets.WebUIAssets)
93	schemeHandler.RegisterWithContext(wkCtx)
94
95	settings := webkit.NewSettingsManager(ctx, cfg)
96	injector := webkit.NewContentInjector(themeManager.PrefersDark())
97
98	prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
99	themeCSSText := themeManager.GetWebUIThemeCSS()
100	if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil {
101		logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS")
102	}
103
104	messageRouter := webkit.NewMessageRouter(ctx)
105	poolCfg := webkit.DefaultPoolConfig()
106	// Override prewarm count from config if set
107	if cfg.Performance.WebViewPoolPrewarmCount > 0 {
108		poolCfg.PrewarmCount = cfg.Performance.WebViewPoolPrewarmCount
109	}
110	pool := webkit.NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter)
111
112	if filterManager != nil {
113		pool.SetFilterApplier(filterManager)
114	}
115
116	return WebKitStack{
117		Context:       wkCtx,
118		Settings:      settings,
119		Injector:      injector,
120		MessageRouter: messageRouter,
121		Pool:          pool,
122		FilterManager: filterManager,
123	}
124}

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