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 { | |||
| 13 | 13 | ||
| 14 | 14 | // WebUIPerformanceConfig represents performance settings editable in dumb://config. | |
| 15 | 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 | 29 | 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 | |||
| 65 | 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 | 77 | return g.mgr.Save(current) | |
| 69 | 78 | } | |
| @@ -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 @@ | |||
| 10 | 10 | import { Spinner } from "$lib/components/ui/spinner"; | |
| 11 | 11 | import * as Card from "$lib/components/ui/card"; | |
| 12 | 12 | import * as Tabs from "$lib/components/ui/tabs"; | |
| 13 | + | import { FlaskConical, RefreshCw } from "@lucide/svelte"; | |
| 13 | 14 | ||
| 14 | 15 | type ColorPalette = { | |
| 15 | 16 | background: string; | |
| @@ -46,8 +47,17 @@ | |||
| 46 | 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 | 58 | type PerformanceConfig = { | |
| 50 | 59 | profile: string; | |
| 60 | + | custom: CustomPerformance; | |
| 51 | 61 | resolved: ResolvedPerformance; | |
| 52 | 62 | hardware: HardwareInfo; | |
| 53 | 63 | }; | |
| @@ -443,7 +453,7 @@ | |||
| 443 | 453 | ||
| 444 | 454 | <!-- Experimental feature warning --> | |
| 445 | 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 | 457 | <div class="space-y-1"> | |
| 448 | 458 | <p class="text-sm font-medium text-info">Experimental Feature</p> | |
| 449 | 459 | <p class="text-xs text-muted-foreground"> | |
| @@ -480,13 +490,68 @@ | |||
| 480 | 490 | </div> | |
| 481 | 491 | {/if} | |
| 482 | 492 | ||
| 483 | - | <!-- Custom profile note --> | |
| 493 | + | <!-- Custom profile fields --> | |
| 484 | 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"> | |
| 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> | |
| 490 | 555 | </div> | |
| 491 | 556 | {/if} | |
| 492 | 557 | ||
| @@ -599,7 +664,7 @@ | |||
| 599 | 664 | <!-- Restart warning after saving performance settings --> | |
| 600 | 665 | {#if showRestartWarning} | |
| 601 | 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 | 668 | <div class="space-y-1"> | |
| 604 | 669 | <p class="text-sm font-medium text-warning">Restart Required</p> | |
| 605 | 670 | <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"> | ||