detached e64d6f3

feat(webui): add support for editing custom performance profile

commit
e64d6f3c45ce2a2fc650683f84d57f13b5b05c7e
author
brice · · 2025-12-29 17:07 UTC
committer
brice · · 2025-12-29 17:28 UTC
parent
e6cc2c8
signature
unsigned
3 files · +93 -9
M internal/application/port/webui_config.go +11 -1
@@ -13,7 +13,17 @@ type WebUIConfig struct {
1313
1414 // WebUIPerformanceConfig represents performance settings editable in dumb://config.
1515 type WebUIPerformanceConfig struct {
16- Profile string `json:"profile"`
16+ Profile string `json:"profile"`
17+ Custom WebUICustomPerformanceConfig `json:"custom"`
18+}
19+
20+// WebUICustomPerformanceConfig holds user-editable fields for custom profile.
21+type WebUICustomPerformanceConfig struct {
22+ SkiaCPUThreads int `json:"skia_cpu_threads"`
23+ SkiaGPUThreads int `json:"skia_gpu_threads"`
24+ WebProcessMemoryMB int `json:"web_process_memory_mb"`
25+ NetworkProcessMemoryMB int `json:"network_process_memory_mb"`
26+ WebViewPoolPrewarm int `json:"webview_pool_prewarm"`
1727 }
1828
1929 type WebUIAppearanceConfig struct {
@@ -13,7 +13,17 @@ type WebUIConfig struct {
13 13
14 // WebUIPerformanceConfig represents performance settings editable in dumb://config.14 // WebUIPerformanceConfig represents performance settings editable in dumb://config.
15 type WebUIPerformanceConfig struct {15 type WebUIPerformanceConfig struct {
16- Profile string `json:"profile"`16+ Profile string `json:"profile"`
17+ Custom WebUICustomPerformanceConfig `json:"custom"`
18+}
19+
20+// WebUICustomPerformanceConfig holds user-editable fields for custom profile.
21+type WebUICustomPerformanceConfig struct {
22+ SkiaCPUThreads int `json:"skia_cpu_threads"`
23+ SkiaGPUThreads int `json:"skia_gpu_threads"`
24+ WebProcessMemoryMB int `json:"web_process_memory_mb"`
25+ NetworkProcessMemoryMB int `json:"network_process_memory_mb"`
26+ WebViewPoolPrewarm int `json:"webview_pool_prewarm"`
17 }27 }
18 28
19 type WebUIAppearanceConfig struct {29 type WebUIAppearanceConfig struct {
M internal/infrastructure/config/webui_gateway.go +9 -0
@@ -65,5 +65,14 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUI
6565 current.Performance.Profile = PerformanceProfile(cfg.Performance.Profile)
6666 }
6767
68+ // Custom performance fields (only used when profile is "custom")
69+ if cfg.Performance.Profile == string(ProfileCustom) {
70+ current.Performance.SkiaCPUPaintingThreads = cfg.Performance.Custom.SkiaCPUThreads
71+ current.Performance.SkiaGPUPaintingThreads = cfg.Performance.Custom.SkiaGPUThreads
72+ current.Performance.WebProcessMemoryLimitMB = cfg.Performance.Custom.WebProcessMemoryMB
73+ current.Performance.NetworkProcessMemoryLimitMB = cfg.Performance.Custom.NetworkProcessMemoryMB
74+ current.Performance.WebViewPoolPrewarmCount = cfg.Performance.Custom.WebViewPoolPrewarm
75+ }
76+
6877 return g.mgr.Save(current)
6978 }
@@ -65,5 +65,14 @@ func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUI
65 current.Performance.Profile = PerformanceProfile(cfg.Performance.Profile)65 current.Performance.Profile = PerformanceProfile(cfg.Performance.Profile)
66 }66 }
67 67
68+ // Custom performance fields (only used when profile is "custom")
69+ if cfg.Performance.Profile == string(ProfileCustom) {
70+ current.Performance.SkiaCPUPaintingThreads = cfg.Performance.Custom.SkiaCPUThreads
71+ current.Performance.SkiaGPUPaintingThreads = cfg.Performance.Custom.SkiaGPUThreads
72+ current.Performance.WebProcessMemoryLimitMB = cfg.Performance.Custom.WebProcessMemoryMB
73+ current.Performance.NetworkProcessMemoryLimitMB = cfg.Performance.Custom.NetworkProcessMemoryMB
74+ current.Performance.WebViewPoolPrewarmCount = cfg.Performance.Custom.WebViewPoolPrewarm
75+ }
76+
68 return g.mgr.Save(current)77 return g.mgr.Save(current)
69 }78 }
M webui/src/pages/ConfigPage.svelte +73 -8
@@ -10,6 +10,7 @@
1010 import { Spinner } from "$lib/components/ui/spinner";
1111 import * as Card from "$lib/components/ui/card";
1212 import * as Tabs from "$lib/components/ui/tabs";
13+ import { FlaskConical, RefreshCw } from "@lucide/svelte";
1314
1415 type ColorPalette = {
1516 background: string;
@@ -46,8 +47,17 @@
4647 vram_mb: number;
4748 };
4849
50+ type CustomPerformance = {
51+ skia_cpu_threads: number;
52+ skia_gpu_threads: number;
53+ web_process_memory_mb: number;
54+ network_process_memory_mb: number;
55+ webview_pool_prewarm: number;
56+ };
57+
4958 type PerformanceConfig = {
5059 profile: string;
60+ custom: CustomPerformance;
5161 resolved: ResolvedPerformance;
5262 hardware: HardwareInfo;
5363 };
@@ -443,7 +453,7 @@
443453
444454 <!-- Experimental feature warning -->
445455 <div class="flex items-start gap-3 rounded-md border border-info/30 bg-info/10 px-4 py-3">
446- <span class="text-info">🧪</span>
456+ <FlaskConical class="mt-0.5 size-4 shrink-0 text-info" />
447457 <div class="space-y-1">
448458 <p class="text-sm font-medium text-info">Experimental Feature</p>
449459 <p class="text-xs text-muted-foreground">
@@ -480,13 +490,68 @@
480490 </div>
481491 {/if}
482492
483- <!-- Custom profile note -->
493+ <!-- Custom profile fields -->
484494 {#if config.performance?.profile === "custom"}
485- <div class="rounded-md border border-border bg-muted/30 px-4 py-3 text-sm text-muted-foreground">
486- <p>
487- Custom profile settings are configured in your <code class="rounded bg-muted px-1 py-0.5">config.toml</code> file.
488- See the <a href="https://github.com/bnema/dumber/blob/main/docs/CONFIG.md#performance-profiles" target="_blank" class="text-primary underline">documentation</a> for available options.
489- </p>
495+ <div class="space-y-4 rounded-md border border-border bg-muted/20 p-4">
496+ <div class="text-sm font-semibold uppercase tracking-[0.15em] text-muted-foreground">
497+ Custom Settings
498+ </div>
499+ <div class="grid gap-4 md:grid-cols-2">
500+ <div class="space-y-2">
501+ <Label for="skia_cpu_threads">Skia CPU Threads</Label>
502+ <Input
503+ id="skia_cpu_threads"
504+ type="number"
505+ min="0"
506+ max="8"
507+ bind:value={config.performance.custom.skia_cpu_threads}
508+ />
509+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default). Max 8.</p>
510+ </div>
511+ <div class="space-y-2">
512+ <Label for="skia_gpu_threads">Skia GPU Threads</Label>
513+ <Input
514+ id="skia_gpu_threads"
515+ type="number"
516+ min="-1"
517+ bind:value={config.performance.custom.skia_gpu_threads}
518+ />
519+ <p class="text-xs text-muted-foreground">-1 = unset, 0 = disable GPU tiles.</p>
520+ </div>
521+ <div class="space-y-2">
522+ <Label for="web_process_memory">Web Process Memory (MB)</Label>
523+ <Input
524+ id="web_process_memory"
525+ type="number"
526+ min="0"
527+ step="256"
528+ bind:value={config.performance.custom.web_process_memory_mb}
529+ />
530+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default).</p>
531+ </div>
532+ <div class="space-y-2">
533+ <Label for="network_process_memory">Network Process Memory (MB)</Label>
534+ <Input
535+ id="network_process_memory"
536+ type="number"
537+ min="0"
538+ step="128"
539+ bind:value={config.performance.custom.network_process_memory_mb}
540+ />
541+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default).</p>
542+ </div>
543+ <div class="space-y-2">
544+ <Label for="webview_pool_prewarm">WebView Pool Prewarm</Label>
545+ <Input
546+ id="webview_pool_prewarm"
547+ type="number"
548+ min="0"
549+ max="20"
550+ bind:value={config.performance.custom.webview_pool_prewarm}
551+ />
552+ <p class="text-xs text-muted-foreground">Pre-created WebViews at startup.</p>
553+ </div>
554+ </div>
490555 </div>
491556 {/if}
492557
@@ -599,7 +664,7 @@
599664 <!-- Restart warning after saving performance settings -->
600665 {#if showRestartWarning}
601666 <div class="flex items-start gap-3 border border-warning/30 bg-warning/10 px-4 py-3">
602- <span class="text-warning">⚠</span>
667+ <RefreshCw class="mt-0.5 size-4 shrink-0 text-warning" />
603668 <div class="space-y-1">
604669 <p class="text-sm font-medium text-warning">Restart Required</p>
605670 <p class="text-xs text-muted-foreground">
@@ -10,6 +10,7 @@
10 import { Spinner } from "$lib/components/ui/spinner";10 import { Spinner } from "$lib/components/ui/spinner";
11 import * as Card from "$lib/components/ui/card";11 import * as Card from "$lib/components/ui/card";
12 import * as Tabs from "$lib/components/ui/tabs";12 import * as Tabs from "$lib/components/ui/tabs";
13+ import { FlaskConical, RefreshCw } from "@lucide/svelte";
13 14
14 type ColorPalette = {15 type ColorPalette = {
15 background: string;16 background: string;
@@ -46,8 +47,17 @@
46 vram_mb: number;47 vram_mb: number;
47 };48 };
48 49
50+ type CustomPerformance = {
51+ skia_cpu_threads: number;
52+ skia_gpu_threads: number;
53+ web_process_memory_mb: number;
54+ network_process_memory_mb: number;
55+ webview_pool_prewarm: number;
56+ };
57+
49 type PerformanceConfig = {58 type PerformanceConfig = {
50 profile: string;59 profile: string;
60+ custom: CustomPerformance;
51 resolved: ResolvedPerformance;61 resolved: ResolvedPerformance;
52 hardware: HardwareInfo;62 hardware: HardwareInfo;
53 };63 };
@@ -443,7 +453,7 @@
443 453
444 <!-- Experimental feature warning -->454 <!-- Experimental feature warning -->
445 <div class="flex items-start gap-3 rounded-md border border-info/30 bg-info/10 px-4 py-3">455 <div class="flex items-start gap-3 rounded-md border border-info/30 bg-info/10 px-4 py-3">
446- <span class="text-info">🧪</span>456+ <FlaskConical class="mt-0.5 size-4 shrink-0 text-info" />
447 <div class="space-y-1">457 <div class="space-y-1">
448 <p class="text-sm font-medium text-info">Experimental Feature</p>458 <p class="text-sm font-medium text-info">Experimental Feature</p>
449 <p class="text-xs text-muted-foreground">459 <p class="text-xs text-muted-foreground">
@@ -480,13 +490,68 @@
480 </div>490 </div>
481 {/if}491 {/if}
482 492
483- <!-- Custom profile note -->493+ <!-- Custom profile fields -->
484 {#if config.performance?.profile === "custom"}494 {#if config.performance?.profile === "custom"}
485- <div class="rounded-md border border-border bg-muted/30 px-4 py-3 text-sm text-muted-foreground">495+ <div class="space-y-4 rounded-md border border-border bg-muted/20 p-4">
486- <p>496+ <div class="text-sm font-semibold uppercase tracking-[0.15em] text-muted-foreground">
487- Custom profile settings are configured in your <code class="rounded bg-muted px-1 py-0.5">config.toml</code> file.497+ Custom Settings
488- See the <a href="https://github.com/bnema/dumber/blob/main/docs/CONFIG.md#performance-profiles" target="_blank" class="text-primary underline">documentation</a> for available options.498+ </div>
489- </p>499+ <div class="grid gap-4 md:grid-cols-2">
500+ <div class="space-y-2">
501+ <Label for="skia_cpu_threads">Skia CPU Threads</Label>
502+ <Input
503+ id="skia_cpu_threads"
504+ type="number"
505+ min="0"
506+ max="8"
507+ bind:value={config.performance.custom.skia_cpu_threads}
508+ />
509+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default). Max 8.</p>
510+ </div>
511+ <div class="space-y-2">
512+ <Label for="skia_gpu_threads">Skia GPU Threads</Label>
513+ <Input
514+ id="skia_gpu_threads"
515+ type="number"
516+ min="-1"
517+ bind:value={config.performance.custom.skia_gpu_threads}
518+ />
519+ <p class="text-xs text-muted-foreground">-1 = unset, 0 = disable GPU tiles.</p>
520+ </div>
521+ <div class="space-y-2">
522+ <Label for="web_process_memory">Web Process Memory (MB)</Label>
523+ <Input
524+ id="web_process_memory"
525+ type="number"
526+ min="0"
527+ step="256"
528+ bind:value={config.performance.custom.web_process_memory_mb}
529+ />
530+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default).</p>
531+ </div>
532+ <div class="space-y-2">
533+ <Label for="network_process_memory">Network Process Memory (MB)</Label>
534+ <Input
535+ id="network_process_memory"
536+ type="number"
537+ min="0"
538+ step="128"
539+ bind:value={config.performance.custom.network_process_memory_mb}
540+ />
541+ <p class="text-xs text-muted-foreground">0 = unset (WebKit default).</p>
542+ </div>
543+ <div class="space-y-2">
544+ <Label for="webview_pool_prewarm">WebView Pool Prewarm</Label>
545+ <Input
546+ id="webview_pool_prewarm"
547+ type="number"
548+ min="0"
549+ max="20"
550+ bind:value={config.performance.custom.webview_pool_prewarm}
551+ />
552+ <p class="text-xs text-muted-foreground">Pre-created WebViews at startup.</p>
553+ </div>
554+ </div>
490 </div>555 </div>
491 {/if}556 {/if}
492 557
@@ -599,7 +664,7 @@
599 <!-- Restart warning after saving performance settings -->664 <!-- Restart warning after saving performance settings -->
600 {#if showRestartWarning}665 {#if showRestartWarning}
601 <div class="flex items-start gap-3 border border-warning/30 bg-warning/10 px-4 py-3">666 <div class="flex items-start gap-3 border border-warning/30 bg-warning/10 px-4 py-3">
602- <span class="text-warning">⚠</span>667+ <RefreshCw class="mt-0.5 size-4 shrink-0 text-warning" />
603 <div class="space-y-1">668 <div class="space-y-1">
604 <p class="text-sm font-medium text-warning">Restart Required</p>669 <p class="text-sm font-medium text-warning">Restart Required</p>
605 <p class="text-xs text-muted-foreground">670 <p class="text-xs text-muted-foreground">

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