detached 324f8f3

feat(webkit): implement NewEngine constructor absorbing bootstrap logic

commit
324f8f31a34445fbec3134af7637d50e39203d0c
author
brice · · 2026-03-15 16:25 UTC
parent
f7e5518
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +272 -0
A internal/infrastructure/webkit/engine_init.go +272 -0
@@ -0,0 +1,272 @@
1+package webkit
2+
3+import (
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/logging"
14+ "github.com/bnema/dumber/internal/ui/theme"
15+ "github.com/jwijenbergh/puregotk/v4/gdk"
16+ "github.com/rs/zerolog"
17+)
18+
19+// NewEngine creates and initializes all WebKit engine components.
20+// It absorbs the initialization logic previously found in bootstrap.BuildWebKitStack.
21+func NewEngine(
22+ ctx context.Context,
23+ cfg *config.Config,
24+ opts port.EngineOptions,
25+ wkCfg WebKitEngineConfig,
26+ themeManager *theme.Manager,
27+ colorResolver port.ColorSchemeResolver,
28+ logger zerolog.Logger,
29+) (*Engine, error) {
30+ // --- Hardware survey and performance profile resolution ---
31+ perfSettings := engineSurveyHardwareAndResolveProfile(ctx, cfg, logger)
32+
33+ // --- Configure rendering environment (must be before GTK/WebKit init) ---
34+ engineConfigureRenderingEnvironment(ctx, cfg, wkCfg, &perfSettings, logger)
35+ logging.Trace().Mark("render_env")
36+
37+ // --- Build WebKitContextOptions from opts + wkCfg + perfSettings ---
38+ wkOpts := engineBuildContextOptions(opts, wkCfg, &perfSettings)
39+ logger.Info().
40+ Str("cookie_policy", string(wkOpts.CookiePolicy)).
41+ Bool("itp_enabled", wkOpts.ITPEnabled).
42+ Msg("webkit privacy configuration")
43+
44+ wkCtx, err := NewWebKitContextWithOptions(ctx, wkOpts)
45+ if err != nil {
46+ return nil, err
47+ }
48+ logging.Trace().Mark("webkit_context")
49+
50+ // --- Filter manager ---
51+ filterManager := engineInitFilterManager(ctx, cfg, opts.DataDir, logger)
52+
53+ // --- Scheme handler ---
54+ schemeHandler := NewDumbSchemeHandler(ctx)
55+ schemeHandler.SetAssets(assets.WebUIAssets)
56+ schemeHandler.RegisterWithContext(wkCtx)
57+
58+ // --- Settings, injector, message router ---
59+ settings := NewSettingsManager(ctx, cfg)
60+ injector := NewContentInjector(colorResolver)
61+
62+ injector.SetAutoCopyConfigGetter(func() bool {
63+ return config.Get().Clipboard.AutoCopyOnSelection
64+ })
65+
66+ prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
67+ themeCSSText := themeManager.GetWebUIThemeCSS()
68+ if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil {
69+ logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS")
70+ }
71+
72+ messageRouter := NewMessageRouter(ctx)
73+ logging.Trace().Mark("settings_manager")
74+
75+ // --- WebView pool ---
76+ poolCfg := DefaultPoolConfig()
77+ if perfSettings.WebViewPoolPrewarmCount > 0 {
78+ poolCfg.PrewarmCount = perfSettings.WebViewPoolPrewarmCount
79+ }
80+ pool := NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter)
81+
82+ bgR, bgG, bgB, bgA := themeManager.GetBackgroundRGBA()
83+ pool.SetBackgroundColor(bgR, bgG, bgB, bgA)
84+
85+ if filterManager != nil {
86+ pool.SetFilterApplier(filterManager)
87+ }
88+
89+ if gdk.DisplayGetDefault() != nil {
90+ if err := pool.PrewarmFirst(ctx); err != nil {
91+ logger.Warn().Err(err).Msg("failed to prewarm first webview, first tab may be slower")
92+ }
93+ } else {
94+ logger.Warn().Msg("skipping first webview prewarm: no GDK display available yet")
95+ }
96+ logging.Trace().Mark("pool_prewarm_first")
97+
98+ // --- WebView factory ---
99+ factory := NewWebViewFactory(wkCtx, settings, pool, injector, messageRouter)
100+ factory.SetBackgroundColor(bgR, bgG, bgB, bgA)
101+ if filterManager != nil {
102+ factory.SetFilterApplier(filterManager)
103+ }
104+
105+ // --- Assemble Engine ---
106+ engine := &Engine{
107+ wkCtx: wkCtx,
108+ settings: settings,
109+ injector: injector,
110+ messageRouter: messageRouter,
111+ pool: pool,
112+ factory: factory,
113+ filterManager: filterManager,
114+ schemeHandler: schemeHandler,
115+ schemePath: "dumb://",
116+ }
117+
118+ return engine, nil
119+}
120+
121+// engineSurveyHardwareAndResolveProfile surveys hardware and resolves the performance profile.
122+func engineSurveyHardwareAndResolveProfile(ctx context.Context, cfg *config.Config, logger zerolog.Logger) config.ResolvedPerformanceSettings {
123+ hwSurveyor := env.NewHardwareSurveyor()
124+ hwInfo := hwSurveyor.Survey(ctx)
125+ logging.Trace().Mark("hardware_survey")
126+ logger.Info().
127+ Int("cpu_cores", hwInfo.CPUCores).
128+ Int("cpu_threads", hwInfo.CPUThreads).
129+ Uint64("ram_mb", hwInfo.TotalRAM/(1024*1024)).
130+ Str("gpu_vendor", string(hwInfo.GPUVendor)).
131+ Uint64("vram_mb", hwInfo.VRAM/(1024*1024)).
132+ Msg("hardware survey completed")
133+
134+ perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo)
135+ logging.Trace().Mark("performance_profile")
136+ logger.Info().
137+ Str("profile", string(cfg.Performance.Profile)).
138+ Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads).
139+ Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads).
140+ Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount).
141+ Msg("resolved performance profile")
142+
143+ return perfSettings
144+}
145+
146+// engineConfigureRenderingEnvironment sets up the rendering environment.
147+// Environment variables must be set before GTK/WebKit/GStreamer initializes.
148+func engineConfigureRenderingEnvironment(
149+ ctx context.Context,
150+ cfg *config.Config,
151+ wkCfg WebKitEngineConfig,
152+ perfSettings *config.ResolvedPerformanceSettings,
153+ logger zerolog.Logger,
154+) {
155+ // Install GLib log handler FIRST if configured (must be before any GTK/GLib calls).
156+ if cfg.Logging.CaptureGTKLogs {
157+ enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace"
158+ logging.InstallGLibLogHandler(ctx, logger, enableDebug)
159+ }
160+
161+ renderEnv := env.NewManager()
162+ gpuVendor := renderEnv.DetectGPUVendor(ctx)
163+ renderSettings := env.RenderingSettings{
164+ // GStreamer settings
165+ ForceVSync: wkCfg.ForceVSync,
166+ GLRenderingMode: wkCfg.GLRenderingMode,
167+ GStreamerDebugLevel: wkCfg.GStreamerDebugLevel,
168+ // WebKit compositor settings
169+ DisableDMABufRenderer: wkCfg.DisableDMABufRenderer,
170+ ForceCompositingMode: wkCfg.ForceCompositingMode,
171+ DisableCompositingMode: wkCfg.DisableCompositingMode,
172+ // GTK/GSK settings
173+ GSKRenderer: wkCfg.GSKRenderer,
174+ DisableMipmaps: wkCfg.DisableMipmaps,
175+ PreferGL: wkCfg.PreferGL,
176+ // Debug settings
177+ ShowFPS: wkCfg.ShowFPS,
178+ SampleMemory: wkCfg.SampleMemory,
179+ DebugFrames: wkCfg.DebugFrames,
180+ // Skia rendering thread settings (from resolved performance profile)
181+ SkiaCPUPaintingThreads: perfSettings.SkiaCPUPaintingThreads,
182+ SkiaGPUPaintingThreads: perfSettings.SkiaGPUPaintingThreads,
183+ SkiaEnableCPURendering: perfSettings.SkiaEnableCPURendering,
184+ }
185+ if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil {
186+ logger.Warn().Err(err).Msg("failed to apply rendering environment")
187+ }
188+ logger.Info().
189+ Str("gpu", string(gpuVendor)).
190+ Interface("vars", renderEnv.GetAppliedVars()).
191+ Msg("rendering environment configured")
192+}
193+
194+// engineBuildContextOptions builds port.WebKitContextOptions from EngineOptions, wkCfg and perfSettings.
195+func engineBuildContextOptions(
196+ opts port.EngineOptions,
197+ wkCfg WebKitEngineConfig,
198+ perfSettings *config.ResolvedPerformanceSettings,
199+) port.WebKitContextOptions {
200+ cookiePolicy := port.WebKitCookiePolicyNoThirdParty
201+ if opts.CookiePolicy != "" {
202+ cookiePolicy = port.WebKitCookiePolicy(opts.CookiePolicy)
203+ }
204+
205+ wkOpts := port.WebKitContextOptions{
206+ DataDir: opts.DataDir,
207+ CacheDir: opts.CacheDir,
208+ CookiePolicy: cookiePolicy,
209+ ITPEnabled: wkCfg.ITPEnabled,
210+ }
211+
212+ if opts.WebProcessMemory != nil {
213+ wkOpts.WebProcessMemory = opts.WebProcessMemory
214+ } else if engineHasWebProcessMemoryConfig(perfSettings) {
215+ wkOpts.WebProcessMemory = &port.MemoryPressureConfig{
216+ MemoryLimitMB: perfSettings.WebProcessMemoryLimitMB,
217+ PollIntervalSec: perfSettings.WebProcessMemoryPollIntervalSec,
218+ ConservativeThreshold: perfSettings.WebProcessMemoryConservativeThreshold,
219+ StrictThreshold: perfSettings.WebProcessMemoryStrictThreshold,
220+ }
221+ }
222+
223+ if opts.NetworkProcessMemory != nil {
224+ wkOpts.NetworkProcessMemory = opts.NetworkProcessMemory
225+ } else if engineHasNetworkProcessMemoryConfig(perfSettings) {
226+ wkOpts.NetworkProcessMemory = &port.MemoryPressureConfig{
227+ MemoryLimitMB: perfSettings.NetworkProcessMemoryLimitMB,
228+ PollIntervalSec: perfSettings.NetworkProcessMemoryPollIntervalSec,
229+ ConservativeThreshold: perfSettings.NetworkProcessMemoryConservativeThreshold,
230+ StrictThreshold: perfSettings.NetworkProcessMemoryStrictThreshold,
231+ }
232+ }
233+
234+ return wkOpts
235+}
236+
237+// engineInitFilterManager creates and initializes the content filter manager.
238+func engineInitFilterManager(ctx context.Context, cfg *config.Config, dataDir string, logger zerolog.Logger) *filtering.Manager {
239+ filterStoreDir := filepath.Join(dataDir, "filters", "store")
240+ filterJSONDir := filepath.Join(dataDir, "filters", "json")
241+ filterManager, err := filtering.NewManager(filtering.ManagerConfig{
242+ StoreDir: filterStoreDir,
243+ JSONDir: filterJSONDir,
244+ Enabled: cfg.ContentFiltering.Enabled,
245+ AutoUpdate: cfg.ContentFiltering.AutoUpdate,
246+ })
247+ if err != nil {
248+ logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering")
249+ return nil
250+ }
251+ if err := filterManager.Initialize(ctx); err != nil {
252+ logger.Warn().Err(err).Msg("failed to initialize filters, will load async")
253+ }
254+ logging.Trace().Mark("filter_manager")
255+ return filterManager
256+}
257+
258+// engineHasWebProcessMemoryConfig returns true if any web process memory setting is configured.
259+func engineHasWebProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool {
260+ return p.WebProcessMemoryLimitMB > 0 ||
261+ p.WebProcessMemoryPollIntervalSec > 0 ||
262+ p.WebProcessMemoryConservativeThreshold > 0 ||
263+ p.WebProcessMemoryStrictThreshold > 0
264+}
265+
266+// engineHasNetworkProcessMemoryConfig returns true if any network process memory setting is configured.
267+func engineHasNetworkProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool {
268+ return p.NetworkProcessMemoryLimitMB > 0 ||
269+ p.NetworkProcessMemoryPollIntervalSec > 0 ||
270+ p.NetworkProcessMemoryConservativeThreshold > 0 ||
271+ p.NetworkProcessMemoryStrictThreshold > 0
272+}
@@ -0,0 +1,272 @@
1+package webkit
2+
3+import (
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/logging"
14+ "github.com/bnema/dumber/internal/ui/theme"
15+ "github.com/jwijenbergh/puregotk/v4/gdk"
16+ "github.com/rs/zerolog"
17+)
18+
19+// NewEngine creates and initializes all WebKit engine components.
20+// It absorbs the initialization logic previously found in bootstrap.BuildWebKitStack.
21+func NewEngine(
22+ ctx context.Context,
23+ cfg *config.Config,
24+ opts port.EngineOptions,
25+ wkCfg WebKitEngineConfig,
26+ themeManager *theme.Manager,
27+ colorResolver port.ColorSchemeResolver,
28+ logger zerolog.Logger,
29+) (*Engine, error) {
30+ // --- Hardware survey and performance profile resolution ---
31+ perfSettings := engineSurveyHardwareAndResolveProfile(ctx, cfg, logger)
32+
33+ // --- Configure rendering environment (must be before GTK/WebKit init) ---
34+ engineConfigureRenderingEnvironment(ctx, cfg, wkCfg, &perfSettings, logger)
35+ logging.Trace().Mark("render_env")
36+
37+ // --- Build WebKitContextOptions from opts + wkCfg + perfSettings ---
38+ wkOpts := engineBuildContextOptions(opts, wkCfg, &perfSettings)
39+ logger.Info().
40+ Str("cookie_policy", string(wkOpts.CookiePolicy)).
41+ Bool("itp_enabled", wkOpts.ITPEnabled).
42+ Msg("webkit privacy configuration")
43+
44+ wkCtx, err := NewWebKitContextWithOptions(ctx, wkOpts)
45+ if err != nil {
46+ return nil, err
47+ }
48+ logging.Trace().Mark("webkit_context")
49+
50+ // --- Filter manager ---
51+ filterManager := engineInitFilterManager(ctx, cfg, opts.DataDir, logger)
52+
53+ // --- Scheme handler ---
54+ schemeHandler := NewDumbSchemeHandler(ctx)
55+ schemeHandler.SetAssets(assets.WebUIAssets)
56+ schemeHandler.RegisterWithContext(wkCtx)
57+
58+ // --- Settings, injector, message router ---
59+ settings := NewSettingsManager(ctx, cfg)
60+ injector := NewContentInjector(colorResolver)
61+
62+ injector.SetAutoCopyConfigGetter(func() bool {
63+ return config.Get().Clipboard.AutoCopyOnSelection
64+ })
65+
66+ prepareThemeUC := usecase.NewPrepareWebUIThemeUseCase(injector)
67+ themeCSSText := themeManager.GetWebUIThemeCSS()
68+ if err := prepareThemeUC.Execute(ctx, usecase.PrepareWebUIThemeInput{CSSVars: themeCSSText}); err != nil {
69+ logger.Warn().Err(err).Msg("failed to prepare WebUI theme CSS")
70+ }
71+
72+ messageRouter := NewMessageRouter(ctx)
73+ logging.Trace().Mark("settings_manager")
74+
75+ // --- WebView pool ---
76+ poolCfg := DefaultPoolConfig()
77+ if perfSettings.WebViewPoolPrewarmCount > 0 {
78+ poolCfg.PrewarmCount = perfSettings.WebViewPoolPrewarmCount
79+ }
80+ pool := NewWebViewPool(ctx, wkCtx, settings, poolCfg, injector, messageRouter)
81+
82+ bgR, bgG, bgB, bgA := themeManager.GetBackgroundRGBA()
83+ pool.SetBackgroundColor(bgR, bgG, bgB, bgA)
84+
85+ if filterManager != nil {
86+ pool.SetFilterApplier(filterManager)
87+ }
88+
89+ if gdk.DisplayGetDefault() != nil {
90+ if err := pool.PrewarmFirst(ctx); err != nil {
91+ logger.Warn().Err(err).Msg("failed to prewarm first webview, first tab may be slower")
92+ }
93+ } else {
94+ logger.Warn().Msg("skipping first webview prewarm: no GDK display available yet")
95+ }
96+ logging.Trace().Mark("pool_prewarm_first")
97+
98+ // --- WebView factory ---
99+ factory := NewWebViewFactory(wkCtx, settings, pool, injector, messageRouter)
100+ factory.SetBackgroundColor(bgR, bgG, bgB, bgA)
101+ if filterManager != nil {
102+ factory.SetFilterApplier(filterManager)
103+ }
104+
105+ // --- Assemble Engine ---
106+ engine := &Engine{
107+ wkCtx: wkCtx,
108+ settings: settings,
109+ injector: injector,
110+ messageRouter: messageRouter,
111+ pool: pool,
112+ factory: factory,
113+ filterManager: filterManager,
114+ schemeHandler: schemeHandler,
115+ schemePath: "dumb://",
116+ }
117+
118+ return engine, nil
119+}
120+
121+// engineSurveyHardwareAndResolveProfile surveys hardware and resolves the performance profile.
122+func engineSurveyHardwareAndResolveProfile(ctx context.Context, cfg *config.Config, logger zerolog.Logger) config.ResolvedPerformanceSettings {
123+ hwSurveyor := env.NewHardwareSurveyor()
124+ hwInfo := hwSurveyor.Survey(ctx)
125+ logging.Trace().Mark("hardware_survey")
126+ logger.Info().
127+ Int("cpu_cores", hwInfo.CPUCores).
128+ Int("cpu_threads", hwInfo.CPUThreads).
129+ Uint64("ram_mb", hwInfo.TotalRAM/(1024*1024)).
130+ Str("gpu_vendor", string(hwInfo.GPUVendor)).
131+ Uint64("vram_mb", hwInfo.VRAM/(1024*1024)).
132+ Msg("hardware survey completed")
133+
134+ perfSettings := config.ResolvePerformanceProfile(&cfg.Performance, &hwInfo)
135+ logging.Trace().Mark("performance_profile")
136+ logger.Info().
137+ Str("profile", string(cfg.Performance.Profile)).
138+ Int("skia_cpu_threads", perfSettings.SkiaCPUPaintingThreads).
139+ Int("skia_gpu_threads", perfSettings.SkiaGPUPaintingThreads).
140+ Int("webview_pool_prewarm", perfSettings.WebViewPoolPrewarmCount).
141+ Msg("resolved performance profile")
142+
143+ return perfSettings
144+}
145+
146+// engineConfigureRenderingEnvironment sets up the rendering environment.
147+// Environment variables must be set before GTK/WebKit/GStreamer initializes.
148+func engineConfigureRenderingEnvironment(
149+ ctx context.Context,
150+ cfg *config.Config,
151+ wkCfg WebKitEngineConfig,
152+ perfSettings *config.ResolvedPerformanceSettings,
153+ logger zerolog.Logger,
154+) {
155+ // Install GLib log handler FIRST if configured (must be before any GTK/GLib calls).
156+ if cfg.Logging.CaptureGTKLogs {
157+ enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace"
158+ logging.InstallGLibLogHandler(ctx, logger, enableDebug)
159+ }
160+
161+ renderEnv := env.NewManager()
162+ gpuVendor := renderEnv.DetectGPUVendor(ctx)
163+ renderSettings := env.RenderingSettings{
164+ // GStreamer settings
165+ ForceVSync: wkCfg.ForceVSync,
166+ GLRenderingMode: wkCfg.GLRenderingMode,
167+ GStreamerDebugLevel: wkCfg.GStreamerDebugLevel,
168+ // WebKit compositor settings
169+ DisableDMABufRenderer: wkCfg.DisableDMABufRenderer,
170+ ForceCompositingMode: wkCfg.ForceCompositingMode,
171+ DisableCompositingMode: wkCfg.DisableCompositingMode,
172+ // GTK/GSK settings
173+ GSKRenderer: wkCfg.GSKRenderer,
174+ DisableMipmaps: wkCfg.DisableMipmaps,
175+ PreferGL: wkCfg.PreferGL,
176+ // Debug settings
177+ ShowFPS: wkCfg.ShowFPS,
178+ SampleMemory: wkCfg.SampleMemory,
179+ DebugFrames: wkCfg.DebugFrames,
180+ // Skia rendering thread settings (from resolved performance profile)
181+ SkiaCPUPaintingThreads: perfSettings.SkiaCPUPaintingThreads,
182+ SkiaGPUPaintingThreads: perfSettings.SkiaGPUPaintingThreads,
183+ SkiaEnableCPURendering: perfSettings.SkiaEnableCPURendering,
184+ }
185+ if err := renderEnv.ApplyEnvironment(ctx, renderSettings); err != nil {
186+ logger.Warn().Err(err).Msg("failed to apply rendering environment")
187+ }
188+ logger.Info().
189+ Str("gpu", string(gpuVendor)).
190+ Interface("vars", renderEnv.GetAppliedVars()).
191+ Msg("rendering environment configured")
192+}
193+
194+// engineBuildContextOptions builds port.WebKitContextOptions from EngineOptions, wkCfg and perfSettings.
195+func engineBuildContextOptions(
196+ opts port.EngineOptions,
197+ wkCfg WebKitEngineConfig,
198+ perfSettings *config.ResolvedPerformanceSettings,
199+) port.WebKitContextOptions {
200+ cookiePolicy := port.WebKitCookiePolicyNoThirdParty
201+ if opts.CookiePolicy != "" {
202+ cookiePolicy = port.WebKitCookiePolicy(opts.CookiePolicy)
203+ }
204+
205+ wkOpts := port.WebKitContextOptions{
206+ DataDir: opts.DataDir,
207+ CacheDir: opts.CacheDir,
208+ CookiePolicy: cookiePolicy,
209+ ITPEnabled: wkCfg.ITPEnabled,
210+ }
211+
212+ if opts.WebProcessMemory != nil {
213+ wkOpts.WebProcessMemory = opts.WebProcessMemory
214+ } else if engineHasWebProcessMemoryConfig(perfSettings) {
215+ wkOpts.WebProcessMemory = &port.MemoryPressureConfig{
216+ MemoryLimitMB: perfSettings.WebProcessMemoryLimitMB,
217+ PollIntervalSec: perfSettings.WebProcessMemoryPollIntervalSec,
218+ ConservativeThreshold: perfSettings.WebProcessMemoryConservativeThreshold,
219+ StrictThreshold: perfSettings.WebProcessMemoryStrictThreshold,
220+ }
221+ }
222+
223+ if opts.NetworkProcessMemory != nil {
224+ wkOpts.NetworkProcessMemory = opts.NetworkProcessMemory
225+ } else if engineHasNetworkProcessMemoryConfig(perfSettings) {
226+ wkOpts.NetworkProcessMemory = &port.MemoryPressureConfig{
227+ MemoryLimitMB: perfSettings.NetworkProcessMemoryLimitMB,
228+ PollIntervalSec: perfSettings.NetworkProcessMemoryPollIntervalSec,
229+ ConservativeThreshold: perfSettings.NetworkProcessMemoryConservativeThreshold,
230+ StrictThreshold: perfSettings.NetworkProcessMemoryStrictThreshold,
231+ }
232+ }
233+
234+ return wkOpts
235+}
236+
237+// engineInitFilterManager creates and initializes the content filter manager.
238+func engineInitFilterManager(ctx context.Context, cfg *config.Config, dataDir string, logger zerolog.Logger) *filtering.Manager {
239+ filterStoreDir := filepath.Join(dataDir, "filters", "store")
240+ filterJSONDir := filepath.Join(dataDir, "filters", "json")
241+ filterManager, err := filtering.NewManager(filtering.ManagerConfig{
242+ StoreDir: filterStoreDir,
243+ JSONDir: filterJSONDir,
244+ Enabled: cfg.ContentFiltering.Enabled,
245+ AutoUpdate: cfg.ContentFiltering.AutoUpdate,
246+ })
247+ if err != nil {
248+ logger.Warn().Err(err).Msg("failed to create filter manager, continuing without content filtering")
249+ return nil
250+ }
251+ if err := filterManager.Initialize(ctx); err != nil {
252+ logger.Warn().Err(err).Msg("failed to initialize filters, will load async")
253+ }
254+ logging.Trace().Mark("filter_manager")
255+ return filterManager
256+}
257+
258+// engineHasWebProcessMemoryConfig returns true if any web process memory setting is configured.
259+func engineHasWebProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool {
260+ return p.WebProcessMemoryLimitMB > 0 ||
261+ p.WebProcessMemoryPollIntervalSec > 0 ||
262+ p.WebProcessMemoryConservativeThreshold > 0 ||
263+ p.WebProcessMemoryStrictThreshold > 0
264+}
265+
266+// engineHasNetworkProcessMemoryConfig returns true if any network process memory setting is configured.
267+func engineHasNetworkProcessMemoryConfig(p *config.ResolvedPerformanceSettings) bool {
268+ return p.NetworkProcessMemoryLimitMB > 0 ||
269+ p.NetworkProcessMemoryPollIntervalSec > 0 ||
270+ p.NetworkProcessMemoryConservativeThreshold > 0 ||
271+ p.NetworkProcessMemoryStrictThreshold > 0
272+}

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