fix(systemviews): address PR review feedback
- commit
- a4e770b16a30227ac27509968b02f52df07ad183
- author
- brice · · 2026-04-25 08:34 UTC
- parent
- 06c3b66
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
51 files · +720 -452
M CONTRIBUTING.md +1 -0
| @@ -36,6 +36,7 @@ git push origin my-fix | |||
| 36 | 36 | ||
| 37 | 37 | **Systemviews assets:** | |
| 38 | 38 | 1. If you modified systemviews files such as `cmd/systemviews/` or `.templ` components, run `make build-systemviews` to regenerate embedded WASM assets | |
| 39 | + | 2. Run `make check` to verify build, tests, and tools before committing | |
| 39 | 40 | ||
| 40 | 41 | Keep commits focused and use single-line commit messages. | |
| 41 | 42 | ||
| @@ -36,6 +36,7 @@ git push origin my-fix | |||||
| 36 | 36 | ||||
| 37 | **Systemviews assets:** | 37 | **Systemviews assets:** | ||
| 38 | 1. If you modified systemviews files such as `cmd/systemviews/` or `.templ` components, run `make build-systemviews` to regenerate embedded WASM assets | 38 | 1. If you modified systemviews files such as `cmd/systemviews/` or `.templ` components, run `make build-systemviews` to regenerate embedded WASM assets | ||
| 39 | + | 2. Run `make check` to verify build, tests, and tools before committing | |||
| 39 | 40 | ||||
| 40 | Keep commits focused and use single-line commit messages. | 41 | Keep commits focused and use single-line commit messages. | ||
| 41 | 42 | ||||
M Makefile +2 -2
| @@ -56,8 +56,8 @@ build-systemviews: generate-systemviews ## Build the WASM systemviews runtime | |||
| 56 | 56 | @command -v brotli >/dev/null 2>&1 || { echo "Error: brotli is required to build compressed systemviews assets. Install brotli and retry."; exit 1; } | |
| 57 | 57 | @mkdir -p assets/systemviews | |
| 58 | 58 | @cp "$$(go env GOROOT)/lib/wasm/wasm_exec.js" assets/systemviews/wasm_exec.js | |
| 59 | - | GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o assets/systemviews/systemviews.wasm ./cmd/systemviews | |
| 60 | - | brotli -f assets/systemviews/systemviews.wasm -o assets/systemviews/systemviews.wasm.br | |
| 59 | + | GOOS=js GOARCH=wasm go build -buildvcs=false -ldflags="-s -w" -o assets/systemviews/systemviews.wasm ./cmd/systemviews | |
| 60 | + | brotli -f -o assets/systemviews/systemviews.wasm.br assets/systemviews/systemviews.wasm | |
| 61 | 61 | @echo "Systemviews build complete" | |
| 62 | 62 | ||
| 63 | 63 | build-quick: ## Build quickly for backend development | |
| @@ -56,8 +56,8 @@ build-systemviews: generate-systemviews ## Build the WASM systemviews runtime | |||||
| 56 | @command -v brotli >/dev/null 2>&1 || { echo "Error: brotli is required to build compressed systemviews assets. Install brotli and retry."; exit 1; } | 56 | @command -v brotli >/dev/null 2>&1 || { echo "Error: brotli is required to build compressed systemviews assets. Install brotli and retry."; exit 1; } | ||
| 57 | @mkdir -p assets/systemviews | 57 | @mkdir -p assets/systemviews | ||
| 58 | @cp "$$(go env GOROOT)/lib/wasm/wasm_exec.js" assets/systemviews/wasm_exec.js | 58 | @cp "$$(go env GOROOT)/lib/wasm/wasm_exec.js" assets/systemviews/wasm_exec.js | ||
| 59 | - | GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o assets/systemviews/systemviews.wasm ./cmd/systemviews | 59 | + | GOOS=js GOARCH=wasm go build -buildvcs=false -ldflags="-s -w" -o assets/systemviews/systemviews.wasm ./cmd/systemviews |
| 60 | - | brotli -f assets/systemviews/systemviews.wasm -o assets/systemviews/systemviews.wasm.br | 60 | + | brotli -f -o assets/systemviews/systemviews.wasm.br assets/systemviews/systemviews.wasm |
| 61 | @echo "Systemviews build complete" | 61 | @echo "Systemviews build complete" | ||
| 62 | 62 | ||||
| 63 | build-quick: ## Build quickly for backend development | 63 | build-quick: ## Build quickly for backend development | ||
M assets/webui_embed.go +1 -0
| @@ -5,6 +5,7 @@ import ( | |||
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | // WebUIAssets contains the embedded systemviews build output. | |
| 8 | + | // The legacy WebUIAssets name is kept for compatibility with existing callers. | |
| 8 | 9 | // Used by the scheme handlers to serve dumb://error, dumb://config, | |
| 9 | 10 | // dumb://history, and dumb://favorites. | |
| 10 | 11 | // | |
| @@ -5,6 +5,7 @@ import ( | |||||
| 5 | ) | 5 | ) | ||
| 6 | 6 | ||||
| 7 | // WebUIAssets contains the embedded systemviews build output. | 7 | // WebUIAssets contains the embedded systemviews build output. | ||
| 8 | + | // The legacy WebUIAssets name is kept for compatibility with existing callers. | |||
| 8 | // Used by the scheme handlers to serve dumb://error, dumb://config, | 9 | // Used by the scheme handlers to serve dumb://error, dumb://config, | ||
| 9 | // dumb://history, and dumb://favorites. | 10 | // dumb://history, and dumb://favorites. | ||
| 10 | // | 11 | // | ||
M cmd/systemviews/bootstrap.go +4 -3
| @@ -3,6 +3,7 @@ package main | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | ||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 6 | 7 | "github.com/bnema/dumber/internal/application/port" | |
| 7 | 8 | "github.com/bnema/dumber/internal/ui/systemviews" | |
| 8 | 9 | ) | |
| @@ -29,15 +30,15 @@ type bridgeConfigProxy struct { | |||
| 29 | 30 | route systemviews.Route | |
| 30 | 31 | } | |
| 31 | 32 | ||
| 32 | - | func (p bridgeConfigProxy) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 33 | + | func (p bridgeConfigProxy) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 33 | 34 | return p.bridge.Current(ctx) | |
| 34 | 35 | } | |
| 35 | 36 | ||
| 36 | - | func (p bridgeConfigProxy) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 37 | + | func (p bridgeConfigProxy) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 37 | 38 | return p.bridge.Default(ctx) | |
| 38 | 39 | } | |
| 39 | 40 | ||
| 40 | - | func (p bridgeConfigProxy) Save(ctx context.Context, cfg port.WebUIConfig) error { | |
| 41 | + | func (p bridgeConfigProxy) Save(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 41 | 42 | return p.bridge.Save(ctx, cfg) | |
| 42 | 43 | } | |
| 43 | 44 | ||
| @@ -3,6 +3,7 @@ package main | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 6 | "github.com/bnema/dumber/internal/application/port" | 7 | "github.com/bnema/dumber/internal/application/port" | ||
| 7 | "github.com/bnema/dumber/internal/ui/systemviews" | 8 | "github.com/bnema/dumber/internal/ui/systemviews" | ||
| 8 | ) | 9 | ) | ||
| @@ -29,15 +30,15 @@ type bridgeConfigProxy struct { | |||||
| 29 | route systemviews.Route | 30 | route systemviews.Route | ||
| 30 | } | 31 | } | ||
| 31 | 32 | ||||
| 32 | - | func (p bridgeConfigProxy) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | 33 | + | func (p bridgeConfigProxy) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 33 | return p.bridge.Current(ctx) | 34 | return p.bridge.Current(ctx) | ||
| 34 | } | 35 | } | ||
| 35 | 36 | ||||
| 36 | - | func (p bridgeConfigProxy) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | 37 | + | func (p bridgeConfigProxy) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 37 | return p.bridge.Default(ctx) | 38 | return p.bridge.Default(ctx) | ||
| 38 | } | 39 | } | ||
| 39 | 40 | ||||
| 40 | - | func (p bridgeConfigProxy) Save(ctx context.Context, cfg port.WebUIConfig) error { | 41 | + | func (p bridgeConfigProxy) Save(ctx context.Context, cfg dto.WebUIConfig) error { |
| 41 | return p.bridge.Save(ctx, cfg) | 42 | return p.bridge.Save(ctx, cfg) | ||
| 42 | } | 43 | } | ||
| 43 | 44 | ||||
M cmd/systemviews/bootstrap_test.go +10 -9
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | 9 | "github.com/bnema/dumber/internal/domain/entity" | |
| 9 | 10 | "github.com/stretchr/testify/assert" | |
| @@ -28,10 +29,10 @@ func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |||
| 28 | 29 | ||
| 29 | 30 | bridge := &fakeBridgeService{ | |
| 30 | 31 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | |
| 31 | - | currentConfig: port.SystemviewConfigPayload{ | |
| 32 | - | Appearance: port.WebUIAppearanceConfig{ | |
| 32 | + | currentConfig: dto.SystemviewConfigPayload{ | |
| 33 | + | Appearance: dto.WebUIAppearanceConfig{ | |
| 33 | 34 | ColorScheme: "prefer-dark", | |
| 34 | - | DarkPalette: port.ColorPalette{ | |
| 35 | + | DarkPalette: dto.ColorPalette{ | |
| 35 | 36 | Background: "#111111", | |
| 36 | 37 | Surface: "#1a1a1a", | |
| 37 | 38 | SurfaceVariant: "#2a2a2a", | |
| @@ -75,7 +76,7 @@ func TestNewBridgeApp_WiresConfigService(t *testing.T) { | |||
| 75 | 76 | t.Parallel() | |
| 76 | 77 | ||
| 77 | 78 | bridge := &fakeBridgeService{ | |
| 78 | - | currentConfig: port.SystemviewConfigPayload{EngineType: "webkit"}, | |
| 79 | + | currentConfig: dto.SystemviewConfigPayload{EngineType: "webkit"}, | |
| 79 | 80 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{DisplayName: "Default"}}}, | |
| 80 | 81 | } | |
| 81 | 82 | app := newBridgeApp(&fakeDOM{}, "dumb://config", bridge) | |
| @@ -108,7 +109,7 @@ type fakeBridgeService struct { | |||
| 108 | 109 | favorites []*entity.Favorite | |
| 109 | 110 | folders []*entity.Folder | |
| 110 | 111 | tags []*entity.Tag | |
| 111 | - | currentConfig port.SystemviewConfigPayload | |
| 112 | + | currentConfig dto.SystemviewConfigPayload | |
| 112 | 113 | keybindings port.KeybindingsConfig | |
| 113 | 114 | } | |
| 114 | 115 | ||
| @@ -187,16 +188,16 @@ func (*fakeBridgeService) AssignTag(context.Context, int64, int64) error { retur | |||
| 187 | 188 | ||
| 188 | 189 | func (*fakeBridgeService) RemoveTag(context.Context, int64, int64) error { return nil } | |
| 189 | 190 | ||
| 190 | - | func (f *fakeBridgeService) Current(context.Context) (port.SystemviewConfigPayload, error) { | |
| 191 | + | func (f *fakeBridgeService) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 191 | 192 | f.calledConfig = true | |
| 192 | 193 | return f.currentConfig, nil | |
| 193 | 194 | } | |
| 194 | 195 | ||
| 195 | - | func (*fakeBridgeService) Default(context.Context) (port.SystemviewConfigPayload, error) { | |
| 196 | - | return port.SystemviewConfigPayload{}, nil | |
| 196 | + | func (*fakeBridgeService) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 197 | + | return dto.SystemviewConfigPayload{}, nil | |
| 197 | 198 | } | |
| 198 | 199 | ||
| 199 | - | func (*fakeBridgeService) Save(context.Context, port.WebUIConfig) error { return nil } | |
| 200 | + | func (*fakeBridgeService) Save(context.Context, dto.WebUIConfig) error { return nil } | |
| 200 | 201 | ||
| 201 | 202 | func (f *fakeBridgeService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | |
| 202 | 203 | f.calledKeybindings = true | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "testing" | 5 | "testing" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 7 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | "github.com/bnema/dumber/internal/domain/entity" | 9 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 9 | "github.com/stretchr/testify/assert" | 10 | "github.com/stretchr/testify/assert" | ||
| @@ -28,10 +29,10 @@ func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |||||
| 28 | 29 | ||||
| 29 | bridge := &fakeBridgeService{ | 30 | bridge := &fakeBridgeService{ | ||
| 30 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | 31 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | ||
| 31 | - | currentConfig: port.SystemviewConfigPayload{ | 32 | + | currentConfig: dto.SystemviewConfigPayload{ |
| 32 | - | Appearance: port.WebUIAppearanceConfig{ | 33 | + | Appearance: dto.WebUIAppearanceConfig{ |
| 33 | ColorScheme: "prefer-dark", | 34 | ColorScheme: "prefer-dark", | ||
| 34 | - | DarkPalette: port.ColorPalette{ | 35 | + | DarkPalette: dto.ColorPalette{ |
| 35 | Background: "#111111", | 36 | Background: "#111111", | ||
| 36 | Surface: "#1a1a1a", | 37 | Surface: "#1a1a1a", | ||
| 37 | SurfaceVariant: "#2a2a2a", | 38 | SurfaceVariant: "#2a2a2a", | ||
| @@ -75,7 +76,7 @@ func TestNewBridgeApp_WiresConfigService(t *testing.T) { | |||||
| 75 | t.Parallel() | 76 | t.Parallel() | ||
| 76 | 77 | ||||
| 77 | bridge := &fakeBridgeService{ | 78 | bridge := &fakeBridgeService{ | ||
| 78 | - | currentConfig: port.SystemviewConfigPayload{EngineType: "webkit"}, | 79 | + | currentConfig: dto.SystemviewConfigPayload{EngineType: "webkit"}, |
| 79 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{DisplayName: "Default"}}}, | 80 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{DisplayName: "Default"}}}, | ||
| 80 | } | 81 | } | ||
| 81 | app := newBridgeApp(&fakeDOM{}, "dumb://config", bridge) | 82 | app := newBridgeApp(&fakeDOM{}, "dumb://config", bridge) | ||
| @@ -108,7 +109,7 @@ type fakeBridgeService struct { | |||||
| 108 | favorites []*entity.Favorite | 109 | favorites []*entity.Favorite | ||
| 109 | folders []*entity.Folder | 110 | folders []*entity.Folder | ||
| 110 | tags []*entity.Tag | 111 | tags []*entity.Tag | ||
| 111 | - | currentConfig port.SystemviewConfigPayload | 112 | + | currentConfig dto.SystemviewConfigPayload |
| 112 | keybindings port.KeybindingsConfig | 113 | keybindings port.KeybindingsConfig | ||
| 113 | } | 114 | } | ||
| 114 | 115 | ||||
| @@ -187,16 +188,16 @@ func (*fakeBridgeService) AssignTag(context.Context, int64, int64) error { retur | |||||
| 187 | 188 | ||||
| 188 | func (*fakeBridgeService) RemoveTag(context.Context, int64, int64) error { return nil } | 189 | func (*fakeBridgeService) RemoveTag(context.Context, int64, int64) error { return nil } | ||
| 189 | 190 | ||||
| 190 | - | func (f *fakeBridgeService) Current(context.Context) (port.SystemviewConfigPayload, error) { | 191 | + | func (f *fakeBridgeService) Current(context.Context) (dto.SystemviewConfigPayload, error) { |
| 191 | f.calledConfig = true | 192 | f.calledConfig = true | ||
| 192 | return f.currentConfig, nil | 193 | return f.currentConfig, nil | ||
| 193 | } | 194 | } | ||
| 194 | 195 | ||||
| 195 | - | func (*fakeBridgeService) Default(context.Context) (port.SystemviewConfigPayload, error) { | 196 | + | func (*fakeBridgeService) Default(context.Context) (dto.SystemviewConfigPayload, error) { |
| 196 | - | return port.SystemviewConfigPayload{}, nil | 197 | + | return dto.SystemviewConfigPayload{}, nil |
| 197 | } | 198 | } | ||
| 198 | 199 | ||||
| 199 | - | func (*fakeBridgeService) Save(context.Context, port.WebUIConfig) error { return nil } | 200 | + | func (*fakeBridgeService) Save(context.Context, dto.WebUIConfig) error { return nil } |
| 200 | 201 | ||||
| 201 | func (f *fakeBridgeService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | 202 | func (f *fakeBridgeService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | ||
| 202 | f.calledKeybindings = true | 203 | f.calledKeybindings = true | ||
A internal/application/dto/webui_config.go +92 -0
| @@ -0,0 +1,92 @@ | |||
| 1 | + | package dto | |
| 2 | + | ||
| 3 | + | // WebUIConfig represents the subset of configuration editable in dumb://config. | |
| 4 | + | type WebUIConfig struct { | |
| 5 | + | Appearance WebUIAppearanceConfig `json:"appearance"` | |
| 6 | + | Performance WebUIPerformanceConfig `json:"performance"` | |
| 7 | + | DefaultUIScale float64 `json:"default_ui_scale"` | |
| 8 | + | DefaultSearchEngine string `json:"default_search_engine"` | |
| 9 | + | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |
| 10 | + | } | |
| 11 | + | ||
| 12 | + | // SystemviewCustomPerformancePayload is the editable custom performance profile shape. | |
| 13 | + | type SystemviewCustomPerformancePayload = WebUICustomPerformanceConfig | |
| 14 | + | ||
| 15 | + | // SystemviewResolvedPerformancePayload is the resolved effective performance shape. | |
| 16 | + | type SystemviewResolvedPerformancePayload struct { | |
| 17 | + | SkiaCPUThreads int `json:"skia_cpu_threads"` | |
| 18 | + | SkiaGPUThreads int `json:"skia_gpu_threads"` | |
| 19 | + | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |
| 20 | + | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |
| 21 | + | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |
| 22 | + | ConservativeThreshold float64 `json:"conservative_threshold"` | |
| 23 | + | StrictThreshold float64 `json:"strict_threshold"` | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // SystemviewHardwarePayload exposes detected hardware to the settings UI. | |
| 27 | + | type SystemviewHardwarePayload struct { | |
| 28 | + | CPUCores int `json:"cpu_cores"` | |
| 29 | + | CPUThreads int `json:"cpu_threads"` | |
| 30 | + | TotalRAMMB int `json:"total_ram_mb"` | |
| 31 | + | GPUVendor string `json:"gpu_vendor"` | |
| 32 | + | GPUName string `json:"gpu_name"` | |
| 33 | + | VRAMMB int `json:"vram_mb"` | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | // SystemviewPerformancePayload is the performance tab payload expected by the UI. | |
| 37 | + | type SystemviewPerformancePayload struct { | |
| 38 | + | Profile string `json:"profile"` | |
| 39 | + | Custom SystemviewCustomPerformancePayload `json:"custom"` | |
| 40 | + | Resolved SystemviewResolvedPerformancePayload `json:"resolved"` | |
| 41 | + | Hardware SystemviewHardwarePayload `json:"hardware"` | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | // SystemviewConfigPayload is the full JSON shape expected by dumb://config. | |
| 45 | + | type SystemviewConfigPayload struct { | |
| 46 | + | Appearance WebUIAppearanceConfig `json:"appearance"` | |
| 47 | + | Performance SystemviewPerformancePayload `json:"performance"` | |
| 48 | + | DefaultUIScale float64 `json:"default_ui_scale"` | |
| 49 | + | DefaultSearchEngine string `json:"default_search_engine"` | |
| 50 | + | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |
| 51 | + | EngineType string `json:"engine_type"` | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | // WebUIPerformanceConfig represents performance settings editable in dumb://config. | |
| 55 | + | type WebUIPerformanceConfig struct { | |
| 56 | + | Profile string `json:"profile"` | |
| 57 | + | Custom WebUICustomPerformanceConfig `json:"custom"` | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | // WebUICustomPerformanceConfig holds user-editable fields for custom profile. | |
| 61 | + | type WebUICustomPerformanceConfig struct { | |
| 62 | + | SkiaCPUThreads int `json:"skia_cpu_threads"` | |
| 63 | + | SkiaGPUThreads int `json:"skia_gpu_threads"` | |
| 64 | + | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |
| 65 | + | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |
| 66 | + | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | type WebUIAppearanceConfig struct { | |
| 70 | + | SansFont string `json:"sans_font"` | |
| 71 | + | SerifFont string `json:"serif_font"` | |
| 72 | + | MonospaceFont string `json:"monospace_font"` | |
| 73 | + | DefaultFontSize int `json:"default_font_size"` | |
| 74 | + | ColorScheme string `json:"color_scheme"` | |
| 75 | + | LightPalette ColorPalette `json:"light_palette"` | |
| 76 | + | DarkPalette ColorPalette `json:"dark_palette"` | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | type ColorPalette struct { | |
| 80 | + | Background string `json:"background"` | |
| 81 | + | Surface string `json:"surface"` | |
| 82 | + | SurfaceVariant string `json:"surface_variant"` | |
| 83 | + | Text string `json:"text"` | |
| 84 | + | Muted string `json:"muted"` | |
| 85 | + | Accent string `json:"accent"` | |
| 86 | + | Border string `json:"border"` | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | type SearchShortcut struct { | |
| 90 | + | URL string `json:"url"` | |
| 91 | + | Description string `json:"description"` | |
| 92 | + | } | |
| @@ -0,0 +1,92 @@ | |||||
| 1 | + | package dto | |||
| 2 | + | ||||
| 3 | + | // WebUIConfig represents the subset of configuration editable in dumb://config. | |||
| 4 | + | type WebUIConfig struct { | |||
| 5 | + | Appearance WebUIAppearanceConfig `json:"appearance"` | |||
| 6 | + | Performance WebUIPerformanceConfig `json:"performance"` | |||
| 7 | + | DefaultUIScale float64 `json:"default_ui_scale"` | |||
| 8 | + | DefaultSearchEngine string `json:"default_search_engine"` | |||
| 9 | + | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |||
| 10 | + | } | |||
| 11 | + | ||||
| 12 | + | // SystemviewCustomPerformancePayload is the editable custom performance profile shape. | |||
| 13 | + | type SystemviewCustomPerformancePayload = WebUICustomPerformanceConfig | |||
| 14 | + | ||||
| 15 | + | // SystemviewResolvedPerformancePayload is the resolved effective performance shape. | |||
| 16 | + | type SystemviewResolvedPerformancePayload struct { | |||
| 17 | + | SkiaCPUThreads int `json:"skia_cpu_threads"` | |||
| 18 | + | SkiaGPUThreads int `json:"skia_gpu_threads"` | |||
| 19 | + | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |||
| 20 | + | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |||
| 21 | + | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |||
| 22 | + | ConservativeThreshold float64 `json:"conservative_threshold"` | |||
| 23 | + | StrictThreshold float64 `json:"strict_threshold"` | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // SystemviewHardwarePayload exposes detected hardware to the settings UI. | |||
| 27 | + | type SystemviewHardwarePayload struct { | |||
| 28 | + | CPUCores int `json:"cpu_cores"` | |||
| 29 | + | CPUThreads int `json:"cpu_threads"` | |||
| 30 | + | TotalRAMMB int `json:"total_ram_mb"` | |||
| 31 | + | GPUVendor string `json:"gpu_vendor"` | |||
| 32 | + | GPUName string `json:"gpu_name"` | |||
| 33 | + | VRAMMB int `json:"vram_mb"` | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | // SystemviewPerformancePayload is the performance tab payload expected by the UI. | |||
| 37 | + | type SystemviewPerformancePayload struct { | |||
| 38 | + | Profile string `json:"profile"` | |||
| 39 | + | Custom SystemviewCustomPerformancePayload `json:"custom"` | |||
| 40 | + | Resolved SystemviewResolvedPerformancePayload `json:"resolved"` | |||
| 41 | + | Hardware SystemviewHardwarePayload `json:"hardware"` | |||
| 42 | + | } | |||
| 43 | + | ||||
| 44 | + | // SystemviewConfigPayload is the full JSON shape expected by dumb://config. | |||
| 45 | + | type SystemviewConfigPayload struct { | |||
| 46 | + | Appearance WebUIAppearanceConfig `json:"appearance"` | |||
| 47 | + | Performance SystemviewPerformancePayload `json:"performance"` | |||
| 48 | + | DefaultUIScale float64 `json:"default_ui_scale"` | |||
| 49 | + | DefaultSearchEngine string `json:"default_search_engine"` | |||
| 50 | + | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |||
| 51 | + | EngineType string `json:"engine_type"` | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | // WebUIPerformanceConfig represents performance settings editable in dumb://config. | |||
| 55 | + | type WebUIPerformanceConfig struct { | |||
| 56 | + | Profile string `json:"profile"` | |||
| 57 | + | Custom WebUICustomPerformanceConfig `json:"custom"` | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | // WebUICustomPerformanceConfig holds user-editable fields for custom profile. | |||
| 61 | + | type WebUICustomPerformanceConfig struct { | |||
| 62 | + | SkiaCPUThreads int `json:"skia_cpu_threads"` | |||
| 63 | + | SkiaGPUThreads int `json:"skia_gpu_threads"` | |||
| 64 | + | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |||
| 65 | + | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |||
| 66 | + | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | type WebUIAppearanceConfig struct { | |||
| 70 | + | SansFont string `json:"sans_font"` | |||
| 71 | + | SerifFont string `json:"serif_font"` | |||
| 72 | + | MonospaceFont string `json:"monospace_font"` | |||
| 73 | + | DefaultFontSize int `json:"default_font_size"` | |||
| 74 | + | ColorScheme string `json:"color_scheme"` | |||
| 75 | + | LightPalette ColorPalette `json:"light_palette"` | |||
| 76 | + | DarkPalette ColorPalette `json:"dark_palette"` | |||
| 77 | + | } | |||
| 78 | + | ||||
| 79 | + | type ColorPalette struct { | |||
| 80 | + | Background string `json:"background"` | |||
| 81 | + | Surface string `json:"surface"` | |||
| 82 | + | SurfaceVariant string `json:"surface_variant"` | |||
| 83 | + | Text string `json:"text"` | |||
| 84 | + | Muted string `json:"muted"` | |||
| 85 | + | Accent string `json:"accent"` | |||
| 86 | + | Border string `json:"border"` | |||
| 87 | + | } | |||
| 88 | + | ||||
| 89 | + | type SearchShortcut struct { | |||
| 90 | + | URL string `json:"url"` | |||
| 91 | + | Description string `json:"description"` | |||
| 92 | + | } | |||
M internal/application/port/handler_deps.go +2 -1
| @@ -3,6 +3,7 @@ package port | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | ||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 6 | 7 | "github.com/bnema/dumber/internal/domain/entity" | |
| 7 | 8 | ) | |
| 8 | 9 | ||
| @@ -43,7 +44,7 @@ type AutoCopyConfig interface { | |||
| 43 | 44 | // A struct is used here to avoid reverse dependencies between the bootstrap and | |
| 44 | 45 | // UI layers, which both need to reference this shared dependency container. | |
| 45 | 46 | type HandlerDeps struct { | |
| 46 | - | SaveConfig func(context.Context, WebUIConfig) error | |
| 47 | + | SaveConfig func(context.Context, dto.WebUIConfig) error | |
| 47 | 48 | SaveOmniboxInitialBehavior func(context.Context, entity.OmniboxInitialBehavior) error | |
| 48 | 49 | KeybindingsGetter KeybindingsGetter | |
| 49 | 50 | KeybindingSetter KeybindingSetter | |
| @@ -3,6 +3,7 @@ package port | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 6 | "github.com/bnema/dumber/internal/domain/entity" | 7 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| @@ -43,7 +44,7 @@ type AutoCopyConfig interface { | |||||
| 43 | // A struct is used here to avoid reverse dependencies between the bootstrap and | 44 | // A struct is used here to avoid reverse dependencies between the bootstrap and | ||
| 44 | // UI layers, which both need to reference this shared dependency container. | 45 | // UI layers, which both need to reference this shared dependency container. | ||
| 45 | type HandlerDeps struct { | 46 | type HandlerDeps struct { | ||
| 46 | - | SaveConfig func(context.Context, WebUIConfig) error | 47 | + | SaveConfig func(context.Context, dto.WebUIConfig) error |
| 47 | SaveOmniboxInitialBehavior func(context.Context, entity.OmniboxInitialBehavior) error | 48 | SaveOmniboxInitialBehavior func(context.Context, entity.OmniboxInitialBehavior) error | ||
| 48 | KeybindingsGetter KeybindingsGetter | 49 | KeybindingsGetter KeybindingsGetter | ||
| 49 | KeybindingSetter KeybindingSetter | 50 | KeybindingSetter KeybindingSetter | ||
M internal/application/port/mocks/mocks.go +21 -21
| @@ -12113,22 +12113,22 @@ func (_m *MockSystemviewConfigReader) EXPECT() *MockSystemviewConfigReader_Expec | |||
| 12113 | 12113 | } | |
| 12114 | 12114 | ||
| 12115 | 12115 | // Current provides a mock function for the type MockSystemviewConfigReader | |
| 12116 | - | func (_mock *MockSystemviewConfigReader) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 12116 | + | func (_mock *MockSystemviewConfigReader) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 12117 | 12117 | ret := _mock.Called(ctx) | |
| 12118 | 12118 | ||
| 12119 | 12119 | if len(ret) == 0 { | |
| 12120 | 12120 | panic("no return value specified for Current") | |
| 12121 | 12121 | } | |
| 12122 | 12122 | ||
| 12123 | - | var r0 port.SystemviewConfigPayload | |
| 12123 | + | var r0 dto.SystemviewConfigPayload | |
| 12124 | 12124 | var r1 error | |
| 12125 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) (port.SystemviewConfigPayload, error)); ok { | |
| 12125 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) (dto.SystemviewConfigPayload, error)); ok { | |
| 12126 | 12126 | return returnFunc(ctx) | |
| 12127 | 12127 | } | |
| 12128 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) port.SystemviewConfigPayload); ok { | |
| 12128 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) dto.SystemviewConfigPayload); ok { | |
| 12129 | 12129 | r0 = returnFunc(ctx) | |
| 12130 | 12130 | } else { | |
| 12131 | - | r0 = ret.Get(0).(port.SystemviewConfigPayload) | |
| 12131 | + | r0 = ret.Get(0).(dto.SystemviewConfigPayload) | |
| 12132 | 12132 | } | |
| 12133 | 12133 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 12134 | 12134 | r1 = returnFunc(ctx) | |
| @@ -12162,33 +12162,33 @@ func (_c *MockSystemviewConfigReader_Current_Call) Run(run func(ctx context.Cont | |||
| 12162 | 12162 | return _c | |
| 12163 | 12163 | } | |
| 12164 | 12164 | ||
| 12165 | - | func (_c *MockSystemviewConfigReader_Current_Call) Return(systemviewConfigPayload port.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Current_Call { | |
| 12165 | + | func (_c *MockSystemviewConfigReader_Current_Call) Return(systemviewConfigPayload dto.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Current_Call { | |
| 12166 | 12166 | _c.Call.Return(systemviewConfigPayload, err) | |
| 12167 | 12167 | return _c | |
| 12168 | 12168 | } | |
| 12169 | 12169 | ||
| 12170 | - | func (_c *MockSystemviewConfigReader_Current_Call) RunAndReturn(run func(ctx context.Context) (port.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Current_Call { | |
| 12170 | + | func (_c *MockSystemviewConfigReader_Current_Call) RunAndReturn(run func(ctx context.Context) (dto.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Current_Call { | |
| 12171 | 12171 | _c.Call.Return(run) | |
| 12172 | 12172 | return _c | |
| 12173 | 12173 | } | |
| 12174 | 12174 | ||
| 12175 | 12175 | // Default provides a mock function for the type MockSystemviewConfigReader | |
| 12176 | - | func (_mock *MockSystemviewConfigReader) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 12176 | + | func (_mock *MockSystemviewConfigReader) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 12177 | 12177 | ret := _mock.Called(ctx) | |
| 12178 | 12178 | ||
| 12179 | 12179 | if len(ret) == 0 { | |
| 12180 | 12180 | panic("no return value specified for Default") | |
| 12181 | 12181 | } | |
| 12182 | 12182 | ||
| 12183 | - | var r0 port.SystemviewConfigPayload | |
| 12183 | + | var r0 dto.SystemviewConfigPayload | |
| 12184 | 12184 | var r1 error | |
| 12185 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) (port.SystemviewConfigPayload, error)); ok { | |
| 12185 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) (dto.SystemviewConfigPayload, error)); ok { | |
| 12186 | 12186 | return returnFunc(ctx) | |
| 12187 | 12187 | } | |
| 12188 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) port.SystemviewConfigPayload); ok { | |
| 12188 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) dto.SystemviewConfigPayload); ok { | |
| 12189 | 12189 | r0 = returnFunc(ctx) | |
| 12190 | 12190 | } else { | |
| 12191 | - | r0 = ret.Get(0).(port.SystemviewConfigPayload) | |
| 12191 | + | r0 = ret.Get(0).(dto.SystemviewConfigPayload) | |
| 12192 | 12192 | } | |
| 12193 | 12193 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 12194 | 12194 | r1 = returnFunc(ctx) | |
| @@ -12222,12 +12222,12 @@ func (_c *MockSystemviewConfigReader_Default_Call) Run(run func(ctx context.Cont | |||
| 12222 | 12222 | return _c | |
| 12223 | 12223 | } | |
| 12224 | 12224 | ||
| 12225 | - | func (_c *MockSystemviewConfigReader_Default_Call) Return(systemviewConfigPayload port.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Default_Call { | |
| 12225 | + | func (_c *MockSystemviewConfigReader_Default_Call) Return(systemviewConfigPayload dto.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Default_Call { | |
| 12226 | 12226 | _c.Call.Return(systemviewConfigPayload, err) | |
| 12227 | 12227 | return _c | |
| 12228 | 12228 | } | |
| 12229 | 12229 | ||
| 12230 | - | func (_c *MockSystemviewConfigReader_Default_Call) RunAndReturn(run func(ctx context.Context) (port.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Default_Call { | |
| 12230 | + | func (_c *MockSystemviewConfigReader_Default_Call) RunAndReturn(run func(ctx context.Context) (dto.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Default_Call { | |
| 12231 | 12231 | _c.Call.Return(run) | |
| 12232 | 12232 | return _c | |
| 12233 | 12233 | } | |
| @@ -12260,7 +12260,7 @@ func (_m *MockWebUIConfigSaver) EXPECT() *MockWebUIConfigSaver_Expecter { | |||
| 12260 | 12260 | } | |
| 12261 | 12261 | ||
| 12262 | 12262 | // SaveWebUIConfig provides a mock function for the type MockWebUIConfigSaver | |
| 12263 | - | func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg port.WebUIConfig) error { | |
| 12263 | + | func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 12264 | 12264 | ret := _mock.Called(ctx, cfg) | |
| 12265 | 12265 | ||
| 12266 | 12266 | if len(ret) == 0 { | |
| @@ -12268,7 +12268,7 @@ func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg port | |||
| 12268 | 12268 | } | |
| 12269 | 12269 | ||
| 12270 | 12270 | var r0 error | |
| 12271 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebUIConfig) error); ok { | |
| 12271 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, dto.WebUIConfig) error); ok { | |
| 12272 | 12272 | r0 = returnFunc(ctx, cfg) | |
| 12273 | 12273 | } else { | |
| 12274 | 12274 | r0 = ret.Error(0) | |
| @@ -12283,20 +12283,20 @@ type MockWebUIConfigSaver_SaveWebUIConfig_Call struct { | |||
| 12283 | 12283 | ||
| 12284 | 12284 | // SaveWebUIConfig is a helper method to define mock.On call | |
| 12285 | 12285 | // - ctx context.Context | |
| 12286 | - | // - cfg port.WebUIConfig | |
| 12286 | + | // - cfg dto.WebUIConfig | |
| 12287 | 12287 | func (_e *MockWebUIConfigSaver_Expecter) SaveWebUIConfig(ctx interface{}, cfg interface{}) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | |
| 12288 | 12288 | return &MockWebUIConfigSaver_SaveWebUIConfig_Call{Call: _e.mock.On("SaveWebUIConfig", ctx, cfg)} | |
| 12289 | 12289 | } | |
| 12290 | 12290 | ||
| 12291 | - | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Run(run func(ctx context.Context, cfg port.WebUIConfig)) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | |
| 12291 | + | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Run(run func(ctx context.Context, cfg dto.WebUIConfig)) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | |
| 12292 | 12292 | _c.Call.Run(func(args mock.Arguments) { | |
| 12293 | 12293 | var arg0 context.Context | |
| 12294 | 12294 | if args[0] != nil { | |
| 12295 | 12295 | arg0 = args[0].(context.Context) | |
| 12296 | 12296 | } | |
| 12297 | - | var arg1 port.WebUIConfig | |
| 12297 | + | var arg1 dto.WebUIConfig | |
| 12298 | 12298 | if args[1] != nil { | |
| 12299 | - | arg1 = args[1].(port.WebUIConfig) | |
| 12299 | + | arg1 = args[1].(dto.WebUIConfig) | |
| 12300 | 12300 | } | |
| 12301 | 12301 | run( | |
| 12302 | 12302 | arg0, | |
| @@ -12311,7 +12311,7 @@ func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Return(err error) *MockWebU | |||
| 12311 | 12311 | return _c | |
| 12312 | 12312 | } | |
| 12313 | 12313 | ||
| 12314 | - | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) RunAndReturn(run func(ctx context.Context, cfg port.WebUIConfig) error) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | |
| 12314 | + | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) RunAndReturn(run func(ctx context.Context, cfg dto.WebUIConfig) error) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | |
| 12315 | 12315 | _c.Call.Return(run) | |
| 12316 | 12316 | return _c | |
| 12317 | 12317 | } | |
| @@ -12113,22 +12113,22 @@ func (_m *MockSystemviewConfigReader) EXPECT() *MockSystemviewConfigReader_Expec | |||||
| 12113 | } | 12113 | } | ||
| 12114 | 12114 | ||||
| 12115 | // Current provides a mock function for the type MockSystemviewConfigReader | 12115 | // Current provides a mock function for the type MockSystemviewConfigReader | ||
| 12116 | - | func (_mock *MockSystemviewConfigReader) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | 12116 | + | func (_mock *MockSystemviewConfigReader) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 12117 | ret := _mock.Called(ctx) | 12117 | ret := _mock.Called(ctx) | ||
| 12118 | 12118 | ||||
| 12119 | if len(ret) == 0 { | 12119 | if len(ret) == 0 { | ||
| 12120 | panic("no return value specified for Current") | 12120 | panic("no return value specified for Current") | ||
| 12121 | } | 12121 | } | ||
| 12122 | 12122 | ||||
| 12123 | - | var r0 port.SystemviewConfigPayload | 12123 | + | var r0 dto.SystemviewConfigPayload |
| 12124 | var r1 error | 12124 | var r1 error | ||
| 12125 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) (port.SystemviewConfigPayload, error)); ok { | 12125 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) (dto.SystemviewConfigPayload, error)); ok { |
| 12126 | return returnFunc(ctx) | 12126 | return returnFunc(ctx) | ||
| 12127 | } | 12127 | } | ||
| 12128 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) port.SystemviewConfigPayload); ok { | 12128 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) dto.SystemviewConfigPayload); ok { |
| 12129 | r0 = returnFunc(ctx) | 12129 | r0 = returnFunc(ctx) | ||
| 12130 | } else { | 12130 | } else { | ||
| 12131 | - | r0 = ret.Get(0).(port.SystemviewConfigPayload) | 12131 | + | r0 = ret.Get(0).(dto.SystemviewConfigPayload) |
| 12132 | } | 12132 | } | ||
| 12133 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | 12133 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | ||
| 12134 | r1 = returnFunc(ctx) | 12134 | r1 = returnFunc(ctx) | ||
| @@ -12162,33 +12162,33 @@ func (_c *MockSystemviewConfigReader_Current_Call) Run(run func(ctx context.Cont | |||||
| 12162 | return _c | 12162 | return _c | ||
| 12163 | } | 12163 | } | ||
| 12164 | 12164 | ||||
| 12165 | - | func (_c *MockSystemviewConfigReader_Current_Call) Return(systemviewConfigPayload port.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Current_Call { | 12165 | + | func (_c *MockSystemviewConfigReader_Current_Call) Return(systemviewConfigPayload dto.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Current_Call { |
| 12166 | _c.Call.Return(systemviewConfigPayload, err) | 12166 | _c.Call.Return(systemviewConfigPayload, err) | ||
| 12167 | return _c | 12167 | return _c | ||
| 12168 | } | 12168 | } | ||
| 12169 | 12169 | ||||
| 12170 | - | func (_c *MockSystemviewConfigReader_Current_Call) RunAndReturn(run func(ctx context.Context) (port.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Current_Call { | 12170 | + | func (_c *MockSystemviewConfigReader_Current_Call) RunAndReturn(run func(ctx context.Context) (dto.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Current_Call { |
| 12171 | _c.Call.Return(run) | 12171 | _c.Call.Return(run) | ||
| 12172 | return _c | 12172 | return _c | ||
| 12173 | } | 12173 | } | ||
| 12174 | 12174 | ||||
| 12175 | // Default provides a mock function for the type MockSystemviewConfigReader | 12175 | // Default provides a mock function for the type MockSystemviewConfigReader | ||
| 12176 | - | func (_mock *MockSystemviewConfigReader) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | 12176 | + | func (_mock *MockSystemviewConfigReader) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 12177 | ret := _mock.Called(ctx) | 12177 | ret := _mock.Called(ctx) | ||
| 12178 | 12178 | ||||
| 12179 | if len(ret) == 0 { | 12179 | if len(ret) == 0 { | ||
| 12180 | panic("no return value specified for Default") | 12180 | panic("no return value specified for Default") | ||
| 12181 | } | 12181 | } | ||
| 12182 | 12182 | ||||
| 12183 | - | var r0 port.SystemviewConfigPayload | 12183 | + | var r0 dto.SystemviewConfigPayload |
| 12184 | var r1 error | 12184 | var r1 error | ||
| 12185 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) (port.SystemviewConfigPayload, error)); ok { | 12185 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) (dto.SystemviewConfigPayload, error)); ok { |
| 12186 | return returnFunc(ctx) | 12186 | return returnFunc(ctx) | ||
| 12187 | } | 12187 | } | ||
| 12188 | - | if returnFunc, ok := ret.Get(0).(func(context.Context) port.SystemviewConfigPayload); ok { | 12188 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) dto.SystemviewConfigPayload); ok { |
| 12189 | r0 = returnFunc(ctx) | 12189 | r0 = returnFunc(ctx) | ||
| 12190 | } else { | 12190 | } else { | ||
| 12191 | - | r0 = ret.Get(0).(port.SystemviewConfigPayload) | 12191 | + | r0 = ret.Get(0).(dto.SystemviewConfigPayload) |
| 12192 | } | 12192 | } | ||
| 12193 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | 12193 | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | ||
| 12194 | r1 = returnFunc(ctx) | 12194 | r1 = returnFunc(ctx) | ||
| @@ -12222,12 +12222,12 @@ func (_c *MockSystemviewConfigReader_Default_Call) Run(run func(ctx context.Cont | |||||
| 12222 | return _c | 12222 | return _c | ||
| 12223 | } | 12223 | } | ||
| 12224 | 12224 | ||||
| 12225 | - | func (_c *MockSystemviewConfigReader_Default_Call) Return(systemviewConfigPayload port.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Default_Call { | 12225 | + | func (_c *MockSystemviewConfigReader_Default_Call) Return(systemviewConfigPayload dto.SystemviewConfigPayload, err error) *MockSystemviewConfigReader_Default_Call { |
| 12226 | _c.Call.Return(systemviewConfigPayload, err) | 12226 | _c.Call.Return(systemviewConfigPayload, err) | ||
| 12227 | return _c | 12227 | return _c | ||
| 12228 | } | 12228 | } | ||
| 12229 | 12229 | ||||
| 12230 | - | func (_c *MockSystemviewConfigReader_Default_Call) RunAndReturn(run func(ctx context.Context) (port.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Default_Call { | 12230 | + | func (_c *MockSystemviewConfigReader_Default_Call) RunAndReturn(run func(ctx context.Context) (dto.SystemviewConfigPayload, error)) *MockSystemviewConfigReader_Default_Call { |
| 12231 | _c.Call.Return(run) | 12231 | _c.Call.Return(run) | ||
| 12232 | return _c | 12232 | return _c | ||
| 12233 | } | 12233 | } | ||
| @@ -12260,7 +12260,7 @@ func (_m *MockWebUIConfigSaver) EXPECT() *MockWebUIConfigSaver_Expecter { | |||||
| 12260 | } | 12260 | } | ||
| 12261 | 12261 | ||||
| 12262 | // SaveWebUIConfig provides a mock function for the type MockWebUIConfigSaver | 12262 | // SaveWebUIConfig provides a mock function for the type MockWebUIConfigSaver | ||
| 12263 | - | func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg port.WebUIConfig) error { | 12263 | + | func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error { |
| 12264 | ret := _mock.Called(ctx, cfg) | 12264 | ret := _mock.Called(ctx, cfg) | ||
| 12265 | 12265 | ||||
| 12266 | if len(ret) == 0 { | 12266 | if len(ret) == 0 { | ||
| @@ -12268,7 +12268,7 @@ func (_mock *MockWebUIConfigSaver) SaveWebUIConfig(ctx context.Context, cfg port | |||||
| 12268 | } | 12268 | } | ||
| 12269 | 12269 | ||||
| 12270 | var r0 error | 12270 | var r0 error | ||
| 12271 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, port.WebUIConfig) error); ok { | 12271 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, dto.WebUIConfig) error); ok { |
| 12272 | r0 = returnFunc(ctx, cfg) | 12272 | r0 = returnFunc(ctx, cfg) | ||
| 12273 | } else { | 12273 | } else { | ||
| 12274 | r0 = ret.Error(0) | 12274 | r0 = ret.Error(0) | ||
| @@ -12283,20 +12283,20 @@ type MockWebUIConfigSaver_SaveWebUIConfig_Call struct { | |||||
| 12283 | 12283 | ||||
| 12284 | // SaveWebUIConfig is a helper method to define mock.On call | 12284 | // SaveWebUIConfig is a helper method to define mock.On call | ||
| 12285 | // - ctx context.Context | 12285 | // - ctx context.Context | ||
| 12286 | - | // - cfg port.WebUIConfig | 12286 | + | // - cfg dto.WebUIConfig |
| 12287 | func (_e *MockWebUIConfigSaver_Expecter) SaveWebUIConfig(ctx interface{}, cfg interface{}) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | 12287 | func (_e *MockWebUIConfigSaver_Expecter) SaveWebUIConfig(ctx interface{}, cfg interface{}) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | ||
| 12288 | return &MockWebUIConfigSaver_SaveWebUIConfig_Call{Call: _e.mock.On("SaveWebUIConfig", ctx, cfg)} | 12288 | return &MockWebUIConfigSaver_SaveWebUIConfig_Call{Call: _e.mock.On("SaveWebUIConfig", ctx, cfg)} | ||
| 12289 | } | 12289 | } | ||
| 12290 | 12290 | ||||
| 12291 | - | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Run(run func(ctx context.Context, cfg port.WebUIConfig)) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | 12291 | + | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Run(run func(ctx context.Context, cfg dto.WebUIConfig)) *MockWebUIConfigSaver_SaveWebUIConfig_Call { |
| 12292 | _c.Call.Run(func(args mock.Arguments) { | 12292 | _c.Call.Run(func(args mock.Arguments) { | ||
| 12293 | var arg0 context.Context | 12293 | var arg0 context.Context | ||
| 12294 | if args[0] != nil { | 12294 | if args[0] != nil { | ||
| 12295 | arg0 = args[0].(context.Context) | 12295 | arg0 = args[0].(context.Context) | ||
| 12296 | } | 12296 | } | ||
| 12297 | - | var arg1 port.WebUIConfig | 12297 | + | var arg1 dto.WebUIConfig |
| 12298 | if args[1] != nil { | 12298 | if args[1] != nil { | ||
| 12299 | - | arg1 = args[1].(port.WebUIConfig) | 12299 | + | arg1 = args[1].(dto.WebUIConfig) |
| 12300 | } | 12300 | } | ||
| 12301 | run( | 12301 | run( | ||
| 12302 | arg0, | 12302 | arg0, | ||
| @@ -12311,7 +12311,7 @@ func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) Return(err error) *MockWebU | |||||
| 12311 | return _c | 12311 | return _c | ||
| 12312 | } | 12312 | } | ||
| 12313 | 12313 | ||||
| 12314 | - | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) RunAndReturn(run func(ctx context.Context, cfg port.WebUIConfig) error) *MockWebUIConfigSaver_SaveWebUIConfig_Call { | 12314 | + | func (_c *MockWebUIConfigSaver_SaveWebUIConfig_Call) RunAndReturn(run func(ctx context.Context, cfg dto.WebUIConfig) error) *MockWebUIConfigSaver_SaveWebUIConfig_Call { |
| 12315 | _c.Call.Return(run) | 12315 | _c.Call.Return(run) | ||
| 12316 | return _c | 12316 | return _c | ||
| 12317 | } | 12317 | } | ||
M internal/application/port/systemviews.go +4 -3
| @@ -3,6 +3,7 @@ package port | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | ||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 6 | 7 | "github.com/bnema/dumber/internal/domain/entity" | |
| 7 | 8 | ) | |
| 8 | 9 | ||
| @@ -40,9 +41,9 @@ type SystemviewFavoritesService interface { | |||
| 40 | 41 | ||
| 41 | 42 | // SystemviewConfigService exposes config and keybinding operations for the systemviews UI. | |
| 42 | 43 | type SystemviewConfigService interface { | |
| 43 | - | Current(ctx context.Context) (SystemviewConfigPayload, error) | |
| 44 | - | Default(ctx context.Context) (SystemviewConfigPayload, error) | |
| 45 | - | Save(ctx context.Context, cfg WebUIConfig) error | |
| 44 | + | Current(ctx context.Context) (dto.SystemviewConfigPayload, error) | |
| 45 | + | Default(ctx context.Context) (dto.SystemviewConfigPayload, error) | |
| 46 | + | Save(ctx context.Context, cfg dto.WebUIConfig) error | |
| 46 | 47 | GetKeybindings(ctx context.Context) (KeybindingsConfig, error) | |
| 47 | 48 | SetKeybinding(ctx context.Context, req SetKeybindingRequest) (SetKeybindingResponse, error) | |
| 48 | 49 | ResetKeybinding(ctx context.Context, req ResetKeybindingRequest) error | |
| @@ -3,6 +3,7 @@ package port | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 6 | "github.com/bnema/dumber/internal/domain/entity" | 7 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| @@ -40,9 +41,9 @@ type SystemviewFavoritesService interface { | |||||
| 40 | 41 | ||||
| 41 | // SystemviewConfigService exposes config and keybinding operations for the systemviews UI. | 42 | // SystemviewConfigService exposes config and keybinding operations for the systemviews UI. | ||
| 42 | type SystemviewConfigService interface { | 43 | type SystemviewConfigService interface { | ||
| 43 | - | Current(ctx context.Context) (SystemviewConfigPayload, error) | 44 | + | Current(ctx context.Context) (dto.SystemviewConfigPayload, error) |
| 44 | - | Default(ctx context.Context) (SystemviewConfigPayload, error) | 45 | + | Default(ctx context.Context) (dto.SystemviewConfigPayload, error) |
| 45 | - | Save(ctx context.Context, cfg WebUIConfig) error | 46 | + | Save(ctx context.Context, cfg dto.WebUIConfig) error |
| 46 | GetKeybindings(ctx context.Context) (KeybindingsConfig, error) | 47 | GetKeybindings(ctx context.Context) (KeybindingsConfig, error) | ||
| 47 | SetKeybinding(ctx context.Context, req SetKeybindingRequest) (SetKeybindingResponse, error) | 48 | SetKeybinding(ctx context.Context, req SetKeybindingRequest) (SetKeybindingResponse, error) | ||
| 48 | ResetKeybinding(ctx context.Context, req ResetKeybindingRequest) error | 49 | ResetKeybinding(ctx context.Context, req ResetKeybindingRequest) error | ||
M internal/application/port/webui_config.go +7 -94
| @@ -1,105 +1,18 @@ | |||
| 1 | 1 | package port | |
| 2 | 2 | ||
| 3 | - | import "context" | |
| 3 | + | import ( | |
| 4 | + | "context" | |
| 4 | 5 | ||
| 5 | - | // WebUIConfig represents the subset of configuration editable in dumb://config. | |
| 6 | - | type WebUIConfig struct { | |
| 7 | - | Appearance WebUIAppearanceConfig `json:"appearance"` | |
| 8 | - | Performance WebUIPerformanceConfig `json:"performance"` | |
| 9 | - | DefaultUIScale float64 `json:"default_ui_scale"` | |
| 10 | - | DefaultSearchEngine string `json:"default_search_engine"` | |
| 11 | - | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |
| 12 | - | } | |
| 13 | - | ||
| 14 | - | // SystemviewCustomPerformancePayload is the editable custom performance profile shape. | |
| 15 | - | type SystemviewCustomPerformancePayload = WebUICustomPerformanceConfig | |
| 16 | - | ||
| 17 | - | // SystemviewResolvedPerformancePayload is the resolved effective performance shape. | |
| 18 | - | type SystemviewResolvedPerformancePayload struct { | |
| 19 | - | SkiaCPUThreads int `json:"skia_cpu_threads"` | |
| 20 | - | SkiaGPUThreads int `json:"skia_gpu_threads"` | |
| 21 | - | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |
| 22 | - | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |
| 23 | - | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |
| 24 | - | ConservativeThreshold float64 `json:"conservative_threshold"` | |
| 25 | - | StrictThreshold float64 `json:"strict_threshold"` | |
| 26 | - | } | |
| 27 | - | ||
| 28 | - | // SystemviewHardwarePayload exposes detected hardware to the settings UI. | |
| 29 | - | type SystemviewHardwarePayload struct { | |
| 30 | - | CPUCores int `json:"cpu_cores"` | |
| 31 | - | CPUThreads int `json:"cpu_threads"` | |
| 32 | - | TotalRAMMB int `json:"total_ram_mb"` | |
| 33 | - | GPUVendor string `json:"gpu_vendor"` | |
| 34 | - | GPUName string `json:"gpu_name"` | |
| 35 | - | VRAMMB int `json:"vram_mb"` | |
| 36 | - | } | |
| 37 | - | ||
| 38 | - | // SystemviewPerformancePayload is the performance tab payload expected by the UI. | |
| 39 | - | type SystemviewPerformancePayload struct { | |
| 40 | - | Profile string `json:"profile"` | |
| 41 | - | Custom SystemviewCustomPerformancePayload `json:"custom"` | |
| 42 | - | Resolved SystemviewResolvedPerformancePayload `json:"resolved"` | |
| 43 | - | Hardware SystemviewHardwarePayload `json:"hardware"` | |
| 44 | - | } | |
| 45 | - | ||
| 46 | - | // SystemviewConfigPayload is the full JSON shape expected by dumb://config. | |
| 47 | - | type SystemviewConfigPayload struct { | |
| 48 | - | Appearance WebUIAppearanceConfig `json:"appearance"` | |
| 49 | - | Performance SystemviewPerformancePayload `json:"performance"` | |
| 50 | - | DefaultUIScale float64 `json:"default_ui_scale"` | |
| 51 | - | DefaultSearchEngine string `json:"default_search_engine"` | |
| 52 | - | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |
| 53 | - | EngineType string `json:"engine_type"` | |
| 54 | - | } | |
| 55 | - | ||
| 56 | - | // WebUIPerformanceConfig represents performance settings editable in dumb://config. | |
| 57 | - | type WebUIPerformanceConfig struct { | |
| 58 | - | Profile string `json:"profile"` | |
| 59 | - | Custom WebUICustomPerformanceConfig `json:"custom"` | |
| 60 | - | } | |
| 61 | - | ||
| 62 | - | // WebUICustomPerformanceConfig holds user-editable fields for custom profile. | |
| 63 | - | type WebUICustomPerformanceConfig struct { | |
| 64 | - | SkiaCPUThreads int `json:"skia_cpu_threads"` | |
| 65 | - | SkiaGPUThreads int `json:"skia_gpu_threads"` | |
| 66 | - | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |
| 67 | - | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |
| 68 | - | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |
| 69 | - | } | |
| 70 | - | ||
| 71 | - | type WebUIAppearanceConfig struct { | |
| 72 | - | SansFont string `json:"sans_font"` | |
| 73 | - | SerifFont string `json:"serif_font"` | |
| 74 | - | MonospaceFont string `json:"monospace_font"` | |
| 75 | - | DefaultFontSize int `json:"default_font_size"` | |
| 76 | - | ColorScheme string `json:"color_scheme"` | |
| 77 | - | LightPalette ColorPalette `json:"light_palette"` | |
| 78 | - | DarkPalette ColorPalette `json:"dark_palette"` | |
| 79 | - | } | |
| 80 | - | ||
| 81 | - | type ColorPalette struct { | |
| 82 | - | Background string `json:"background"` | |
| 83 | - | Surface string `json:"surface"` | |
| 84 | - | SurfaceVariant string `json:"surface_variant"` | |
| 85 | - | Text string `json:"text"` | |
| 86 | - | Muted string `json:"muted"` | |
| 87 | - | Accent string `json:"accent"` | |
| 88 | - | Border string `json:"border"` | |
| 89 | - | } | |
| 90 | - | ||
| 91 | - | type SearchShortcut struct { | |
| 92 | - | URL string `json:"url"` | |
| 93 | - | Description string `json:"description"` | |
| 94 | - | } | |
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | + | ) | |
| 95 | 8 | ||
| 96 | 9 | // SystemviewConfigReader reads the current and default systemview config payloads. | |
| 97 | 10 | type SystemviewConfigReader interface { | |
| 98 | - | Current(ctx context.Context) (SystemviewConfigPayload, error) | |
| 99 | - | Default(ctx context.Context) (SystemviewConfigPayload, error) | |
| 11 | + | Current(ctx context.Context) (dto.SystemviewConfigPayload, error) | |
| 12 | + | Default(ctx context.Context) (dto.SystemviewConfigPayload, error) | |
| 100 | 13 | } | |
| 101 | 14 | ||
| 102 | 15 | // WebUIConfigSaver persists WebUI configuration changes. | |
| 103 | 16 | type WebUIConfigSaver interface { | |
| 104 | - | SaveWebUIConfig(ctx context.Context, cfg WebUIConfig) error | |
| 17 | + | SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error | |
| 105 | 18 | } | |
| @@ -1,105 +1,18 @@ | |||||
| 1 | package port | 1 | package port | ||
| 2 | 2 | ||||
| 3 | - | import "context" | 3 | + | import ( |
| 4 | + | "context" | |||
| 4 | 5 | ||||
| 5 | - | // WebUIConfig represents the subset of configuration editable in dumb://config. | 6 | + | "github.com/bnema/dumber/internal/application/dto" |
| 6 | - | type WebUIConfig struct { | 7 | + | ) |
| 7 | - | Appearance WebUIAppearanceConfig `json:"appearance"` | |||
| 8 | - | Performance WebUIPerformanceConfig `json:"performance"` | |||
| 9 | - | DefaultUIScale float64 `json:"default_ui_scale"` | |||
| 10 | - | DefaultSearchEngine string `json:"default_search_engine"` | |||
| 11 | - | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |||
| 12 | - | } | |||
| 13 | - | ||||
| 14 | - | // SystemviewCustomPerformancePayload is the editable custom performance profile shape. | |||
| 15 | - | type SystemviewCustomPerformancePayload = WebUICustomPerformanceConfig | |||
| 16 | - | ||||
| 17 | - | // SystemviewResolvedPerformancePayload is the resolved effective performance shape. | |||
| 18 | - | type SystemviewResolvedPerformancePayload struct { | |||
| 19 | - | SkiaCPUThreads int `json:"skia_cpu_threads"` | |||
| 20 | - | SkiaGPUThreads int `json:"skia_gpu_threads"` | |||
| 21 | - | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |||
| 22 | - | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |||
| 23 | - | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |||
| 24 | - | ConservativeThreshold float64 `json:"conservative_threshold"` | |||
| 25 | - | StrictThreshold float64 `json:"strict_threshold"` | |||
| 26 | - | } | |||
| 27 | - | ||||
| 28 | - | // SystemviewHardwarePayload exposes detected hardware to the settings UI. | |||
| 29 | - | type SystemviewHardwarePayload struct { | |||
| 30 | - | CPUCores int `json:"cpu_cores"` | |||
| 31 | - | CPUThreads int `json:"cpu_threads"` | |||
| 32 | - | TotalRAMMB int `json:"total_ram_mb"` | |||
| 33 | - | GPUVendor string `json:"gpu_vendor"` | |||
| 34 | - | GPUName string `json:"gpu_name"` | |||
| 35 | - | VRAMMB int `json:"vram_mb"` | |||
| 36 | - | } | |||
| 37 | - | ||||
| 38 | - | // SystemviewPerformancePayload is the performance tab payload expected by the UI. | |||
| 39 | - | type SystemviewPerformancePayload struct { | |||
| 40 | - | Profile string `json:"profile"` | |||
| 41 | - | Custom SystemviewCustomPerformancePayload `json:"custom"` | |||
| 42 | - | Resolved SystemviewResolvedPerformancePayload `json:"resolved"` | |||
| 43 | - | Hardware SystemviewHardwarePayload `json:"hardware"` | |||
| 44 | - | } | |||
| 45 | - | ||||
| 46 | - | // SystemviewConfigPayload is the full JSON shape expected by dumb://config. | |||
| 47 | - | type SystemviewConfigPayload struct { | |||
| 48 | - | Appearance WebUIAppearanceConfig `json:"appearance"` | |||
| 49 | - | Performance SystemviewPerformancePayload `json:"performance"` | |||
| 50 | - | DefaultUIScale float64 `json:"default_ui_scale"` | |||
| 51 | - | DefaultSearchEngine string `json:"default_search_engine"` | |||
| 52 | - | SearchShortcuts map[string]SearchShortcut `json:"search_shortcuts"` | |||
| 53 | - | EngineType string `json:"engine_type"` | |||
| 54 | - | } | |||
| 55 | - | ||||
| 56 | - | // WebUIPerformanceConfig represents performance settings editable in dumb://config. | |||
| 57 | - | type WebUIPerformanceConfig struct { | |||
| 58 | - | Profile string `json:"profile"` | |||
| 59 | - | Custom WebUICustomPerformanceConfig `json:"custom"` | |||
| 60 | - | } | |||
| 61 | - | ||||
| 62 | - | // WebUICustomPerformanceConfig holds user-editable fields for custom profile. | |||
| 63 | - | type WebUICustomPerformanceConfig struct { | |||
| 64 | - | SkiaCPUThreads int `json:"skia_cpu_threads"` | |||
| 65 | - | SkiaGPUThreads int `json:"skia_gpu_threads"` | |||
| 66 | - | WebProcessMemoryMB int `json:"web_process_memory_mb"` | |||
| 67 | - | NetworkProcessMemoryMB int `json:"network_process_memory_mb"` | |||
| 68 | - | WebViewPoolPrewarm int `json:"webview_pool_prewarm"` | |||
| 69 | - | } | |||
| 70 | - | ||||
| 71 | - | type WebUIAppearanceConfig struct { | |||
| 72 | - | SansFont string `json:"sans_font"` | |||
| 73 | - | SerifFont string `json:"serif_font"` | |||
| 74 | - | MonospaceFont string `json:"monospace_font"` | |||
| 75 | - | DefaultFontSize int `json:"default_font_size"` | |||
| 76 | - | ColorScheme string `json:"color_scheme"` | |||
| 77 | - | LightPalette ColorPalette `json:"light_palette"` | |||
| 78 | - | DarkPalette ColorPalette `json:"dark_palette"` | |||
| 79 | - | } | |||
| 80 | - | ||||
| 81 | - | type ColorPalette struct { | |||
| 82 | - | Background string `json:"background"` | |||
| 83 | - | Surface string `json:"surface"` | |||
| 84 | - | SurfaceVariant string `json:"surface_variant"` | |||
| 85 | - | Text string `json:"text"` | |||
| 86 | - | Muted string `json:"muted"` | |||
| 87 | - | Accent string `json:"accent"` | |||
| 88 | - | Border string `json:"border"` | |||
| 89 | - | } | |||
| 90 | - | ||||
| 91 | - | type SearchShortcut struct { | |||
| 92 | - | URL string `json:"url"` | |||
| 93 | - | Description string `json:"description"` | |||
| 94 | - | } | |||
| 95 | 8 | ||||
| 96 | // SystemviewConfigReader reads the current and default systemview config payloads. | 9 | // SystemviewConfigReader reads the current and default systemview config payloads. | ||
| 97 | type SystemviewConfigReader interface { | 10 | type SystemviewConfigReader interface { | ||
| 98 | - | Current(ctx context.Context) (SystemviewConfigPayload, error) | 11 | + | Current(ctx context.Context) (dto.SystemviewConfigPayload, error) |
| 99 | - | Default(ctx context.Context) (SystemviewConfigPayload, error) | 12 | + | Default(ctx context.Context) (dto.SystemviewConfigPayload, error) |
| 100 | } | 13 | } | ||
| 101 | 14 | ||||
| 102 | // WebUIConfigSaver persists WebUI configuration changes. | 15 | // WebUIConfigSaver persists WebUI configuration changes. | ||
| 103 | type WebUIConfigSaver interface { | 16 | type WebUIConfigSaver interface { | ||
| 104 | - | SaveWebUIConfig(ctx context.Context, cfg WebUIConfig) error | 17 | + | SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error |
| 105 | } | 18 | } | ||
M internal/application/usecase/read_systemview_config.go +5 -4
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| @@ -24,17 +25,17 @@ func NewReadSystemviewConfigUseCase(reader port.SystemviewConfigReader) *ReadSys | |||
| 24 | 25 | var ErrNilSystemviewConfigReader = errors.New("systemview config reader is nil") | |
| 25 | 26 | ||
| 26 | 27 | // Current returns the current systemview config payload. | |
| 27 | - | func (uc *ReadSystemviewConfigUseCase) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 28 | + | func (uc *ReadSystemviewConfigUseCase) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 28 | 29 | if uc == nil || uc.reader == nil { | |
| 29 | - | return port.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | |
| 30 | + | return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | |
| 30 | 31 | } | |
| 31 | 32 | return uc.reader.Current(ctx) | |
| 32 | 33 | } | |
| 33 | 34 | ||
| 34 | 35 | // Default returns the default systemview config payload. | |
| 35 | - | func (uc *ReadSystemviewConfigUseCase) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 36 | + | func (uc *ReadSystemviewConfigUseCase) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 36 | 37 | if uc == nil || uc.reader == nil { | |
| 37 | - | return port.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | |
| 38 | + | return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | |
| 38 | 39 | } | |
| 39 | 40 | return uc.reader.Default(ctx) | |
| 40 | 41 | } | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 7 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| @@ -24,17 +25,17 @@ func NewReadSystemviewConfigUseCase(reader port.SystemviewConfigReader) *ReadSys | |||||
| 24 | var ErrNilSystemviewConfigReader = errors.New("systemview config reader is nil") | 25 | var ErrNilSystemviewConfigReader = errors.New("systemview config reader is nil") | ||
| 25 | 26 | ||||
| 26 | // Current returns the current systemview config payload. | 27 | // Current returns the current systemview config payload. | ||
| 27 | - | func (uc *ReadSystemviewConfigUseCase) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | 28 | + | func (uc *ReadSystemviewConfigUseCase) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 28 | if uc == nil || uc.reader == nil { | 29 | if uc == nil || uc.reader == nil { | ||
| 29 | - | return port.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | 30 | + | return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader |
| 30 | } | 31 | } | ||
| 31 | return uc.reader.Current(ctx) | 32 | return uc.reader.Current(ctx) | ||
| 32 | } | 33 | } | ||
| 33 | 34 | ||||
| 34 | // Default returns the default systemview config payload. | 35 | // Default returns the default systemview config payload. | ||
| 35 | - | func (uc *ReadSystemviewConfigUseCase) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | 36 | + | func (uc *ReadSystemviewConfigUseCase) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 36 | if uc == nil || uc.reader == nil { | 37 | if uc == nil || uc.reader == nil { | ||
| 37 | - | return port.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader | 38 | + | return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader |
| 38 | } | 39 | } | ||
| 39 | return uc.reader.Default(ctx) | 40 | return uc.reader.Default(ctx) | ||
| 40 | } | 41 | } | ||
M internal/application/usecase/read_systemview_config_test.go +3 -3
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 8 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 9 | 9 | "github.com/stretchr/testify/mock" | |
| 10 | 10 | "github.com/stretchr/testify/require" | |
| @@ -12,7 +12,7 @@ import ( | |||
| 12 | 12 | ||
| 13 | 13 | func TestReadSystemviewConfigUseCase_Current(t *testing.T) { | |
| 14 | 14 | reader := portmocks.NewMockSystemviewConfigReader(t) | |
| 15 | - | want := port.SystemviewConfigPayload{EngineType: "webkit"} | |
| 15 | + | want := dto.SystemviewConfigPayload{EngineType: "webkit"} | |
| 16 | 16 | reader.EXPECT().Current(mock.Anything).Return(want, nil).Once() | |
| 17 | 17 | ||
| 18 | 18 | uc := NewReadSystemviewConfigUseCase(reader) | |
| @@ -24,7 +24,7 @@ func TestReadSystemviewConfigUseCase_Current(t *testing.T) { | |||
| 24 | 24 | ||
| 25 | 25 | func TestReadSystemviewConfigUseCase_Default(t *testing.T) { | |
| 26 | 26 | reader := portmocks.NewMockSystemviewConfigReader(t) | |
| 27 | - | want := port.SystemviewConfigPayload{EngineType: "webkit"} | |
| 27 | + | want := dto.SystemviewConfigPayload{EngineType: "webkit"} | |
| 28 | 28 | reader.EXPECT().Default(mock.Anything).Return(want, nil).Once() | |
| 29 | 29 | ||
| 30 | 30 | uc := NewReadSystemviewConfigUseCase(reader) | |
| @@ -4,7 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "testing" | 5 | "testing" | ||
| 6 | 6 | ||||
| 7 | - | "github.com/bnema/dumber/internal/application/port" | 7 | + | "github.com/bnema/dumber/internal/application/dto" |
| 8 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | 8 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | ||
| 9 | "github.com/stretchr/testify/mock" | 9 | "github.com/stretchr/testify/mock" | ||
| 10 | "github.com/stretchr/testify/require" | 10 | "github.com/stretchr/testify/require" | ||
| @@ -12,7 +12,7 @@ import ( | |||||
| 12 | 12 | ||||
| 13 | func TestReadSystemviewConfigUseCase_Current(t *testing.T) { | 13 | func TestReadSystemviewConfigUseCase_Current(t *testing.T) { | ||
| 14 | reader := portmocks.NewMockSystemviewConfigReader(t) | 14 | reader := portmocks.NewMockSystemviewConfigReader(t) | ||
| 15 | - | want := port.SystemviewConfigPayload{EngineType: "webkit"} | 15 | + | want := dto.SystemviewConfigPayload{EngineType: "webkit"} |
| 16 | reader.EXPECT().Current(mock.Anything).Return(want, nil).Once() | 16 | reader.EXPECT().Current(mock.Anything).Return(want, nil).Once() | ||
| 17 | 17 | ||||
| 18 | uc := NewReadSystemviewConfigUseCase(reader) | 18 | uc := NewReadSystemviewConfigUseCase(reader) | ||
| @@ -24,7 +24,7 @@ func TestReadSystemviewConfigUseCase_Current(t *testing.T) { | |||||
| 24 | 24 | ||||
| 25 | func TestReadSystemviewConfigUseCase_Default(t *testing.T) { | 25 | func TestReadSystemviewConfigUseCase_Default(t *testing.T) { | ||
| 26 | reader := portmocks.NewMockSystemviewConfigReader(t) | 26 | reader := portmocks.NewMockSystemviewConfigReader(t) | ||
| 27 | - | want := port.SystemviewConfigPayload{EngineType: "webkit"} | 27 | + | want := dto.SystemviewConfigPayload{EngineType: "webkit"} |
| 28 | reader.EXPECT().Default(mock.Anything).Return(want, nil).Once() | 28 | reader.EXPECT().Default(mock.Anything).Return(want, nil).Once() | ||
| 29 | 29 | ||||
| 30 | uc := NewReadSystemviewConfigUseCase(reader) | 30 | uc := NewReadSystemviewConfigUseCase(reader) | ||
M internal/application/usecase/save_webui_config.go +8 -4
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | "math" | |
| 7 | 7 | "strings" | |
| 8 | 8 | ||
| 9 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 9 | 10 | "github.com/bnema/dumber/internal/application/port" | |
| 10 | 11 | "github.com/bnema/dumber/internal/domain/validation" | |
| 11 | 12 | ) | |
| @@ -18,7 +19,7 @@ func NewSaveWebUIConfigUseCase(saver port.WebUIConfigSaver) *SaveWebUIConfigUseC | |||
| 18 | 19 | return &SaveWebUIConfigUseCase{saver: saver} | |
| 19 | 20 | } | |
| 20 | 21 | ||
| 21 | - | func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg port.WebUIConfig) error { | |
| 22 | + | func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 22 | 23 | if uc == nil || uc.saver == nil { | |
| 23 | 24 | return fmt.Errorf("config saver is nil") | |
| 24 | 25 | } | |
| @@ -31,16 +32,19 @@ func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg port.WebUICon | |||
| 31 | 32 | return uc.saver.SaveWebUIConfig(ctx, normalized) | |
| 32 | 33 | } | |
| 33 | 34 | ||
| 34 | - | func normalizeWebUIConfig(cfg port.WebUIConfig) port.WebUIConfig { | |
| 35 | + | func normalizeWebUIConfig(cfg dto.WebUIConfig) dto.WebUIConfig { | |
| 35 | 36 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | |
| 36 | 37 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | |
| 37 | 38 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | |
| 38 | 39 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | |
| 39 | 40 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | |
| 40 | 41 | if len(cfg.SearchShortcuts) > 0 { | |
| 41 | - | normalized := make(map[string]port.SearchShortcut, len(cfg.SearchShortcuts)) | |
| 42 | + | normalized := make(map[string]dto.SearchShortcut, len(cfg.SearchShortcuts)) | |
| 42 | 43 | for key, shortcut := range cfg.SearchShortcuts { | |
| 43 | 44 | trimmedKey := strings.TrimSpace(key) | |
| 45 | + | if trimmedKey == "" { | |
| 46 | + | continue | |
| 47 | + | } | |
| 44 | 48 | shortcut.URL = strings.TrimSpace(shortcut.URL) | |
| 45 | 49 | shortcut.Description = strings.TrimSpace(shortcut.Description) | |
| 46 | 50 | normalized[trimmedKey] = shortcut | |
| @@ -50,7 +54,7 @@ func normalizeWebUIConfig(cfg port.WebUIConfig) port.WebUIConfig { | |||
| 50 | 54 | return cfg | |
| 51 | 55 | } | |
| 52 | 56 | ||
| 53 | - | func validateWebUIConfig(cfg port.WebUIConfig) error { | |
| 57 | + | func validateWebUIConfig(cfg dto.WebUIConfig) error { | |
| 54 | 58 | var errs []string | |
| 55 | 59 | ||
| 56 | 60 | if cfg.Appearance.DefaultFontSize < 1 || cfg.Appearance.DefaultFontSize > 72 { | |
| @@ -6,6 +6,7 @@ import ( | |||||
| 6 | "math" | 6 | "math" | ||
| 7 | "strings" | 7 | "strings" | ||
| 8 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 9 | "github.com/bnema/dumber/internal/application/port" | 10 | "github.com/bnema/dumber/internal/application/port" | ||
| 10 | "github.com/bnema/dumber/internal/domain/validation" | 11 | "github.com/bnema/dumber/internal/domain/validation" | ||
| 11 | ) | 12 | ) | ||
| @@ -18,7 +19,7 @@ func NewSaveWebUIConfigUseCase(saver port.WebUIConfigSaver) *SaveWebUIConfigUseC | |||||
| 18 | return &SaveWebUIConfigUseCase{saver: saver} | 19 | return &SaveWebUIConfigUseCase{saver: saver} | ||
| 19 | } | 20 | } | ||
| 20 | 21 | ||||
| 21 | - | func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg port.WebUIConfig) error { | 22 | + | func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg dto.WebUIConfig) error { |
| 22 | if uc == nil || uc.saver == nil { | 23 | if uc == nil || uc.saver == nil { | ||
| 23 | return fmt.Errorf("config saver is nil") | 24 | return fmt.Errorf("config saver is nil") | ||
| 24 | } | 25 | } | ||
| @@ -31,16 +32,19 @@ func (uc *SaveWebUIConfigUseCase) Execute(ctx context.Context, cfg port.WebUICon | |||||
| 31 | return uc.saver.SaveWebUIConfig(ctx, normalized) | 32 | return uc.saver.SaveWebUIConfig(ctx, normalized) | ||
| 32 | } | 33 | } | ||
| 33 | 34 | ||||
| 34 | - | func normalizeWebUIConfig(cfg port.WebUIConfig) port.WebUIConfig { | 35 | + | func normalizeWebUIConfig(cfg dto.WebUIConfig) dto.WebUIConfig { |
| 35 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | 36 | cfg.Appearance.SansFont = strings.TrimSpace(cfg.Appearance.SansFont) | ||
| 36 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | 37 | cfg.Appearance.SerifFont = strings.TrimSpace(cfg.Appearance.SerifFont) | ||
| 37 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | 38 | cfg.Appearance.MonospaceFont = strings.TrimSpace(cfg.Appearance.MonospaceFont) | ||
| 38 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | 39 | cfg.Appearance.ColorScheme = strings.TrimSpace(cfg.Appearance.ColorScheme) | ||
| 39 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | 40 | cfg.DefaultSearchEngine = strings.TrimSpace(cfg.DefaultSearchEngine) | ||
| 40 | if len(cfg.SearchShortcuts) > 0 { | 41 | if len(cfg.SearchShortcuts) > 0 { | ||
| 41 | - | normalized := make(map[string]port.SearchShortcut, len(cfg.SearchShortcuts)) | 42 | + | normalized := make(map[string]dto.SearchShortcut, len(cfg.SearchShortcuts)) |
| 42 | for key, shortcut := range cfg.SearchShortcuts { | 43 | for key, shortcut := range cfg.SearchShortcuts { | ||
| 43 | trimmedKey := strings.TrimSpace(key) | 44 | trimmedKey := strings.TrimSpace(key) | ||
| 45 | + | if trimmedKey == "" { | |||
| 46 | + | continue | |||
| 47 | + | } | |||
| 44 | shortcut.URL = strings.TrimSpace(shortcut.URL) | 48 | shortcut.URL = strings.TrimSpace(shortcut.URL) | ||
| 45 | shortcut.Description = strings.TrimSpace(shortcut.Description) | 49 | shortcut.Description = strings.TrimSpace(shortcut.Description) | ||
| 46 | normalized[trimmedKey] = shortcut | 50 | normalized[trimmedKey] = shortcut | ||
| @@ -50,7 +54,7 @@ func normalizeWebUIConfig(cfg port.WebUIConfig) port.WebUIConfig { | |||||
| 50 | return cfg | 54 | return cfg | ||
| 51 | } | 55 | } | ||
| 52 | 56 | ||||
| 53 | - | func validateWebUIConfig(cfg port.WebUIConfig) error { | 57 | + | func validateWebUIConfig(cfg dto.WebUIConfig) error { |
| 54 | var errs []string | 58 | var errs []string | ||
| 55 | 59 | ||||
| 56 | if cfg.Appearance.DefaultFontSize < 1 || cfg.Appearance.DefaultFontSize > 72 { | 60 | if cfg.Appearance.DefaultFontSize < 1 || cfg.Appearance.DefaultFontSize > 72 { | ||
M internal/application/usecase/save_webui_config_test.go +17 -22
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "math" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | - | "github.com/bnema/dumber/internal/application/port" | |
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 9 | 9 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 10 | 10 | "github.com/bnema/dumber/internal/application/usecase" | |
| 11 | 11 | "github.com/stretchr/testify/mock" | |
| @@ -15,40 +15,33 @@ import ( | |||
| 15 | 15 | func TestSaveWebUIConfigUseCase_Validation(t *testing.T) { | |
| 16 | 16 | tests := []struct { | |
| 17 | 17 | name string | |
| 18 | - | mutate func(*port.WebUIConfig) | |
| 18 | + | mutate func(*dto.WebUIConfig) | |
| 19 | 19 | wantErr string | |
| 20 | 20 | }{ | |
| 21 | 21 | { | |
| 22 | 22 | name: "rejects invalid default search URL", | |
| 23 | - | mutate: func(cfg *port.WebUIConfig) { | |
| 23 | + | mutate: func(cfg *dto.WebUIConfig) { | |
| 24 | 24 | cfg.DefaultSearchEngine = "not-a-url-%s" | |
| 25 | 25 | }, | |
| 26 | 26 | wantErr: "default_search_engine", | |
| 27 | 27 | }, | |
| 28 | - | { | |
| 29 | - | name: "rejects blank search shortcut key", | |
| 30 | - | mutate: func(cfg *port.WebUIConfig) { | |
| 31 | - | cfg.SearchShortcuts[" "] = port.SearchShortcut{URL: "https://example.com?q=%s", Description: "Example"} | |
| 32 | - | }, | |
| 33 | - | wantErr: "shortcut key cannot be empty", | |
| 34 | - | }, | |
| 35 | 28 | { | |
| 36 | 29 | name: "rejects invalid performance profile", | |
| 37 | - | mutate: func(cfg *port.WebUIConfig) { | |
| 30 | + | mutate: func(cfg *dto.WebUIConfig) { | |
| 38 | 31 | cfg.Performance.Profile = "turbo" | |
| 39 | 32 | }, | |
| 40 | 33 | wantErr: "performance.profile", | |
| 41 | 34 | }, | |
| 42 | 35 | { | |
| 43 | 36 | name: "rejects NaN UI scale", | |
| 44 | - | mutate: func(cfg *port.WebUIConfig) { | |
| 37 | + | mutate: func(cfg *dto.WebUIConfig) { | |
| 45 | 38 | cfg.DefaultUIScale = math.NaN() | |
| 46 | 39 | }, | |
| 47 | 40 | wantErr: "default_ui_scale must be a finite value", | |
| 48 | 41 | }, | |
| 49 | 42 | { | |
| 50 | 43 | name: "rejects infinite UI scale", | |
| 51 | - | mutate: func(cfg *port.WebUIConfig) { | |
| 44 | + | mutate: func(cfg *dto.WebUIConfig) { | |
| 52 | 45 | cfg.DefaultUIScale = math.Inf(1) | |
| 53 | 46 | }, | |
| 54 | 47 | wantErr: "default_ui_scale must be a finite value", | |
| @@ -72,25 +65,27 @@ func TestSaveWebUIConfigUseCase_Validation(t *testing.T) { | |||
| 72 | 65 | ||
| 73 | 66 | func TestSaveWebUIConfigUseCase_NormalizesAndSavesValidConfig(t *testing.T) { | |
| 74 | 67 | saver := portmocks.NewMockWebUIConfigSaver(t) | |
| 75 | - | var saved port.WebUIConfig | |
| 76 | - | saver.EXPECT().SaveWebUIConfig(mock.Anything, mock.AnythingOfType("port.WebUIConfig")).Run(func(_ context.Context, cfg port.WebUIConfig) { | |
| 68 | + | var saved dto.WebUIConfig | |
| 69 | + | saver.EXPECT().SaveWebUIConfig(mock.Anything, mock.AnythingOfType("dto.WebUIConfig")).Run(func(_ context.Context, cfg dto.WebUIConfig) { | |
| 77 | 70 | saved = cfg | |
| 78 | 71 | }).Return(nil).Once() | |
| 79 | 72 | uc := usecase.NewSaveWebUIConfigUseCase(saver) | |
| 80 | 73 | cfg := validWebUIConfig() | |
| 81 | - | cfg.SearchShortcuts[" ddg "] = port.SearchShortcut{URL: " https://duckduckgo.com/?q=%s ", Description: " DuckDuckGo "} | |
| 74 | + | cfg.SearchShortcuts[" ddg "] = dto.SearchShortcut{URL: " https://duckduckgo.com/?q=%s ", Description: " DuckDuckGo "} | |
| 75 | + | cfg.SearchShortcuts[" "] = dto.SearchShortcut{URL: "https://example.com?q=%s", Description: "Example"} | |
| 82 | 76 | ||
| 83 | 77 | err := uc.Execute(context.Background(), cfg) | |
| 84 | 78 | ||
| 85 | 79 | require.NoError(t, err) | |
| 86 | 80 | require.NotContains(t, saved.SearchShortcuts, " ddg ") | |
| 81 | + | require.NotContains(t, saved.SearchShortcuts, "") | |
| 87 | 82 | require.Contains(t, saved.SearchShortcuts, "ddg") | |
| 88 | 83 | require.Equal(t, "https://duckduckgo.com/?q=%s", saved.SearchShortcuts["ddg"].URL) | |
| 89 | 84 | require.Equal(t, "DuckDuckGo", saved.SearchShortcuts["ddg"].Description) | |
| 90 | 85 | } | |
| 91 | 86 | ||
| 92 | - | func validWebUIConfig() port.WebUIConfig { | |
| 93 | - | palette := port.ColorPalette{ | |
| 87 | + | func validWebUIConfig() dto.WebUIConfig { | |
| 88 | + | palette := dto.ColorPalette{ | |
| 94 | 89 | Background: "#ffffff", | |
| 95 | 90 | Surface: "#f8f8f8", | |
| 96 | 91 | SurfaceVariant: "#eeeeee", | |
| @@ -99,8 +94,8 @@ func validWebUIConfig() port.WebUIConfig { | |||
| 99 | 94 | Accent: "#0055ff", | |
| 100 | 95 | Border: "#dddddd", | |
| 101 | 96 | } | |
| 102 | - | return port.WebUIConfig{ | |
| 103 | - | Appearance: port.WebUIAppearanceConfig{ | |
| 97 | + | return dto.WebUIConfig{ | |
| 98 | + | Appearance: dto.WebUIAppearanceConfig{ | |
| 104 | 99 | SansFont: "Inter", | |
| 105 | 100 | SerifFont: "Georgia", | |
| 106 | 101 | MonospaceFont: "JetBrains Mono", | |
| @@ -109,11 +104,11 @@ func validWebUIConfig() port.WebUIConfig { | |||
| 109 | 104 | LightPalette: palette, | |
| 110 | 105 | DarkPalette: palette, | |
| 111 | 106 | }, | |
| 112 | - | Performance: port.WebUIPerformanceConfig{ | |
| 107 | + | Performance: dto.WebUIPerformanceConfig{ | |
| 113 | 108 | Profile: "default", | |
| 114 | 109 | }, | |
| 115 | 110 | DefaultUIScale: 1, | |
| 116 | 111 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | |
| 117 | - | SearchShortcuts: map[string]port.SearchShortcut{}, | |
| 112 | + | SearchShortcuts: map[string]dto.SearchShortcut{}, | |
| 118 | 113 | } | |
| 119 | 114 | } | |
| @@ -5,7 +5,7 @@ import ( | |||||
| 5 | "math" | 5 | "math" | ||
| 6 | "testing" | 6 | "testing" | ||
| 7 | 7 | ||||
| 8 | - | "github.com/bnema/dumber/internal/application/port" | 8 | + | "github.com/bnema/dumber/internal/application/dto" |
| 9 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | 9 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | ||
| 10 | "github.com/bnema/dumber/internal/application/usecase" | 10 | "github.com/bnema/dumber/internal/application/usecase" | ||
| 11 | "github.com/stretchr/testify/mock" | 11 | "github.com/stretchr/testify/mock" | ||
| @@ -15,40 +15,33 @@ import ( | |||||
| 15 | func TestSaveWebUIConfigUseCase_Validation(t *testing.T) { | 15 | func TestSaveWebUIConfigUseCase_Validation(t *testing.T) { | ||
| 16 | tests := []struct { | 16 | tests := []struct { | ||
| 17 | name string | 17 | name string | ||
| 18 | - | mutate func(*port.WebUIConfig) | 18 | + | mutate func(*dto.WebUIConfig) |
| 19 | wantErr string | 19 | wantErr string | ||
| 20 | }{ | 20 | }{ | ||
| 21 | { | 21 | { | ||
| 22 | name: "rejects invalid default search URL", | 22 | name: "rejects invalid default search URL", | ||
| 23 | - | mutate: func(cfg *port.WebUIConfig) { | 23 | + | mutate: func(cfg *dto.WebUIConfig) { |
| 24 | cfg.DefaultSearchEngine = "not-a-url-%s" | 24 | cfg.DefaultSearchEngine = "not-a-url-%s" | ||
| 25 | }, | 25 | }, | ||
| 26 | wantErr: "default_search_engine", | 26 | wantErr: "default_search_engine", | ||
| 27 | }, | 27 | }, | ||
| 28 | - | { | |||
| 29 | - | name: "rejects blank search shortcut key", | |||
| 30 | - | mutate: func(cfg *port.WebUIConfig) { | |||
| 31 | - | cfg.SearchShortcuts[" "] = port.SearchShortcut{URL: "https://example.com?q=%s", Description: "Example"} | |||
| 32 | - | }, | |||
| 33 | - | wantErr: "shortcut key cannot be empty", | |||
| 34 | - | }, | |||
| 35 | { | 28 | { | ||
| 36 | name: "rejects invalid performance profile", | 29 | name: "rejects invalid performance profile", | ||
| 37 | - | mutate: func(cfg *port.WebUIConfig) { | 30 | + | mutate: func(cfg *dto.WebUIConfig) { |
| 38 | cfg.Performance.Profile = "turbo" | 31 | cfg.Performance.Profile = "turbo" | ||
| 39 | }, | 32 | }, | ||
| 40 | wantErr: "performance.profile", | 33 | wantErr: "performance.profile", | ||
| 41 | }, | 34 | }, | ||
| 42 | { | 35 | { | ||
| 43 | name: "rejects NaN UI scale", | 36 | name: "rejects NaN UI scale", | ||
| 44 | - | mutate: func(cfg *port.WebUIConfig) { | 37 | + | mutate: func(cfg *dto.WebUIConfig) { |
| 45 | cfg.DefaultUIScale = math.NaN() | 38 | cfg.DefaultUIScale = math.NaN() | ||
| 46 | }, | 39 | }, | ||
| 47 | wantErr: "default_ui_scale must be a finite value", | 40 | wantErr: "default_ui_scale must be a finite value", | ||
| 48 | }, | 41 | }, | ||
| 49 | { | 42 | { | ||
| 50 | name: "rejects infinite UI scale", | 43 | name: "rejects infinite UI scale", | ||
| 51 | - | mutate: func(cfg *port.WebUIConfig) { | 44 | + | mutate: func(cfg *dto.WebUIConfig) { |
| 52 | cfg.DefaultUIScale = math.Inf(1) | 45 | cfg.DefaultUIScale = math.Inf(1) | ||
| 53 | }, | 46 | }, | ||
| 54 | wantErr: "default_ui_scale must be a finite value", | 47 | wantErr: "default_ui_scale must be a finite value", | ||
| @@ -72,25 +65,27 @@ func TestSaveWebUIConfigUseCase_Validation(t *testing.T) { | |||||
| 72 | 65 | ||||
| 73 | func TestSaveWebUIConfigUseCase_NormalizesAndSavesValidConfig(t *testing.T) { | 66 | func TestSaveWebUIConfigUseCase_NormalizesAndSavesValidConfig(t *testing.T) { | ||
| 74 | saver := portmocks.NewMockWebUIConfigSaver(t) | 67 | saver := portmocks.NewMockWebUIConfigSaver(t) | ||
| 75 | - | var saved port.WebUIConfig | 68 | + | var saved dto.WebUIConfig |
| 76 | - | saver.EXPECT().SaveWebUIConfig(mock.Anything, mock.AnythingOfType("port.WebUIConfig")).Run(func(_ context.Context, cfg port.WebUIConfig) { | 69 | + | saver.EXPECT().SaveWebUIConfig(mock.Anything, mock.AnythingOfType("dto.WebUIConfig")).Run(func(_ context.Context, cfg dto.WebUIConfig) { |
| 77 | saved = cfg | 70 | saved = cfg | ||
| 78 | }).Return(nil).Once() | 71 | }).Return(nil).Once() | ||
| 79 | uc := usecase.NewSaveWebUIConfigUseCase(saver) | 72 | uc := usecase.NewSaveWebUIConfigUseCase(saver) | ||
| 80 | cfg := validWebUIConfig() | 73 | cfg := validWebUIConfig() | ||
| 81 | - | cfg.SearchShortcuts[" ddg "] = port.SearchShortcut{URL: " https://duckduckgo.com/?q=%s ", Description: " DuckDuckGo "} | 74 | + | cfg.SearchShortcuts[" ddg "] = dto.SearchShortcut{URL: " https://duckduckgo.com/?q=%s ", Description: " DuckDuckGo "} |
| 75 | + | cfg.SearchShortcuts[" "] = dto.SearchShortcut{URL: "https://example.com?q=%s", Description: "Example"} | |||
| 82 | 76 | ||||
| 83 | err := uc.Execute(context.Background(), cfg) | 77 | err := uc.Execute(context.Background(), cfg) | ||
| 84 | 78 | ||||
| 85 | require.NoError(t, err) | 79 | require.NoError(t, err) | ||
| 86 | require.NotContains(t, saved.SearchShortcuts, " ddg ") | 80 | require.NotContains(t, saved.SearchShortcuts, " ddg ") | ||
| 81 | + | require.NotContains(t, saved.SearchShortcuts, "") | |||
| 87 | require.Contains(t, saved.SearchShortcuts, "ddg") | 82 | require.Contains(t, saved.SearchShortcuts, "ddg") | ||
| 88 | require.Equal(t, "https://duckduckgo.com/?q=%s", saved.SearchShortcuts["ddg"].URL) | 83 | require.Equal(t, "https://duckduckgo.com/?q=%s", saved.SearchShortcuts["ddg"].URL) | ||
| 89 | require.Equal(t, "DuckDuckGo", saved.SearchShortcuts["ddg"].Description) | 84 | require.Equal(t, "DuckDuckGo", saved.SearchShortcuts["ddg"].Description) | ||
| 90 | } | 85 | } | ||
| 91 | 86 | ||||
| 92 | - | func validWebUIConfig() port.WebUIConfig { | 87 | + | func validWebUIConfig() dto.WebUIConfig { |
| 93 | - | palette := port.ColorPalette{ | 88 | + | palette := dto.ColorPalette{ |
| 94 | Background: "#ffffff", | 89 | Background: "#ffffff", | ||
| 95 | Surface: "#f8f8f8", | 90 | Surface: "#f8f8f8", | ||
| 96 | SurfaceVariant: "#eeeeee", | 91 | SurfaceVariant: "#eeeeee", | ||
| @@ -99,8 +94,8 @@ func validWebUIConfig() port.WebUIConfig { | |||||
| 99 | Accent: "#0055ff", | 94 | Accent: "#0055ff", | ||
| 100 | Border: "#dddddd", | 95 | Border: "#dddddd", | ||
| 101 | } | 96 | } | ||
| 102 | - | return port.WebUIConfig{ | 97 | + | return dto.WebUIConfig{ |
| 103 | - | Appearance: port.WebUIAppearanceConfig{ | 98 | + | Appearance: dto.WebUIAppearanceConfig{ |
| 104 | SansFont: "Inter", | 99 | SansFont: "Inter", | ||
| 105 | SerifFont: "Georgia", | 100 | SerifFont: "Georgia", | ||
| 106 | MonospaceFont: "JetBrains Mono", | 101 | MonospaceFont: "JetBrains Mono", | ||
| @@ -109,11 +104,11 @@ func validWebUIConfig() port.WebUIConfig { | |||||
| 109 | LightPalette: palette, | 104 | LightPalette: palette, | ||
| 110 | DarkPalette: palette, | 105 | DarkPalette: palette, | ||
| 111 | }, | 106 | }, | ||
| 112 | - | Performance: port.WebUIPerformanceConfig{ | 107 | + | Performance: dto.WebUIPerformanceConfig{ |
| 113 | Profile: "default", | 108 | Profile: "default", | ||
| 114 | }, | 109 | }, | ||
| 115 | DefaultUIScale: 1, | 110 | DefaultUIScale: 1, | ||
| 116 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | 111 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | ||
| 117 | - | SearchShortcuts: map[string]port.SearchShortcut{}, | 112 | + | SearchShortcuts: map[string]dto.SearchShortcut{}, |
| 118 | } | 113 | } | ||
| 119 | } | 114 | } | ||
M internal/application/usecase/search_history_test.go +6 -9
| @@ -155,8 +155,10 @@ func TestSearchHistoryUseCase_GetMostVisited_ReturnsEmptyWhenNoHistory(t *testin | |||
| 155 | 155 | func TestSearchHistoryUseCase_ClearRange_RecentRangeDeletesSinceCutoff(t *testing.T) { | |
| 156 | 156 | ctx := testContext() | |
| 157 | 157 | historyRepo := repomocks.NewMockHistoryRepository(t) | |
| 158 | + | now := time.Now() | |
| 159 | + | expectedCutoff := now.Add(-time.Hour) | |
| 158 | 160 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | |
| 159 | - | return !since.IsZero() && time.Since(since) <= time.Hour+time.Minute | |
| 161 | + | return since.After(expectedCutoff.Add(-time.Minute)) && since.Before(expectedCutoff.Add(time.Minute)) | |
| 160 | 162 | })).Return(nil).Once() | |
| 161 | 163 | ||
| 162 | 164 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | |
| @@ -168,15 +170,10 @@ func TestSearchHistoryUseCase_ClearRange_RecentRangeDeletesSinceCutoff(t *testin | |||
| 168 | 170 | func TestSearchHistoryUseCase_ClearRange_TodayDeletesSinceLocalMidnight(t *testing.T) { | |
| 169 | 171 | ctx := testContext() | |
| 170 | 172 | historyRepo := repomocks.NewMockHistoryRepository(t) | |
| 171 | - | today := time.Now() | |
| 173 | + | now := time.Now() | |
| 174 | + | expectedCutoff := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) | |
| 172 | 175 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | |
| 173 | - | return since.Year() == today.Year() && | |
| 174 | - | since.Month() == today.Month() && | |
| 175 | - | since.Day() == today.Day() && | |
| 176 | - | since.Hour() == 0 && | |
| 177 | - | since.Minute() == 0 && | |
| 178 | - | since.Second() == 0 && | |
| 179 | - | since.Nanosecond() == 0 | |
| 176 | + | return since.Equal(expectedCutoff) | |
| 180 | 177 | })).Return(nil).Once() | |
| 181 | 178 | ||
| 182 | 179 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | |
| @@ -155,8 +155,10 @@ func TestSearchHistoryUseCase_GetMostVisited_ReturnsEmptyWhenNoHistory(t *testin | |||||
| 155 | func TestSearchHistoryUseCase_ClearRange_RecentRangeDeletesSinceCutoff(t *testing.T) { | 155 | func TestSearchHistoryUseCase_ClearRange_RecentRangeDeletesSinceCutoff(t *testing.T) { | ||
| 156 | ctx := testContext() | 156 | ctx := testContext() | ||
| 157 | historyRepo := repomocks.NewMockHistoryRepository(t) | 157 | historyRepo := repomocks.NewMockHistoryRepository(t) | ||
| 158 | + | now := time.Now() | |||
| 159 | + | expectedCutoff := now.Add(-time.Hour) | |||
| 158 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | 160 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | ||
| 159 | - | return !since.IsZero() && time.Since(since) <= time.Hour+time.Minute | 161 | + | return since.After(expectedCutoff.Add(-time.Minute)) && since.Before(expectedCutoff.Add(time.Minute)) |
| 160 | })).Return(nil).Once() | 162 | })).Return(nil).Once() | ||
| 161 | 163 | ||||
| 162 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | 164 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | ||
| @@ -168,15 +170,10 @@ func TestSearchHistoryUseCase_ClearRange_RecentRangeDeletesSinceCutoff(t *testin | |||||
| 168 | func TestSearchHistoryUseCase_ClearRange_TodayDeletesSinceLocalMidnight(t *testing.T) { | 170 | func TestSearchHistoryUseCase_ClearRange_TodayDeletesSinceLocalMidnight(t *testing.T) { | ||
| 169 | ctx := testContext() | 171 | ctx := testContext() | ||
| 170 | historyRepo := repomocks.NewMockHistoryRepository(t) | 172 | historyRepo := repomocks.NewMockHistoryRepository(t) | ||
| 171 | - | today := time.Now() | 173 | + | now := time.Now() |
| 174 | + | expectedCutoff := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) | |||
| 172 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | 175 | historyRepo.EXPECT().DeleteSince(mock.Anything, mock.MatchedBy(func(since time.Time) bool { | ||
| 173 | - | return since.Year() == today.Year() && | 176 | + | return since.Equal(expectedCutoff) |
| 174 | - | since.Month() == today.Month() && | |||
| 175 | - | since.Day() == today.Day() && | |||
| 176 | - | since.Hour() == 0 && | |||
| 177 | - | since.Minute() == 0 && | |||
| 178 | - | since.Second() == 0 && | |||
| 179 | - | since.Nanosecond() == 0 | |||
| 180 | })).Return(nil).Once() | 177 | })).Return(nil).Once() | ||
| 181 | 178 | ||||
| 182 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | 179 | uc := usecase.NewSearchHistoryUseCase(historyRepo) | ||
M internal/bootstrap/engine.go +2 -1
| @@ -5,6 +5,7 @@ import ( | |||
| 5 | 5 | "encoding/json" | |
| 6 | 6 | "fmt" | |
| 7 | 7 | ||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 9 | "github.com/bnema/dumber/internal/application/port" | |
| 9 | 10 | "github.com/bnema/dumber/internal/application/usecase" | |
| 10 | 11 | audiofactory "github.com/bnema/dumber/internal/infrastructure/audio/factory" | |
| @@ -35,7 +36,7 @@ func BuildEngine(input EngineInput) (port.Engine, error) { | |||
| 35 | 36 | cfg := input.Config | |
| 36 | 37 | systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor()) | |
| 37 | 38 | systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader) | |
| 38 | - | buildConfigPayload := func(read func(context.Context) (port.SystemviewConfigPayload, error)) func() ([]byte, error) { | |
| 39 | + | buildConfigPayload := func(read func(context.Context) (dto.SystemviewConfigPayload, error)) func() ([]byte, error) { | |
| 39 | 40 | return func() ([]byte, error) { | |
| 40 | 41 | payload, err := read(input.Ctx) | |
| 41 | 42 | if err != nil { | |
| @@ -5,6 +5,7 @@ import ( | |||||
| 5 | "encoding/json" | 5 | "encoding/json" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | 7 | ||||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 8 | "github.com/bnema/dumber/internal/application/port" | 9 | "github.com/bnema/dumber/internal/application/port" | ||
| 9 | "github.com/bnema/dumber/internal/application/usecase" | 10 | "github.com/bnema/dumber/internal/application/usecase" | ||
| 10 | audiofactory "github.com/bnema/dumber/internal/infrastructure/audio/factory" | 11 | audiofactory "github.com/bnema/dumber/internal/infrastructure/audio/factory" | ||
| @@ -35,7 +36,7 @@ func BuildEngine(input EngineInput) (port.Engine, error) { | |||||
| 35 | cfg := input.Config | 36 | cfg := input.Config | ||
| 36 | systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor()) | 37 | systemviewReader := config.NewSystemviewConfigReader(env.NewHardwareSurveyor()) | ||
| 37 | systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader) | 38 | systemviewUC := usecase.NewReadSystemviewConfigUseCase(systemviewReader) | ||
| 38 | - | buildConfigPayload := func(read func(context.Context) (port.SystemviewConfigPayload, error)) func() ([]byte, error) { | 39 | + | buildConfigPayload := func(read func(context.Context) (dto.SystemviewConfigPayload, error)) func() ([]byte, error) { |
| 39 | return func() ([]byte, error) { | 40 | return func() ([]byte, error) { | ||
| 40 | payload, err := read(input.Ctx) | 41 | payload, err := read(input.Ctx) | ||
| 41 | if err != nil { | 42 | if err != nil { | ||
M internal/infrastructure/cef/scheme_handler.go +3 -4
| @@ -279,10 +279,6 @@ func (h *dumbSchemeHandler) handleTranscodeAPI(request purecef.Request) purecef. | |||
| 279 | 279 | if err != nil || sourceParsed.Host == "" || (sourceParsed.Scheme != "http" && sourceParsed.Scheme != actualInternalScheme) { | |
| 280 | 280 | return h.newAPIJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid src"}) | |
| 281 | 281 | } | |
| 282 | - | if err := validateTranscodeSourceURL(h.ctx, sourceParsed); err != nil { | |
| 283 | - | return h.newAPIJSONResourceHandler(http.StatusForbidden, map[string]string{"error": err.Error()}) | |
| 284 | - | } | |
| 285 | - | ||
| 286 | 282 | headers := make(map[string]string) | |
| 287 | 283 | if userAgent := request.GetHeaderByName("User-Agent"); userAgent != "" { | |
| 288 | 284 | headers["User-Agent"] = userAgent | |
| @@ -308,6 +304,9 @@ func (h *dumbSchemeHandler) handleTranscodeAPI(request purecef.Request) purecef. | |||
| 308 | 304 | headers: headers, | |
| 309 | 305 | ctx: ctx, | |
| 310 | 306 | cancel: cancel, | |
| 307 | + | validateSource: func(ctx context.Context) error { | |
| 308 | + | return validateTranscodeSourceURL(ctx, sourceParsed) | |
| 309 | + | }, | |
| 311 | 310 | logf: func() zerolog.Logger { | |
| 312 | 311 | return h.logger.With().Str("component", "scheme-transcoding").Logger() | |
| 313 | 312 | }, | |
| @@ -279,10 +279,6 @@ func (h *dumbSchemeHandler) handleTranscodeAPI(request purecef.Request) purecef. | |||||
| 279 | if err != nil || sourceParsed.Host == "" || (sourceParsed.Scheme != "http" && sourceParsed.Scheme != actualInternalScheme) { | 279 | if err != nil || sourceParsed.Host == "" || (sourceParsed.Scheme != "http" && sourceParsed.Scheme != actualInternalScheme) { | ||
| 280 | return h.newAPIJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid src"}) | 280 | return h.newAPIJSONResourceHandler(http.StatusBadRequest, map[string]string{"error": "invalid src"}) | ||
| 281 | } | 281 | } | ||
| 282 | - | if err := validateTranscodeSourceURL(h.ctx, sourceParsed); err != nil { | |||
| 283 | - | return h.newAPIJSONResourceHandler(http.StatusForbidden, map[string]string{"error": err.Error()}) | |||
| 284 | - | } | |||
| 285 | - | ||||
| 286 | headers := make(map[string]string) | 282 | headers := make(map[string]string) | ||
| 287 | if userAgent := request.GetHeaderByName("User-Agent"); userAgent != "" { | 283 | if userAgent := request.GetHeaderByName("User-Agent"); userAgent != "" { | ||
| 288 | headers["User-Agent"] = userAgent | 284 | headers["User-Agent"] = userAgent | ||
| @@ -308,6 +304,9 @@ func (h *dumbSchemeHandler) handleTranscodeAPI(request purecef.Request) purecef. | |||||
| 308 | headers: headers, | 304 | headers: headers, | ||
| 309 | ctx: ctx, | 305 | ctx: ctx, | ||
| 310 | cancel: cancel, | 306 | cancel: cancel, | ||
| 307 | + | validateSource: func(ctx context.Context) error { | |||
| 308 | + | return validateTranscodeSourceURL(ctx, sourceParsed) | |||
| 309 | + | }, | |||
| 311 | logf: func() zerolog.Logger { | 310 | logf: func() zerolog.Logger { | ||
| 312 | return h.logger.With().Str("component", "scheme-transcoding").Logger() | 311 | return h.logger.With().Str("component", "scheme-transcoding").Logger() | ||
| 313 | }, | 312 | }, | ||
M internal/infrastructure/cef/transcoding_handler.go +68 -12
| @@ -24,6 +24,8 @@ var _ purecef.ResourceRequestHandler = (*transcodingRequestHandler)(nil) | |||
| 24 | 24 | const ( | |
| 25 | 25 | maxTranscodingURLLength = 240 | |
| 26 | 26 | httpStatusOK = 200 | |
| 27 | + | httpStatusForbidden = 403 | |
| 28 | + | httpStatusBadGateway = 502 | |
| 27 | 29 | ) | |
| 28 | 30 | ||
| 29 | 31 | // transcodingResourceHandler is checked indirectly: purecef.NewResourceHandler | |
| @@ -34,14 +36,18 @@ const ( | |||
| 34 | 36 | // --------------------------------------------------------------------------- | |
| 35 | 37 | ||
| 36 | 38 | type transcodingResourceHandler struct { | |
| 37 | - | transcoder port.MediaTranscoder | |
| 38 | - | sourceURL string | |
| 39 | - | headers map[string]string | |
| 40 | - | session port.TranscodeSession | |
| 41 | - | ctx context.Context | |
| 42 | - | cancel context.CancelFunc | |
| 43 | - | logf func() zerolog.Logger | |
| 44 | - | totalBytes int64 | |
| 39 | + | transcoder port.MediaTranscoder | |
| 40 | + | sourceURL string | |
| 41 | + | headers map[string]string | |
| 42 | + | session port.TranscodeSession | |
| 43 | + | ctx context.Context | |
| 44 | + | cancel context.CancelFunc | |
| 45 | + | logf func() zerolog.Logger | |
| 46 | + | validateSource func(context.Context) error | |
| 47 | + | mu sync.Mutex | |
| 48 | + | startErr error | |
| 49 | + | startErrStatus int | |
| 50 | + | totalBytes int64 | |
| 45 | 51 | } | |
| 46 | 52 | ||
| 47 | 53 | // Open starts the transcode session asynchronously. CEF will call | |
| @@ -62,6 +68,18 @@ func (rh *transcodingResourceHandler) Open( | |||
| 62 | 68 | Msg("cef: starting transcoding resource stream") | |
| 63 | 69 | ||
| 64 | 70 | go func() { | |
| 71 | + | if rh.validateSource != nil { | |
| 72 | + | if err := rh.validateSource(rh.ctx); err != nil { | |
| 73 | + | log.Warn(). | |
| 74 | + | Err(err). | |
| 75 | + | Str("source_url", sourceURL). | |
| 76 | + | Msg("cef: rejected transcoding source") | |
| 77 | + | rh.setStartErr(err, httpStatusForbidden) | |
| 78 | + | callback.Cont() | |
| 79 | + | return | |
| 80 | + | } | |
| 81 | + | } | |
| 82 | + | ||
| 65 | 83 | session, err := rh.transcoder.Start(rh.ctx, rh.sourceURL, rh.headers) | |
| 66 | 84 | if err != nil { | |
| 67 | 85 | log.Error(). | |
| @@ -69,11 +87,14 @@ func (rh *transcodingResourceHandler) Open( | |||
| 69 | 87 | Str("source_url", sourceURL). | |
| 70 | 88 | Msg("cef: failed to start transcoding resource stream") | |
| 71 | 89 | // Let CEF know we failed — Cancel will be called. | |
| 90 | + | rh.setStartErr(err, httpStatusBadGateway) | |
| 72 | 91 | rh.cancel() | |
| 73 | 92 | callback.Cont() | |
| 74 | 93 | return | |
| 75 | 94 | } | |
| 95 | + | rh.mu.Lock() | |
| 76 | 96 | rh.session = session | |
| 97 | + | rh.mu.Unlock() | |
| 77 | 98 | log.Info(). | |
| 78 | 99 | Str("source_url", sourceURL). | |
| 79 | 100 | Str("content_type", session.ContentType()). | |
| @@ -84,10 +105,39 @@ func (rh *transcodingResourceHandler) Open( | |||
| 84 | 105 | return 1 | |
| 85 | 106 | } | |
| 86 | 107 | ||
| 108 | + | func (rh *transcodingResourceHandler) setStartErr(err error, status int) { | |
| 109 | + | rh.mu.Lock() | |
| 110 | + | defer rh.mu.Unlock() | |
| 111 | + | rh.startErr = err | |
| 112 | + | rh.startErrStatus = status | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | func (rh *transcodingResourceHandler) getStartErr() (error, int) { | |
| 116 | + | rh.mu.Lock() | |
| 117 | + | defer rh.mu.Unlock() | |
| 118 | + | return rh.startErr, rh.startErrStatus | |
| 119 | + | } | |
| 120 | + | ||
| 87 | 121 | // GetResponseHeaders sets the streaming response metadata. | |
| 88 | 122 | func (rh *transcodingResourceHandler) GetResponseHeaders( | |
| 89 | 123 | response purecef.Response, responseLength *int64, _ uintptr, | |
| 90 | 124 | ) { | |
| 125 | + | if startErr, status := rh.getStartErr(); startErr != nil { | |
| 126 | + | if status == 0 { | |
| 127 | + | status = httpStatusBadGateway | |
| 128 | + | } | |
| 129 | + | response.SetStatus(int32(status)) | |
| 130 | + | if status == httpStatusForbidden { | |
| 131 | + | response.SetStatusText("Forbidden") | |
| 132 | + | } else { | |
| 133 | + | response.SetStatusText("Bad Gateway") | |
| 134 | + | } | |
| 135 | + | response.SetMimeType("text/plain") | |
| 136 | + | if responseLength != nil { | |
| 137 | + | *responseLength = 0 | |
| 138 | + | } | |
| 139 | + | return | |
| 140 | + | } | |
| 91 | 141 | response.SetStatus(httpStatusOK) | |
| 92 | 142 | response.SetStatusText("OK") | |
| 93 | 143 | response.SetMimeType("video/webm") | |
| @@ -108,12 +158,15 @@ func (rh *transcodingResourceHandler) Read( | |||
| 108 | 158 | dataOut unsafe.Pointer, bytesToRead int32, | |
| 109 | 159 | bytesRead *int32, _ purecef.ResourceReadCallback, | |
| 110 | 160 | ) int32 { | |
| 111 | - | if rh.session == nil { | |
| 161 | + | rh.mu.Lock() | |
| 162 | + | session := rh.session | |
| 163 | + | rh.mu.Unlock() | |
| 164 | + | if session == nil { | |
| 112 | 165 | return 0 | |
| 113 | 166 | } | |
| 114 | 167 | ||
| 115 | 168 | dst := unsafe.Slice((*byte)(dataOut), int(bytesToRead)) | |
| 116 | - | n, err := rh.session.Read(dst) | |
| 169 | + | n, err := session.Read(dst) | |
| 117 | 170 | if n > 0 { | |
| 118 | 171 | rh.totalBytes += int64(n) | |
| 119 | 172 | if rh.totalBytes == int64(n) { | |
| @@ -153,8 +206,11 @@ func (rh *transcodingResourceHandler) Cancel() { | |||
| 153 | 206 | Str("source_url", sourceURL). | |
| 154 | 207 | Int64("total_bytes", rh.totalBytes). | |
| 155 | 208 | Msg("cef: transcoding resource stream canceled") | |
| 156 | - | if rh.session != nil { | |
| 157 | - | if err := rh.session.Close(); err != nil { | |
| 209 | + | rh.mu.Lock() | |
| 210 | + | session := rh.session | |
| 211 | + | rh.mu.Unlock() | |
| 212 | + | if session != nil { | |
| 213 | + | if err := session.Close(); err != nil { | |
| 158 | 214 | rh.logger().Warn(). | |
| 159 | 215 | Str("source_url", sourceURL). | |
| 160 | 216 | Err(err). | |
| @@ -24,6 +24,8 @@ var _ purecef.ResourceRequestHandler = (*transcodingRequestHandler)(nil) | |||||
| 24 | const ( | 24 | const ( | ||
| 25 | maxTranscodingURLLength = 240 | 25 | maxTranscodingURLLength = 240 | ||
| 26 | httpStatusOK = 200 | 26 | httpStatusOK = 200 | ||
| 27 | + | httpStatusForbidden = 403 | |||
| 28 | + | httpStatusBadGateway = 502 | |||
| 27 | ) | 29 | ) | ||
| 28 | 30 | ||||
| 29 | // transcodingResourceHandler is checked indirectly: purecef.NewResourceHandler | 31 | // transcodingResourceHandler is checked indirectly: purecef.NewResourceHandler | ||
| @@ -34,14 +36,18 @@ const ( | |||||
| 34 | // --------------------------------------------------------------------------- | 36 | // --------------------------------------------------------------------------- | ||
| 35 | 37 | ||||
| 36 | type transcodingResourceHandler struct { | 38 | type transcodingResourceHandler struct { | ||
| 37 | - | transcoder port.MediaTranscoder | 39 | + | transcoder port.MediaTranscoder |
| 38 | - | sourceURL string | 40 | + | sourceURL string |
| 39 | - | headers map[string]string | 41 | + | headers map[string]string |
| 40 | - | session port.TranscodeSession | 42 | + | session port.TranscodeSession |
| 41 | - | ctx context.Context | 43 | + | ctx context.Context |
| 42 | - | cancel context.CancelFunc | 44 | + | cancel context.CancelFunc |
| 43 | - | logf func() zerolog.Logger | 45 | + | logf func() zerolog.Logger |
| 44 | - | totalBytes int64 | 46 | + | validateSource func(context.Context) error |
| 47 | + | mu sync.Mutex | |||
| 48 | + | startErr error | |||
| 49 | + | startErrStatus int | |||
| 50 | + | totalBytes int64 | |||
| 45 | } | 51 | } | ||
| 46 | 52 | ||||
| 47 | // Open starts the transcode session asynchronously. CEF will call | 53 | // Open starts the transcode session asynchronously. CEF will call | ||
| @@ -62,6 +68,18 @@ func (rh *transcodingResourceHandler) Open( | |||||
| 62 | Msg("cef: starting transcoding resource stream") | 68 | Msg("cef: starting transcoding resource stream") | ||
| 63 | 69 | ||||
| 64 | go func() { | 70 | go func() { | ||
| 71 | + | if rh.validateSource != nil { | |||
| 72 | + | if err := rh.validateSource(rh.ctx); err != nil { | |||
| 73 | + | log.Warn(). | |||
| 74 | + | Err(err). | |||
| 75 | + | Str("source_url", sourceURL). | |||
| 76 | + | Msg("cef: rejected transcoding source") | |||
| 77 | + | rh.setStartErr(err, httpStatusForbidden) | |||
| 78 | + | callback.Cont() | |||
| 79 | + | return | |||
| 80 | + | } | |||
| 81 | + | } | |||
| 82 | + | ||||
| 65 | session, err := rh.transcoder.Start(rh.ctx, rh.sourceURL, rh.headers) | 83 | session, err := rh.transcoder.Start(rh.ctx, rh.sourceURL, rh.headers) | ||
| 66 | if err != nil { | 84 | if err != nil { | ||
| 67 | log.Error(). | 85 | log.Error(). | ||
| @@ -69,11 +87,14 @@ func (rh *transcodingResourceHandler) Open( | |||||
| 69 | Str("source_url", sourceURL). | 87 | Str("source_url", sourceURL). | ||
| 70 | Msg("cef: failed to start transcoding resource stream") | 88 | Msg("cef: failed to start transcoding resource stream") | ||
| 71 | // Let CEF know we failed — Cancel will be called. | 89 | // Let CEF know we failed — Cancel will be called. | ||
| 90 | + | rh.setStartErr(err, httpStatusBadGateway) | |||
| 72 | rh.cancel() | 91 | rh.cancel() | ||
| 73 | callback.Cont() | 92 | callback.Cont() | ||
| 74 | return | 93 | return | ||
| 75 | } | 94 | } | ||
| 95 | + | rh.mu.Lock() | |||
| 76 | rh.session = session | 96 | rh.session = session | ||
| 97 | + | rh.mu.Unlock() | |||
| 77 | log.Info(). | 98 | log.Info(). | ||
| 78 | Str("source_url", sourceURL). | 99 | Str("source_url", sourceURL). | ||
| 79 | Str("content_type", session.ContentType()). | 100 | Str("content_type", session.ContentType()). | ||
| @@ -84,10 +105,39 @@ func (rh *transcodingResourceHandler) Open( | |||||
| 84 | return 1 | 105 | return 1 | ||
| 85 | } | 106 | } | ||
| 86 | 107 | ||||
| 108 | + | func (rh *transcodingResourceHandler) setStartErr(err error, status int) { | |||
| 109 | + | rh.mu.Lock() | |||
| 110 | + | defer rh.mu.Unlock() | |||
| 111 | + | rh.startErr = err | |||
| 112 | + | rh.startErrStatus = status | |||
| 113 | + | } | |||
| 114 | + | ||||
| 115 | + | func (rh *transcodingResourceHandler) getStartErr() (error, int) { | |||
| 116 | + | rh.mu.Lock() | |||
| 117 | + | defer rh.mu.Unlock() | |||
| 118 | + | return rh.startErr, rh.startErrStatus | |||
| 119 | + | } | |||
| 120 | + | ||||
| 87 | // GetResponseHeaders sets the streaming response metadata. | 121 | // GetResponseHeaders sets the streaming response metadata. | ||
| 88 | func (rh *transcodingResourceHandler) GetResponseHeaders( | 122 | func (rh *transcodingResourceHandler) GetResponseHeaders( | ||
| 89 | response purecef.Response, responseLength *int64, _ uintptr, | 123 | response purecef.Response, responseLength *int64, _ uintptr, | ||
| 90 | ) { | 124 | ) { | ||
| 125 | + | if startErr, status := rh.getStartErr(); startErr != nil { | |||
| 126 | + | if status == 0 { | |||
| 127 | + | status = httpStatusBadGateway | |||
| 128 | + | } | |||
| 129 | + | response.SetStatus(int32(status)) | |||
| 130 | + | if status == httpStatusForbidden { | |||
| 131 | + | response.SetStatusText("Forbidden") | |||
| 132 | + | } else { | |||
| 133 | + | response.SetStatusText("Bad Gateway") | |||
| 134 | + | } | |||
| 135 | + | response.SetMimeType("text/plain") | |||
| 136 | + | if responseLength != nil { | |||
| 137 | + | *responseLength = 0 | |||
| 138 | + | } | |||
| 139 | + | return | |||
| 140 | + | } | |||
| 91 | response.SetStatus(httpStatusOK) | 141 | response.SetStatus(httpStatusOK) | ||
| 92 | response.SetStatusText("OK") | 142 | response.SetStatusText("OK") | ||
| 93 | response.SetMimeType("video/webm") | 143 | response.SetMimeType("video/webm") | ||
| @@ -108,12 +158,15 @@ func (rh *transcodingResourceHandler) Read( | |||||
| 108 | dataOut unsafe.Pointer, bytesToRead int32, | 158 | dataOut unsafe.Pointer, bytesToRead int32, | ||
| 109 | bytesRead *int32, _ purecef.ResourceReadCallback, | 159 | bytesRead *int32, _ purecef.ResourceReadCallback, | ||
| 110 | ) int32 { | 160 | ) int32 { | ||
| 111 | - | if rh.session == nil { | 161 | + | rh.mu.Lock() |
| 162 | + | session := rh.session | |||
| 163 | + | rh.mu.Unlock() | |||
| 164 | + | if session == nil { | |||
| 112 | return 0 | 165 | return 0 | ||
| 113 | } | 166 | } | ||
| 114 | 167 | ||||
| 115 | dst := unsafe.Slice((*byte)(dataOut), int(bytesToRead)) | 168 | dst := unsafe.Slice((*byte)(dataOut), int(bytesToRead)) | ||
| 116 | - | n, err := rh.session.Read(dst) | 169 | + | n, err := session.Read(dst) |
| 117 | if n > 0 { | 170 | if n > 0 { | ||
| 118 | rh.totalBytes += int64(n) | 171 | rh.totalBytes += int64(n) | ||
| 119 | if rh.totalBytes == int64(n) { | 172 | if rh.totalBytes == int64(n) { | ||
| @@ -153,8 +206,11 @@ func (rh *transcodingResourceHandler) Cancel() { | |||||
| 153 | Str("source_url", sourceURL). | 206 | Str("source_url", sourceURL). | ||
| 154 | Int64("total_bytes", rh.totalBytes). | 207 | Int64("total_bytes", rh.totalBytes). | ||
| 155 | Msg("cef: transcoding resource stream canceled") | 208 | Msg("cef: transcoding resource stream canceled") | ||
| 156 | - | if rh.session != nil { | 209 | + | rh.mu.Lock() |
| 157 | - | if err := rh.session.Close(); err != nil { | 210 | + | session := rh.session |
| 211 | + | rh.mu.Unlock() | |||
| 212 | + | if session != nil { | |||
| 213 | + | if err := session.Close(); err != nil { | |||
| 158 | rh.logger().Warn(). | 214 | rh.logger().Warn(). | ||
| 159 | Str("source_url", sourceURL). | 215 | Str("source_url", sourceURL). | ||
| 160 | Err(err). | 216 | Err(err). | ||
M internal/infrastructure/config/defaults_test.go +4 -0
| @@ -41,5 +41,9 @@ func requireActionBinding(t *testing.T, actions map[string]ActionBinding, action | |||
| 41 | 41 | t.Helper() | |
| 42 | 42 | binding, ok := actions[action] | |
| 43 | 43 | require.True(t, ok, "missing action binding %q", action) | |
| 44 | + | if len(keys) == 0 { | |
| 45 | + | assert.Empty(t, binding.Keys) | |
| 46 | + | return | |
| 47 | + | } | |
| 44 | 48 | assert.Equal(t, keys, binding.Keys) | |
| 45 | 49 | } | |
| @@ -41,5 +41,9 @@ func requireActionBinding(t *testing.T, actions map[string]ActionBinding, action | |||||
| 41 | t.Helper() | 41 | t.Helper() | ||
| 42 | binding, ok := actions[action] | 42 | binding, ok := actions[action] | ||
| 43 | require.True(t, ok, "missing action binding %q", action) | 43 | require.True(t, ok, "missing action binding %q", action) | ||
| 44 | + | if len(keys) == 0 { | |||
| 45 | + | assert.Empty(t, binding.Keys) | |||
| 46 | + | return | |||
| 47 | + | } | |||
| 44 | assert.Equal(t, keys, binding.Keys) | 48 | assert.Equal(t, keys, binding.Keys) | ||
| 45 | } | 49 | } | ||
M internal/infrastructure/config/migrate.go +8 -0
| @@ -777,6 +777,10 @@ func (m *Migrator) detectMissingWorkspaceShortcutActions(userKeysWithValues map[ | |||
| 777 | 777 | if _, exists := userActions[actionName]; exists { | |
| 778 | 778 | continue | |
| 779 | 779 | } | |
| 780 | + | legacyActionName := strings.ReplaceAll(actionName, "-", "_") | |
| 781 | + | if _, exists := userActions[legacyActionName]; exists { | |
| 782 | + | continue | |
| 783 | + | } | |
| 780 | 784 | missing = append(missing, workspaceShortcutsActionsKey+"."+actionName) | |
| 781 | 785 | } | |
| 782 | 786 | ||
| @@ -801,6 +805,10 @@ func (m *Migrator) mergeMissingWorkspaceShortcutActions(rawConfig map[string]any | |||
| 801 | 805 | if _, exists := userActions[actionName]; exists { | |
| 802 | 806 | continue | |
| 803 | 807 | } | |
| 808 | + | legacyActionName := strings.ReplaceAll(actionName, "-", "_") | |
| 809 | + | if _, exists := userActions[legacyActionName]; exists { | |
| 810 | + | continue | |
| 811 | + | } | |
| 804 | 812 | userActions[actionName] = defaultAction | |
| 805 | 813 | } | |
| 806 | 814 | ||
| @@ -777,6 +777,10 @@ func (m *Migrator) detectMissingWorkspaceShortcutActions(userKeysWithValues map[ | |||||
| 777 | if _, exists := userActions[actionName]; exists { | 777 | if _, exists := userActions[actionName]; exists { | ||
| 778 | continue | 778 | continue | ||
| 779 | } | 779 | } | ||
| 780 | + | legacyActionName := strings.ReplaceAll(actionName, "-", "_") | |||
| 781 | + | if _, exists := userActions[legacyActionName]; exists { | |||
| 782 | + | continue | |||
| 783 | + | } | |||
| 780 | missing = append(missing, workspaceShortcutsActionsKey+"."+actionName) | 784 | missing = append(missing, workspaceShortcutsActionsKey+"."+actionName) | ||
| 781 | } | 785 | } | ||
| 782 | 786 | ||||
| @@ -801,6 +805,10 @@ func (m *Migrator) mergeMissingWorkspaceShortcutActions(rawConfig map[string]any | |||||
| 801 | if _, exists := userActions[actionName]; exists { | 805 | if _, exists := userActions[actionName]; exists { | ||
| 802 | continue | 806 | continue | ||
| 803 | } | 807 | } | ||
| 808 | + | legacyActionName := strings.ReplaceAll(actionName, "-", "_") | |||
| 809 | + | if _, exists := userActions[legacyActionName]; exists { | |||
| 810 | + | continue | |||
| 811 | + | } | |||
| 804 | userActions[actionName] = defaultAction | 812 | userActions[actionName] = defaultAction | ||
| 805 | } | 813 | } | ||
| 806 | 814 | ||||
M internal/infrastructure/config/migrate_test.go +7 -0
| @@ -759,6 +759,13 @@ func TestMigrator_MergeMissingWorkspaceShortcutActions(t *testing.T) { | |||
| 759 | 759 | closePane, ok := closePaneAny.(map[string]any) | |
| 760 | 760 | require.True(t, ok) | |
| 761 | 761 | assert.Equal(t, "Custom close pane", closePane["desc"]) | |
| 762 | + | assert.NotContains(t, actions, "close-pane") | |
| 763 | + | assert.NotContains(t, actions, "next-tab") | |
| 764 | + | assert.NotContains(t, actions, "previous-tab") | |
| 765 | + | assert.NotContains(t, actions, "consume-or-expel-left") | |
| 766 | + | assert.NotContains(t, actions, "consume-or-expel-right") | |
| 767 | + | assert.NotContains(t, actions, "consume-or-expel-up") | |
| 768 | + | assert.NotContains(t, actions, "consume-or-expel-down") | |
| 762 | 769 | } | |
| 763 | 770 | ||
| 764 | 771 | func TestMigrator_DefaultValueForKey_WorkspaceShortcutAction(t *testing.T) { | |
| @@ -759,6 +759,13 @@ func TestMigrator_MergeMissingWorkspaceShortcutActions(t *testing.T) { | |||||
| 759 | closePane, ok := closePaneAny.(map[string]any) | 759 | closePane, ok := closePaneAny.(map[string]any) | ||
| 760 | require.True(t, ok) | 760 | require.True(t, ok) | ||
| 761 | assert.Equal(t, "Custom close pane", closePane["desc"]) | 761 | assert.Equal(t, "Custom close pane", closePane["desc"]) | ||
| 762 | + | assert.NotContains(t, actions, "close-pane") | |||
| 763 | + | assert.NotContains(t, actions, "next-tab") | |||
| 764 | + | assert.NotContains(t, actions, "previous-tab") | |||
| 765 | + | assert.NotContains(t, actions, "consume-or-expel-left") | |||
| 766 | + | assert.NotContains(t, actions, "consume-or-expel-right") | |||
| 767 | + | assert.NotContains(t, actions, "consume-or-expel-up") | |||
| 768 | + | assert.NotContains(t, actions, "consume-or-expel-down") | |||
| 762 | } | 769 | } | ||
| 763 | 770 | ||||
| 764 | func TestMigrator_DefaultValueForKey_WorkspaceShortcutAction(t *testing.T) { | 771 | func TestMigrator_DefaultValueForKey_WorkspaceShortcutAction(t *testing.T) { | ||
M internal/infrastructure/config/systemview_reader.go +5 -4
| @@ -3,6 +3,7 @@ package config | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | ||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 6 | 7 | "github.com/bnema/dumber/internal/application/port" | |
| 7 | 8 | ) | |
| 8 | 9 | ||
| @@ -17,17 +18,17 @@ func NewSystemviewConfigReader(hwSurveyor port.HardwareSurveyor) port.Systemview | |||
| 17 | 18 | return &systemviewConfigReader{hwSurveyor: hwSurveyor} | |
| 18 | 19 | } | |
| 19 | 20 | ||
| 20 | - | func (r *systemviewConfigReader) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 21 | + | func (r *systemviewConfigReader) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 21 | 22 | return r.build(ctx, Get()) | |
| 22 | 23 | } | |
| 23 | 24 | ||
| 24 | - | func (r *systemviewConfigReader) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 25 | + | func (r *systemviewConfigReader) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 25 | 26 | return r.build(ctx, DefaultConfig()) | |
| 26 | 27 | } | |
| 27 | 28 | ||
| 28 | - | func (r *systemviewConfigReader) build(ctx context.Context, cfg *Config) (port.SystemviewConfigPayload, error) { | |
| 29 | + | func (r *systemviewConfigReader) build(ctx context.Context, cfg *Config) (dto.SystemviewConfigPayload, error) { | |
| 29 | 30 | if cfg == nil { | |
| 30 | - | return port.SystemviewConfigPayload{}, nil | |
| 31 | + | return dto.SystemviewConfigPayload{}, nil | |
| 31 | 32 | } | |
| 32 | 33 | ||
| 33 | 34 | var hw *port.HardwareInfo | |
| @@ -3,6 +3,7 @@ package config | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | 5 | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 6 | "github.com/bnema/dumber/internal/application/port" | 7 | "github.com/bnema/dumber/internal/application/port" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| @@ -17,17 +18,17 @@ func NewSystemviewConfigReader(hwSurveyor port.HardwareSurveyor) port.Systemview | |||||
| 17 | return &systemviewConfigReader{hwSurveyor: hwSurveyor} | 18 | return &systemviewConfigReader{hwSurveyor: hwSurveyor} | ||
| 18 | } | 19 | } | ||
| 19 | 20 | ||||
| 20 | - | func (r *systemviewConfigReader) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | 21 | + | func (r *systemviewConfigReader) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 21 | return r.build(ctx, Get()) | 22 | return r.build(ctx, Get()) | ||
| 22 | } | 23 | } | ||
| 23 | 24 | ||||
| 24 | - | func (r *systemviewConfigReader) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | 25 | + | func (r *systemviewConfigReader) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 25 | return r.build(ctx, DefaultConfig()) | 26 | return r.build(ctx, DefaultConfig()) | ||
| 26 | } | 27 | } | ||
| 27 | 28 | ||||
| 28 | - | func (r *systemviewConfigReader) build(ctx context.Context, cfg *Config) (port.SystemviewConfigPayload, error) { | 29 | + | func (r *systemviewConfigReader) build(ctx context.Context, cfg *Config) (dto.SystemviewConfigPayload, error) { |
| 29 | if cfg == nil { | 30 | if cfg == nil { | ||
| 30 | - | return port.SystemviewConfigPayload{}, nil | 31 | + | return dto.SystemviewConfigPayload{}, nil |
| 31 | } | 32 | } | ||
| 32 | 33 | ||||
| 33 | var hw *port.HardwareInfo | 34 | var hw *port.HardwareInfo | ||
M internal/infrastructure/config/webui_gateway.go +2 -2
| @@ -4,7 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "fmt" | |
| 6 | 6 | ||
| 7 | - | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | type WebUIConfigGateway struct { | |
| @@ -33,7 +33,7 @@ func clampInt(v, lo, hi int) int { | |||
| 33 | 33 | return v | |
| 34 | 34 | } | |
| 35 | 35 | ||
| 36 | - | func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUIConfig) error { | |
| 36 | + | func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 37 | 37 | _ = ctx | |
| 38 | 38 | if g == nil || g.mgr == nil { | |
| 39 | 39 | return fmt.Errorf("config manager not initialized") | |
| @@ -4,7 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | 6 | ||||
| 7 | - | "github.com/bnema/dumber/internal/application/port" | 7 | + | "github.com/bnema/dumber/internal/application/dto" |
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | type WebUIConfigGateway struct { | 10 | type WebUIConfigGateway struct { | ||
| @@ -33,7 +33,7 @@ func clampInt(v, lo, hi int) int { | |||||
| 33 | return v | 33 | return v | ||
| 34 | } | 34 | } | ||
| 35 | 35 | ||||
| 36 | - | func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg port.WebUIConfig) error { | 36 | + | func (g *WebUIConfigGateway) SaveWebUIConfig(ctx context.Context, cfg dto.WebUIConfig) error { |
| 37 | _ = ctx | 37 | _ = ctx | ||
| 38 | if g == nil || g.mgr == nil { | 38 | if g == nil || g.mgr == nil { | ||
| 39 | return fmt.Errorf("config manager not initialized") | 39 | return fmt.Errorf("config manager not initialized") | ||
M internal/infrastructure/config/webui_payload.go +21 -18
| @@ -1,12 +1,15 @@ | |||
| 1 | 1 | package config | |
| 2 | 2 | ||
| 3 | - | import "github.com/bnema/dumber/internal/application/port" | |
| 3 | + | import ( | |
| 4 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 5 | + | "github.com/bnema/dumber/internal/application/port" | |
| 6 | + | ) | |
| 4 | 7 | ||
| 5 | 8 | // BuildSystemviewConfigPayload projects the full config into the stable DTO used by | |
| 6 | 9 | // the settings page across engines. | |
| 7 | - | func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.SystemviewConfigPayload { | |
| 8 | - | payload := port.SystemviewConfigPayload{ | |
| 9 | - | SearchShortcuts: map[string]port.SearchShortcut{}, | |
| 10 | + | func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) dto.SystemviewConfigPayload { | |
| 11 | + | payload := dto.SystemviewConfigPayload{ | |
| 12 | + | SearchShortcuts: map[string]dto.SearchShortcut{}, | |
| 10 | 13 | } | |
| 11 | 14 | if cfg == nil { | |
| 12 | 15 | return payload | |
| @@ -20,16 +23,16 @@ func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.Syste | |||
| 20 | 23 | payload.DefaultSearchEngine = cfg.DefaultSearchEngine | |
| 21 | 24 | payload.SearchShortcuts = buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts) | |
| 22 | 25 | payload.EngineType = cfg.Engine.ResolveEngineType() | |
| 23 | - | payload.Performance = port.SystemviewPerformancePayload{ | |
| 26 | + | payload.Performance = dto.SystemviewPerformancePayload{ | |
| 24 | 27 | Profile: string(cfg.Engine.Profile), | |
| 25 | - | Custom: port.SystemviewCustomPerformancePayload{ | |
| 28 | + | Custom: dto.SystemviewCustomPerformancePayload{ | |
| 26 | 29 | SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads, | |
| 27 | 30 | SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads, | |
| 28 | 31 | WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB, | |
| 29 | 32 | NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB, | |
| 30 | 33 | WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount, | |
| 31 | 34 | }, | |
| 32 | - | Resolved: port.SystemviewResolvedPerformancePayload{ | |
| 35 | + | Resolved: dto.SystemviewResolvedPerformancePayload{ | |
| 33 | 36 | SkiaCPUThreads: resolved.SkiaCPUPaintingThreads, | |
| 34 | 37 | SkiaGPUThreads: resolved.SkiaGPUPaintingThreads, | |
| 35 | 38 | WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB, | |
| @@ -44,9 +47,9 @@ func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.Syste | |||
| 44 | 47 | return payload | |
| 45 | 48 | } | |
| 46 | 49 | ||
| 47 | - | func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIAppearanceConfig { | |
| 50 | + | func buildSystemviewAppearancePayload(appearance AppearanceConfig) dto.WebUIAppearanceConfig { | |
| 48 | 51 | defaults := DefaultConfig().Appearance | |
| 49 | - | return port.WebUIAppearanceConfig{ | |
| 52 | + | return dto.WebUIAppearanceConfig{ | |
| 50 | 53 | SansFont: appearance.SansFont, | |
| 51 | 54 | SerifFont: appearance.SerifFont, | |
| 52 | 55 | MonospaceFont: appearance.MonospaceFont, | |
| @@ -57,8 +60,8 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIApp | |||
| 57 | 60 | } | |
| 58 | 61 | } | |
| 59 | 62 | ||
| 60 | - | func buildSystemviewPalettePayload(palette, fallback ColorPalette) port.ColorPalette { | |
| 61 | - | return port.ColorPalette{ | |
| 63 | + | func buildSystemviewPalettePayload(palette, fallback ColorPalette) dto.ColorPalette { | |
| 64 | + | return dto.ColorPalette{ | |
| 62 | 65 | Background: nonEmptyColor(palette.Background, fallback.Background), | |
| 63 | 66 | Surface: nonEmptyColor(palette.Surface, fallback.Surface), | |
| 64 | 67 | SurfaceVariant: nonEmptyColor(palette.SurfaceVariant, fallback.SurfaceVariant), | |
| @@ -76,23 +79,23 @@ func nonEmptyColor(value, fallback string) string { | |||
| 76 | 79 | return fallback | |
| 77 | 80 | } | |
| 78 | 81 | ||
| 79 | - | func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]port.SearchShortcut { | |
| 82 | + | func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]dto.SearchShortcut { | |
| 80 | 83 | if len(shortcuts) == 0 { | |
| 81 | - | return map[string]port.SearchShortcut{} | |
| 84 | + | return map[string]dto.SearchShortcut{} | |
| 82 | 85 | } | |
| 83 | 86 | ||
| 84 | - | result := make(map[string]port.SearchShortcut, len(shortcuts)) | |
| 87 | + | result := make(map[string]dto.SearchShortcut, len(shortcuts)) | |
| 85 | 88 | for key, shortcut := range shortcuts { | |
| 86 | - | result[key] = port.SearchShortcut{URL: shortcut.URL, Description: shortcut.Description} | |
| 89 | + | result[key] = dto.SearchShortcut{URL: shortcut.URL, Description: shortcut.Description} | |
| 87 | 90 | } | |
| 88 | 91 | return result | |
| 89 | 92 | } | |
| 90 | 93 | ||
| 91 | - | func buildSystemviewHardwarePayload(hw *port.HardwareInfo) port.SystemviewHardwarePayload { | |
| 94 | + | func buildSystemviewHardwarePayload(hw *port.HardwareInfo) dto.SystemviewHardwarePayload { | |
| 92 | 95 | if hw == nil { | |
| 93 | - | return port.SystemviewHardwarePayload{} | |
| 96 | + | return dto.SystemviewHardwarePayload{} | |
| 94 | 97 | } | |
| 95 | - | return port.SystemviewHardwarePayload{ | |
| 98 | + | return dto.SystemviewHardwarePayload{ | |
| 96 | 99 | CPUCores: hw.CPUCores, | |
| 97 | 100 | CPUThreads: hw.CPUThreads, | |
| 98 | 101 | TotalRAMMB: hw.TotalRAMMB(), | |
| @@ -1,12 +1,15 @@ | |||||
| 1 | package config | 1 | package config | ||
| 2 | 2 | ||||
| 3 | - | import "github.com/bnema/dumber/internal/application/port" | 3 | + | import ( |
| 4 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 5 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 6 | + | ) | |||
| 4 | 7 | ||||
| 5 | // BuildSystemviewConfigPayload projects the full config into the stable DTO used by | 8 | // BuildSystemviewConfigPayload projects the full config into the stable DTO used by | ||
| 6 | // the settings page across engines. | 9 | // the settings page across engines. | ||
| 7 | - | func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.SystemviewConfigPayload { | 10 | + | func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) dto.SystemviewConfigPayload { |
| 8 | - | payload := port.SystemviewConfigPayload{ | 11 | + | payload := dto.SystemviewConfigPayload{ |
| 9 | - | SearchShortcuts: map[string]port.SearchShortcut{}, | 12 | + | SearchShortcuts: map[string]dto.SearchShortcut{}, |
| 10 | } | 13 | } | ||
| 11 | if cfg == nil { | 14 | if cfg == nil { | ||
| 12 | return payload | 15 | return payload | ||
| @@ -20,16 +23,16 @@ func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.Syste | |||||
| 20 | payload.DefaultSearchEngine = cfg.DefaultSearchEngine | 23 | payload.DefaultSearchEngine = cfg.DefaultSearchEngine | ||
| 21 | payload.SearchShortcuts = buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts) | 24 | payload.SearchShortcuts = buildSystemviewSearchShortcutsPayload(cfg.SearchShortcuts) | ||
| 22 | payload.EngineType = cfg.Engine.ResolveEngineType() | 25 | payload.EngineType = cfg.Engine.ResolveEngineType() | ||
| 23 | - | payload.Performance = port.SystemviewPerformancePayload{ | 26 | + | payload.Performance = dto.SystemviewPerformancePayload{ |
| 24 | Profile: string(cfg.Engine.Profile), | 27 | Profile: string(cfg.Engine.Profile), | ||
| 25 | - | Custom: port.SystemviewCustomPerformancePayload{ | 28 | + | Custom: dto.SystemviewCustomPerformancePayload{ |
| 26 | SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads, | 29 | SkiaCPUThreads: cfg.Engine.WebKit.SkiaCPUPaintingThreads, | ||
| 27 | SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads, | 30 | SkiaGPUThreads: cfg.Engine.WebKit.SkiaGPUPaintingThreads, | ||
| 28 | WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB, | 31 | WebProcessMemoryMB: cfg.Engine.WebKit.WebProcessMemoryLimitMB, | ||
| 29 | NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB, | 32 | NetworkProcessMemoryMB: cfg.Engine.WebKit.NetworkProcessMemoryLimitMB, | ||
| 30 | WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount, | 33 | WebViewPoolPrewarm: cfg.Engine.PoolPrewarmCount, | ||
| 31 | }, | 34 | }, | ||
| 32 | - | Resolved: port.SystemviewResolvedPerformancePayload{ | 35 | + | Resolved: dto.SystemviewResolvedPerformancePayload{ |
| 33 | SkiaCPUThreads: resolved.SkiaCPUPaintingThreads, | 36 | SkiaCPUThreads: resolved.SkiaCPUPaintingThreads, | ||
| 34 | SkiaGPUThreads: resolved.SkiaGPUPaintingThreads, | 37 | SkiaGPUThreads: resolved.SkiaGPUPaintingThreads, | ||
| 35 | WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB, | 38 | WebProcessMemoryMB: resolved.WebProcessMemoryLimitMB, | ||
| @@ -44,9 +47,9 @@ func BuildSystemviewConfigPayload(cfg *Config, hw *port.HardwareInfo) port.Syste | |||||
| 44 | return payload | 47 | return payload | ||
| 45 | } | 48 | } | ||
| 46 | 49 | ||||
| 47 | - | func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIAppearanceConfig { | 50 | + | func buildSystemviewAppearancePayload(appearance AppearanceConfig) dto.WebUIAppearanceConfig { |
| 48 | defaults := DefaultConfig().Appearance | 51 | defaults := DefaultConfig().Appearance | ||
| 49 | - | return port.WebUIAppearanceConfig{ | 52 | + | return dto.WebUIAppearanceConfig{ |
| 50 | SansFont: appearance.SansFont, | 53 | SansFont: appearance.SansFont, | ||
| 51 | SerifFont: appearance.SerifFont, | 54 | SerifFont: appearance.SerifFont, | ||
| 52 | MonospaceFont: appearance.MonospaceFont, | 55 | MonospaceFont: appearance.MonospaceFont, | ||
| @@ -57,8 +60,8 @@ func buildSystemviewAppearancePayload(appearance AppearanceConfig) port.WebUIApp | |||||
| 57 | } | 60 | } | ||
| 58 | } | 61 | } | ||
| 59 | 62 | ||||
| 60 | - | func buildSystemviewPalettePayload(palette, fallback ColorPalette) port.ColorPalette { | 63 | + | func buildSystemviewPalettePayload(palette, fallback ColorPalette) dto.ColorPalette { |
| 61 | - | return port.ColorPalette{ | 64 | + | return dto.ColorPalette{ |
| 62 | Background: nonEmptyColor(palette.Background, fallback.Background), | 65 | Background: nonEmptyColor(palette.Background, fallback.Background), | ||
| 63 | Surface: nonEmptyColor(palette.Surface, fallback.Surface), | 66 | Surface: nonEmptyColor(palette.Surface, fallback.Surface), | ||
| 64 | SurfaceVariant: nonEmptyColor(palette.SurfaceVariant, fallback.SurfaceVariant), | 67 | SurfaceVariant: nonEmptyColor(palette.SurfaceVariant, fallback.SurfaceVariant), | ||
| @@ -76,23 +79,23 @@ func nonEmptyColor(value, fallback string) string { | |||||
| 76 | return fallback | 79 | return fallback | ||
| 77 | } | 80 | } | ||
| 78 | 81 | ||||
| 79 | - | func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]port.SearchShortcut { | 82 | + | func buildSystemviewSearchShortcutsPayload(shortcuts map[string]SearchShortcut) map[string]dto.SearchShortcut { |
| 80 | if len(shortcuts) == 0 { | 83 | if len(shortcuts) == 0 { | ||
| 81 | - | return map[string]port.SearchShortcut{} | 84 | + | return map[string]dto.SearchShortcut{} |
| 82 | } | 85 | } | ||
| 83 | 86 | ||||
| 84 | - | result := make(map[string]port.SearchShortcut, len(shortcuts)) | 87 | + | result := make(map[string]dto.SearchShortcut, len(shortcuts)) |
| 85 | for key, shortcut := range shortcuts { | 88 | for key, shortcut := range shortcuts { | ||
| 86 | - | result[key] = port.SearchShortcut{URL: shortcut.URL, Description: shortcut.Description} | 89 | + | result[key] = dto.SearchShortcut{URL: shortcut.URL, Description: shortcut.Description} |
| 87 | } | 90 | } | ||
| 88 | return result | 91 | return result | ||
| 89 | } | 92 | } | ||
| 90 | 93 | ||||
| 91 | - | func buildSystemviewHardwarePayload(hw *port.HardwareInfo) port.SystemviewHardwarePayload { | 94 | + | func buildSystemviewHardwarePayload(hw *port.HardwareInfo) dto.SystemviewHardwarePayload { |
| 92 | if hw == nil { | 95 | if hw == nil { | ||
| 93 | - | return port.SystemviewHardwarePayload{} | 96 | + | return dto.SystemviewHardwarePayload{} |
| 94 | } | 97 | } | ||
| 95 | - | return port.SystemviewHardwarePayload{ | 98 | + | return dto.SystemviewHardwarePayload{ |
| 96 | CPUCores: hw.CPUCores, | 99 | CPUCores: hw.CPUCores, | ||
| 97 | CPUThreads: hw.CPUThreads, | 100 | CPUThreads: hw.CPUThreads, | ||
| 98 | TotalRAMMB: hw.TotalRAMMB(), | 101 | TotalRAMMB: hw.TotalRAMMB(), | ||
M internal/infrastructure/handlers/config.go +5 -4
| @@ -5,18 +5,19 @@ import ( | |||
| 5 | 5 | "encoding/json" | |
| 6 | 6 | "fmt" | |
| 7 | 7 | ||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 9 | "github.com/bnema/dumber/internal/application/port" | |
| 9 | 10 | "github.com/bnema/dumber/internal/logging" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | 13 | // ConfigHandler handles configuration-related messages. | |
| 13 | 14 | type ConfigHandler struct { | |
| 14 | - | saveConfig func(context.Context, port.WebUIConfig) error | |
| 15 | + | saveConfig func(context.Context, dto.WebUIConfig) error | |
| 15 | 16 | } | |
| 16 | 17 | ||
| 17 | 18 | // NewConfigHandler creates a new ConfigHandler. | |
| 18 | 19 | // saveConfig is called to persist config changes (typically usecase.SaveWebUIConfigUseCase.Execute). | |
| 19 | - | func NewConfigHandler(saveConfig func(context.Context, port.WebUIConfig) error) *ConfigHandler { | |
| 20 | + | func NewConfigHandler(saveConfig func(context.Context, dto.WebUIConfig) error) *ConfigHandler { | |
| 20 | 21 | if saveConfig == nil { | |
| 21 | 22 | panic("NewConfigHandler: saveConfig must not be nil") | |
| 22 | 23 | } | |
| @@ -33,7 +34,7 @@ func (h *ConfigHandler) Handle(ctx context.Context, viewID port.WebViewID, paylo | |||
| 33 | 34 | } | |
| 34 | 35 | log := logging.FromContext(ctx).With().Str("handler", "config").Logger() | |
| 35 | 36 | ||
| 36 | - | var payloadCfg port.WebUIConfig | |
| 37 | + | var payloadCfg dto.WebUIConfig | |
| 37 | 38 | if err := json.Unmarshal(payload, &payloadCfg); err != nil { | |
| 38 | 39 | log.Error().Err(err).Msg("failed to unmarshal config payload") | |
| 39 | 40 | return nil, fmt.Errorf("invalid config format: %w", err) | |
| @@ -56,7 +57,7 @@ func (h *ConfigHandler) Handle(ctx context.Context, viewID port.WebViewID, paylo | |||
| 56 | 57 | // RegisterConfigHandlers registers configuration handlers with the router. | |
| 57 | 58 | func RegisterConfigHandlers( | |
| 58 | 59 | ctx context.Context, router port.WebUIHandlerRouter, | |
| 59 | - | saveConfig func(context.Context, port.WebUIConfig) error, | |
| 60 | + | saveConfig func(context.Context, dto.WebUIConfig) error, | |
| 60 | 61 | ) error { | |
| 61 | 62 | handler := NewConfigHandler(saveConfig) | |
| 62 | 63 | ||
| @@ -5,18 +5,19 @@ import ( | |||||
| 5 | "encoding/json" | 5 | "encoding/json" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | 7 | ||||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 8 | "github.com/bnema/dumber/internal/application/port" | 9 | "github.com/bnema/dumber/internal/application/port" | ||
| 9 | "github.com/bnema/dumber/internal/logging" | 10 | "github.com/bnema/dumber/internal/logging" | ||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| 12 | // ConfigHandler handles configuration-related messages. | 13 | // ConfigHandler handles configuration-related messages. | ||
| 13 | type ConfigHandler struct { | 14 | type ConfigHandler struct { | ||
| 14 | - | saveConfig func(context.Context, port.WebUIConfig) error | 15 | + | saveConfig func(context.Context, dto.WebUIConfig) error |
| 15 | } | 16 | } | ||
| 16 | 17 | ||||
| 17 | // NewConfigHandler creates a new ConfigHandler. | 18 | // NewConfigHandler creates a new ConfigHandler. | ||
| 18 | // saveConfig is called to persist config changes (typically usecase.SaveWebUIConfigUseCase.Execute). | 19 | // saveConfig is called to persist config changes (typically usecase.SaveWebUIConfigUseCase.Execute). | ||
| 19 | - | func NewConfigHandler(saveConfig func(context.Context, port.WebUIConfig) error) *ConfigHandler { | 20 | + | func NewConfigHandler(saveConfig func(context.Context, dto.WebUIConfig) error) *ConfigHandler { |
| 20 | if saveConfig == nil { | 21 | if saveConfig == nil { | ||
| 21 | panic("NewConfigHandler: saveConfig must not be nil") | 22 | panic("NewConfigHandler: saveConfig must not be nil") | ||
| 22 | } | 23 | } | ||
| @@ -33,7 +34,7 @@ func (h *ConfigHandler) Handle(ctx context.Context, viewID port.WebViewID, paylo | |||||
| 33 | } | 34 | } | ||
| 34 | log := logging.FromContext(ctx).With().Str("handler", "config").Logger() | 35 | log := logging.FromContext(ctx).With().Str("handler", "config").Logger() | ||
| 35 | 36 | ||||
| 36 | - | var payloadCfg port.WebUIConfig | 37 | + | var payloadCfg dto.WebUIConfig |
| 37 | if err := json.Unmarshal(payload, &payloadCfg); err != nil { | 38 | if err := json.Unmarshal(payload, &payloadCfg); err != nil { | ||
| 38 | log.Error().Err(err).Msg("failed to unmarshal config payload") | 39 | log.Error().Err(err).Msg("failed to unmarshal config payload") | ||
| 39 | return nil, fmt.Errorf("invalid config format: %w", err) | 40 | return nil, fmt.Errorf("invalid config format: %w", err) | ||
| @@ -56,7 +57,7 @@ func (h *ConfigHandler) Handle(ctx context.Context, viewID port.WebViewID, paylo | |||||
| 56 | // RegisterConfigHandlers registers configuration handlers with the router. | 57 | // RegisterConfigHandlers registers configuration handlers with the router. | ||
| 57 | func RegisterConfigHandlers( | 58 | func RegisterConfigHandlers( | ||
| 58 | ctx context.Context, router port.WebUIHandlerRouter, | 59 | ctx context.Context, router port.WebUIHandlerRouter, | ||
| 59 | - | saveConfig func(context.Context, port.WebUIConfig) error, | 60 | + | saveConfig func(context.Context, dto.WebUIConfig) error, |
| 60 | ) error { | 61 | ) error { | ||
| 61 | handler := NewConfigHandler(saveConfig) | 62 | handler := NewConfigHandler(saveConfig) | ||
| 62 | 63 | ||||
M internal/infrastructure/handlers/homepage/favorites.go +36 -14
| @@ -11,22 +11,26 @@ import ( | |||
| 11 | 11 | "github.com/bnema/dumber/internal/logging" | |
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | - | func folderIDFromInt64(id *int64) *entity.FolderID { | |
| 14 | + | func folderIDFromInt64(id *int64) (*entity.FolderID, error) { | |
| 15 | 15 | if id == nil { | |
| 16 | - | return nil | |
| 16 | + | return nil, nil | |
| 17 | + | } | |
| 18 | + | if *id <= 0 { | |
| 19 | + | return nil, fmt.Errorf("folder id must be positive") | |
| 17 | 20 | } | |
| 18 | 21 | folderID := entity.FolderID(*id) | |
| 19 | - | return &folderID | |
| 22 | + | return &folderID, nil | |
| 20 | 23 | } | |
| 21 | 24 | ||
| 22 | - | func tagIDsFromInt64s(ids []int64) []entity.TagID { | |
| 25 | + | func tagIDsFromInt64s(ids []int64) ([]entity.TagID, error) { | |
| 23 | 26 | out := make([]entity.TagID, 0, len(ids)) | |
| 24 | 27 | for _, id := range ids { | |
| 25 | - | if id > 0 { | |
| 26 | - | out = append(out, entity.TagID(id)) | |
| 28 | + | if id <= 0 { | |
| 29 | + | return nil, fmt.Errorf("tag id must be positive") | |
| 27 | 30 | } | |
| 31 | + | out = append(out, entity.TagID(id)) | |
| 28 | 32 | } | |
| 29 | - | return out | |
| 33 | + | return out, nil | |
| 30 | 34 | } | |
| 31 | 35 | ||
| 32 | 36 | // FavoritesHandlers handles favorite-related messages from the homepage. | |
| @@ -80,19 +84,27 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler { | |||
| 80 | 84 | ||
| 81 | 85 | log.Debug(). | |
| 82 | 86 | Str("request_id", req.RequestID). | |
| 83 | - | Str("url", req.URL). | |
| 84 | 87 | Msg("handling favorite_create") | |
| 85 | 88 | ||
| 86 | - | if strings.TrimSpace(req.URL) == "" { | |
| 89 | + | trimmedURL := strings.TrimSpace(req.URL) | |
| 90 | + | if trimmedURL == "" { | |
| 87 | 91 | return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil | |
| 88 | 92 | } | |
| 93 | + | folderID, err := folderIDFromInt64(req.FolderID) | |
| 94 | + | if err != nil { | |
| 95 | + | return NewErrorResponse(req.RequestID, err), nil | |
| 96 | + | } | |
| 97 | + | tags, err := tagIDsFromInt64s(req.Tags) | |
| 98 | + | if err != nil { | |
| 99 | + | return NewErrorResponse(req.RequestID, err), nil | |
| 100 | + | } | |
| 89 | 101 | ||
| 90 | 102 | input := port.FavoriteCreateInput{ | |
| 91 | - | URL: req.URL, | |
| 103 | + | URL: trimmedURL, | |
| 92 | 104 | Title: req.Title, | |
| 93 | 105 | FaviconURL: req.FaviconURL, | |
| 94 | - | FolderID: folderIDFromInt64(req.FolderID), | |
| 95 | - | Tags: tagIDsFromInt64s(req.Tags), | |
| 106 | + | FolderID: folderID, | |
| 107 | + | Tags: tags, | |
| 96 | 108 | } | |
| 97 | 109 | favorite, err := h.favoritesUC.AddFavorite(ctx, input) | |
| 98 | 110 | if err != nil { | |
| @@ -131,11 +143,16 @@ func (h *FavoritesHandlers) HandleUpdate() port.WebUIMessageHandler { | |||
| 131 | 143 | return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil | |
| 132 | 144 | } | |
| 133 | 145 | ||
| 146 | + | folderID, err := folderIDFromInt64(req.FolderID) | |
| 147 | + | if err != nil { | |
| 148 | + | return NewErrorResponse(req.RequestID, err), nil | |
| 149 | + | } | |
| 150 | + | ||
| 134 | 151 | favorite, err := h.favoritesUC.UpdateFavorite(ctx, port.FavoriteUpdateInput{ | |
| 135 | 152 | ID: entity.FavoriteID(req.ID), | |
| 136 | 153 | Title: req.Title, | |
| 137 | 154 | FaviconURL: req.FaviconURL, | |
| 138 | - | FolderID: folderIDFromInt64(req.FolderID), | |
| 155 | + | FolderID: folderID, | |
| 139 | 156 | ShortcutKey: req.ShortcutKey, | |
| 140 | 157 | }) | |
| 141 | 158 | if err != nil { | |
| @@ -260,7 +277,12 @@ func (h *FavoritesHandlers) HandleSetFolder() port.WebUIMessageHandler { | |||
| 260 | 277 | Int64("favorite_id", req.FavoriteID). | |
| 261 | 278 | Msg("handling favorite_set_folder") | |
| 262 | 279 | ||
| 263 | - | if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderIDFromInt64(req.FolderID)); err != nil { | |
| 280 | + | folderID, err := folderIDFromInt64(req.FolderID) | |
| 281 | + | if err != nil { | |
| 282 | + | return NewErrorResponse(req.RequestID, err), nil | |
| 283 | + | } | |
| 284 | + | ||
| 285 | + | if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderID); err != nil { | |
| 264 | 286 | return NewErrorResponse(req.RequestID, err), nil | |
| 265 | 287 | } | |
| 266 | 288 | ||
| @@ -11,22 +11,26 @@ import ( | |||||
| 11 | "github.com/bnema/dumber/internal/logging" | 11 | "github.com/bnema/dumber/internal/logging" | ||
| 12 | ) | 12 | ) | ||
| 13 | 13 | ||||
| 14 | - | func folderIDFromInt64(id *int64) *entity.FolderID { | 14 | + | func folderIDFromInt64(id *int64) (*entity.FolderID, error) { |
| 15 | if id == nil { | 15 | if id == nil { | ||
| 16 | - | return nil | 16 | + | return nil, nil |
| 17 | + | } | |||
| 18 | + | if *id <= 0 { | |||
| 19 | + | return nil, fmt.Errorf("folder id must be positive") | |||
| 17 | } | 20 | } | ||
| 18 | folderID := entity.FolderID(*id) | 21 | folderID := entity.FolderID(*id) | ||
| 19 | - | return &folderID | 22 | + | return &folderID, nil |
| 20 | } | 23 | } | ||
| 21 | 24 | ||||
| 22 | - | func tagIDsFromInt64s(ids []int64) []entity.TagID { | 25 | + | func tagIDsFromInt64s(ids []int64) ([]entity.TagID, error) { |
| 23 | out := make([]entity.TagID, 0, len(ids)) | 26 | out := make([]entity.TagID, 0, len(ids)) | ||
| 24 | for _, id := range ids { | 27 | for _, id := range ids { | ||
| 25 | - | if id > 0 { | 28 | + | if id <= 0 { |
| 26 | - | out = append(out, entity.TagID(id)) | 29 | + | return nil, fmt.Errorf("tag id must be positive") |
| 27 | } | 30 | } | ||
| 31 | + | out = append(out, entity.TagID(id)) | |||
| 28 | } | 32 | } | ||
| 29 | - | return out | 33 | + | return out, nil |
| 30 | } | 34 | } | ||
| 31 | 35 | ||||
| 32 | // FavoritesHandlers handles favorite-related messages from the homepage. | 36 | // FavoritesHandlers handles favorite-related messages from the homepage. | ||
| @@ -80,19 +84,27 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler { | |||||
| 80 | 84 | ||||
| 81 | log.Debug(). | 85 | log.Debug(). | ||
| 82 | Str("request_id", req.RequestID). | 86 | Str("request_id", req.RequestID). | ||
| 83 | - | Str("url", req.URL). | |||
| 84 | Msg("handling favorite_create") | 87 | Msg("handling favorite_create") | ||
| 85 | 88 | ||||
| 86 | - | if strings.TrimSpace(req.URL) == "" { | 89 | + | trimmedURL := strings.TrimSpace(req.URL) |
| 90 | + | if trimmedURL == "" { | |||
| 87 | return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil | 91 | return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil | ||
| 88 | } | 92 | } | ||
| 93 | + | folderID, err := folderIDFromInt64(req.FolderID) | |||
| 94 | + | if err != nil { | |||
| 95 | + | return NewErrorResponse(req.RequestID, err), nil | |||
| 96 | + | } | |||
| 97 | + | tags, err := tagIDsFromInt64s(req.Tags) | |||
| 98 | + | if err != nil { | |||
| 99 | + | return NewErrorResponse(req.RequestID, err), nil | |||
| 100 | + | } | |||
| 89 | 101 | ||||
| 90 | input := port.FavoriteCreateInput{ | 102 | input := port.FavoriteCreateInput{ | ||
| 91 | - | URL: req.URL, | 103 | + | URL: trimmedURL, |
| 92 | Title: req.Title, | 104 | Title: req.Title, | ||
| 93 | FaviconURL: req.FaviconURL, | 105 | FaviconURL: req.FaviconURL, | ||
| 94 | - | FolderID: folderIDFromInt64(req.FolderID), | 106 | + | FolderID: folderID, |
| 95 | - | Tags: tagIDsFromInt64s(req.Tags), | 107 | + | Tags: tags, |
| 96 | } | 108 | } | ||
| 97 | favorite, err := h.favoritesUC.AddFavorite(ctx, input) | 109 | favorite, err := h.favoritesUC.AddFavorite(ctx, input) | ||
| 98 | if err != nil { | 110 | if err != nil { | ||
| @@ -131,11 +143,16 @@ func (h *FavoritesHandlers) HandleUpdate() port.WebUIMessageHandler { | |||||
| 131 | return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil | 143 | return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil | ||
| 132 | } | 144 | } | ||
| 133 | 145 | ||||
| 146 | + | folderID, err := folderIDFromInt64(req.FolderID) | |||
| 147 | + | if err != nil { | |||
| 148 | + | return NewErrorResponse(req.RequestID, err), nil | |||
| 149 | + | } | |||
| 150 | + | ||||
| 134 | favorite, err := h.favoritesUC.UpdateFavorite(ctx, port.FavoriteUpdateInput{ | 151 | favorite, err := h.favoritesUC.UpdateFavorite(ctx, port.FavoriteUpdateInput{ | ||
| 135 | ID: entity.FavoriteID(req.ID), | 152 | ID: entity.FavoriteID(req.ID), | ||
| 136 | Title: req.Title, | 153 | Title: req.Title, | ||
| 137 | FaviconURL: req.FaviconURL, | 154 | FaviconURL: req.FaviconURL, | ||
| 138 | - | FolderID: folderIDFromInt64(req.FolderID), | 155 | + | FolderID: folderID, |
| 139 | ShortcutKey: req.ShortcutKey, | 156 | ShortcutKey: req.ShortcutKey, | ||
| 140 | }) | 157 | }) | ||
| 141 | if err != nil { | 158 | if err != nil { | ||
| @@ -260,7 +277,12 @@ func (h *FavoritesHandlers) HandleSetFolder() port.WebUIMessageHandler { | |||||
| 260 | Int64("favorite_id", req.FavoriteID). | 277 | Int64("favorite_id", req.FavoriteID). | ||
| 261 | Msg("handling favorite_set_folder") | 278 | Msg("handling favorite_set_folder") | ||
| 262 | 279 | ||||
| 263 | - | if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderIDFromInt64(req.FolderID)); err != nil { | 280 | + | folderID, err := folderIDFromInt64(req.FolderID) |
| 281 | + | if err != nil { | |||
| 282 | + | return NewErrorResponse(req.RequestID, err), nil | |||
| 283 | + | } | |||
| 284 | + | ||||
| 285 | + | if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderID); err != nil { | |||
| 264 | return NewErrorResponse(req.RequestID, err), nil | 286 | return NewErrorResponse(req.RequestID, err), nil | ||
| 265 | } | 287 | } | ||
| 266 | 288 | ||||
M internal/infrastructure/handlers/homepage/folders.go +13 -10
| @@ -62,11 +62,12 @@ func (h *FolderHandlers) HandleCreate() port.WebUIMessageHandler { | |||
| 62 | 62 | Str("name", req.Name). | |
| 63 | 63 | Msg("handling folder_create") | |
| 64 | 64 | ||
| 65 | - | icon := "" | |
| 66 | - | if req.Icon != nil { | |
| 67 | - | icon = *req.Icon | |
| 65 | + | parentID, err := folderIDFromInt64(req.ParentID) | |
| 66 | + | if err != nil { | |
| 67 | + | return NewErrorResponse(req.RequestID, err), nil | |
| 68 | 68 | } | |
| 69 | - | folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, icon, folderIDFromInt64(req.ParentID)) | |
| 69 | + | ||
| 70 | + | folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, optionalStringValue(req.Icon), parentID) | |
| 70 | 71 | if err != nil { | |
| 71 | 72 | return NewErrorResponse(req.RequestID, err), nil | |
| 72 | 73 | } | |
| @@ -114,6 +115,13 @@ type updateFolderRequest struct { | |||
| 114 | 115 | ||
| 115 | 116 | // HandleUpdate handles folder_update messages. | |
| 116 | 117 | // It delegates to the port.HomepageFavorites interface's UpdateFolder method. | |
| 118 | + | func optionalStringValue(value *string) string { | |
| 119 | + | if value == nil { | |
| 120 | + | return "" | |
| 121 | + | } | |
| 122 | + | return *value | |
| 123 | + | } | |
| 124 | + | ||
| 117 | 125 | func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler { | |
| 118 | 126 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | |
| 119 | 127 | log := logging.FromContext(ctx) | |
| @@ -129,12 +137,7 @@ func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler { | |||
| 129 | 137 | Str("name", req.Name). | |
| 130 | 138 | Msg("handling folder_update") | |
| 131 | 139 | ||
| 132 | - | icon := "" | |
| 133 | - | if req.Icon != nil { | |
| 134 | - | icon = *req.Icon | |
| 135 | - | } | |
| 136 | - | ||
| 137 | - | if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, icon); err != nil { | |
| 140 | + | if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, optionalStringValue(req.Icon)); err != nil { | |
| 138 | 141 | return NewErrorResponse(req.RequestID, err), nil | |
| 139 | 142 | } | |
| 140 | 143 | ||
| @@ -62,11 +62,12 @@ func (h *FolderHandlers) HandleCreate() port.WebUIMessageHandler { | |||||
| 62 | Str("name", req.Name). | 62 | Str("name", req.Name). | ||
| 63 | Msg("handling folder_create") | 63 | Msg("handling folder_create") | ||
| 64 | 64 | ||||
| 65 | - | icon := "" | 65 | + | parentID, err := folderIDFromInt64(req.ParentID) |
| 66 | - | if req.Icon != nil { | 66 | + | if err != nil { |
| 67 | - | icon = *req.Icon | 67 | + | return NewErrorResponse(req.RequestID, err), nil |
| 68 | } | 68 | } | ||
| 69 | - | folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, icon, folderIDFromInt64(req.ParentID)) | 69 | + | |
| 70 | + | folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, optionalStringValue(req.Icon), parentID) | |||
| 70 | if err != nil { | 71 | if err != nil { | ||
| 71 | return NewErrorResponse(req.RequestID, err), nil | 72 | return NewErrorResponse(req.RequestID, err), nil | ||
| 72 | } | 73 | } | ||
| @@ -114,6 +115,13 @@ type updateFolderRequest struct { | |||||
| 114 | 115 | ||||
| 115 | // HandleUpdate handles folder_update messages. | 116 | // HandleUpdate handles folder_update messages. | ||
| 116 | // It delegates to the port.HomepageFavorites interface's UpdateFolder method. | 117 | // It delegates to the port.HomepageFavorites interface's UpdateFolder method. | ||
| 118 | + | func optionalStringValue(value *string) string { | |||
| 119 | + | if value == nil { | |||
| 120 | + | return "" | |||
| 121 | + | } | |||
| 122 | + | return *value | |||
| 123 | + | } | |||
| 124 | + | ||||
| 117 | func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler { | 125 | func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler { | ||
| 118 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | 126 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | ||
| 119 | log := logging.FromContext(ctx) | 127 | log := logging.FromContext(ctx) | ||
| @@ -129,12 +137,7 @@ func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler { | |||||
| 129 | Str("name", req.Name). | 137 | Str("name", req.Name). | ||
| 130 | Msg("handling folder_update") | 138 | Msg("handling folder_update") | ||
| 131 | 139 | ||||
| 132 | - | icon := "" | 140 | + | if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, optionalStringValue(req.Icon)); err != nil { |
| 133 | - | if req.Icon != nil { | |||
| 134 | - | icon = *req.Icon | |||
| 135 | - | } | |||
| 136 | - | ||||
| 137 | - | if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, icon); err != nil { | |||
| 138 | return NewErrorResponse(req.RequestID, err), nil | 141 | return NewErrorResponse(req.RequestID, err), nil | ||
| 139 | } | 142 | } | ||
| 140 | 143 | ||||
M internal/infrastructure/handlers/homepage/history.go +0 -12
| @@ -182,9 +182,6 @@ func (h *HistoryHandlers) HandleDeleteRange() port.WebUIMessageHandler { | |||
| 182 | 182 | Str("range", req.Range). | |
| 183 | 183 | Msg("handling history_delete_range") | |
| 184 | 184 | ||
| 185 | - | if !isAllowedHistoryDeleteRange(req.Range) { | |
| 186 | - | return NewErrorResponse(req.RequestID, fmt.Errorf("invalid range: %s", req.Range)), nil | |
| 187 | - | } | |
| 188 | 185 | if err := h.historyUC.ClearRange(ctx, req.Range); err != nil { | |
| 189 | 186 | return NewErrorResponse(req.RequestID, err), nil | |
| 190 | 187 | } | |
| @@ -193,15 +190,6 @@ func (h *HistoryHandlers) HandleDeleteRange() port.WebUIMessageHandler { | |||
| 193 | 190 | }) | |
| 194 | 191 | } | |
| 195 | 192 | ||
| 196 | - | func isAllowedHistoryDeleteRange(rangeID string) bool { | |
| 197 | - | switch rangeID { | |
| 198 | - | case "hour", "day", "week", "month", "all": | |
| 199 | - | return true | |
| 200 | - | default: | |
| 201 | - | return false | |
| 202 | - | } | |
| 203 | - | } | |
| 204 | - | ||
| 205 | 193 | // HandleClearAll handles history_clear_all messages. | |
| 206 | 194 | func (h *HistoryHandlers) HandleClearAll() port.WebUIMessageHandler { | |
| 207 | 195 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | |
| @@ -182,9 +182,6 @@ func (h *HistoryHandlers) HandleDeleteRange() port.WebUIMessageHandler { | |||||
| 182 | Str("range", req.Range). | 182 | Str("range", req.Range). | ||
| 183 | Msg("handling history_delete_range") | 183 | Msg("handling history_delete_range") | ||
| 184 | 184 | ||||
| 185 | - | if !isAllowedHistoryDeleteRange(req.Range) { | |||
| 186 | - | return NewErrorResponse(req.RequestID, fmt.Errorf("invalid range: %s", req.Range)), nil | |||
| 187 | - | } | |||
| 188 | if err := h.historyUC.ClearRange(ctx, req.Range); err != nil { | 185 | if err := h.historyUC.ClearRange(ctx, req.Range); err != nil { | ||
| 189 | return NewErrorResponse(req.RequestID, err), nil | 186 | return NewErrorResponse(req.RequestID, err), nil | ||
| 190 | } | 187 | } | ||
| @@ -193,15 +190,6 @@ func (h *HistoryHandlers) HandleDeleteRange() port.WebUIMessageHandler { | |||||
| 193 | }) | 190 | }) | ||
| 194 | } | 191 | } | ||
| 195 | 192 | ||||
| 196 | - | func isAllowedHistoryDeleteRange(rangeID string) bool { | |||
| 197 | - | switch rangeID { | |||
| 198 | - | case "hour", "day", "week", "month", "all": | |||
| 199 | - | return true | |||
| 200 | - | default: | |||
| 201 | - | return false | |||
| 202 | - | } | |||
| 203 | - | } | |||
| 204 | - | ||||
| 205 | // HandleClearAll handles history_clear_all messages. | 193 | // HandleClearAll handles history_clear_all messages. | ||
| 206 | func (h *HistoryHandlers) HandleClearAll() port.WebUIMessageHandler { | 194 | func (h *HistoryHandlers) HandleClearAll() port.WebUIMessageHandler { | ||
| 207 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | 195 | return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) { | ||
M internal/infrastructure/persistence/sqlite/history_repo.go +3 -3
| @@ -494,9 +494,10 @@ func (r *historyRepo) GetRecent(ctx context.Context, limit, offset int) ([]*enti | |||
| 494 | 494 | } | |
| 495 | 495 | ||
| 496 | 496 | func (r *historyRepo) GetRecentByDomain(ctx context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) { | |
| 497 | + | rawDomain := domain | |
| 497 | 498 | domain = domainurl.CanonicalDomain(domain) | |
| 498 | 499 | if domain == "" { | |
| 499 | - | return []*entity.HistoryEntry{}, nil | |
| 500 | + | return nil, fmt.Errorf("history domain is required: %q", rawDomain) | |
| 500 | 501 | } | |
| 501 | 502 | rows, err := r.queries.GetRecentHistoryByDomain(ctx, sqlc.GetRecentHistoryByDomainParams{ | |
| 502 | 503 | Domain: sql.NullString{String: domain, Valid: true}, | |
| @@ -597,8 +598,7 @@ func (r *historyRepo) DeleteByDomain(ctx context.Context, domain string) error { | |||
| 597 | 598 | rawDomain := domain | |
| 598 | 599 | domain = domainurl.CanonicalDomain(domain) | |
| 599 | 600 | if domain == "" { | |
| 600 | - | logging.FromContext(ctx).Debug().Str("domain", rawDomain).Msg("skipping delete by domain: empty canonical domain") | |
| 601 | - | return nil | |
| 601 | + | return fmt.Errorf("history domain is required: %q", rawDomain) | |
| 602 | 602 | } | |
| 603 | 603 | return r.queries.DeleteHistoryByDomain(ctx, sql.NullString{String: domain, Valid: true}) | |
| 604 | 604 | } | |
| @@ -494,9 +494,10 @@ func (r *historyRepo) GetRecent(ctx context.Context, limit, offset int) ([]*enti | |||||
| 494 | } | 494 | } | ||
| 495 | 495 | ||||
| 496 | func (r *historyRepo) GetRecentByDomain(ctx context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) { | 496 | func (r *historyRepo) GetRecentByDomain(ctx context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) { | ||
| 497 | + | rawDomain := domain | |||
| 497 | domain = domainurl.CanonicalDomain(domain) | 498 | domain = domainurl.CanonicalDomain(domain) | ||
| 498 | if domain == "" { | 499 | if domain == "" { | ||
| 499 | - | return []*entity.HistoryEntry{}, nil | 500 | + | return nil, fmt.Errorf("history domain is required: %q", rawDomain) |
| 500 | } | 501 | } | ||
| 501 | rows, err := r.queries.GetRecentHistoryByDomain(ctx, sqlc.GetRecentHistoryByDomainParams{ | 502 | rows, err := r.queries.GetRecentHistoryByDomain(ctx, sqlc.GetRecentHistoryByDomainParams{ | ||
| 502 | Domain: sql.NullString{String: domain, Valid: true}, | 503 | Domain: sql.NullString{String: domain, Valid: true}, | ||
| @@ -597,8 +598,7 @@ func (r *historyRepo) DeleteByDomain(ctx context.Context, domain string) error { | |||||
| 597 | rawDomain := domain | 598 | rawDomain := domain | ||
| 598 | domain = domainurl.CanonicalDomain(domain) | 599 | domain = domainurl.CanonicalDomain(domain) | ||
| 599 | if domain == "" { | 600 | if domain == "" { | ||
| 600 | - | logging.FromContext(ctx).Debug().Str("domain", rawDomain).Msg("skipping delete by domain: empty canonical domain") | 601 | + | return fmt.Errorf("history domain is required: %q", rawDomain) |
| 601 | - | return nil | |||
| 602 | } | 602 | } | ||
| 603 | return r.queries.DeleteHistoryByDomain(ctx, sql.NullString{String: domain, Valid: true}) | 603 | return r.queries.DeleteHistoryByDomain(ctx, sql.NullString{String: domain, Valid: true}) | ||
| 604 | } | 604 | } | ||
M internal/infrastructure/persistence/sqlite/history_repo_test.go +16 -0
| @@ -833,6 +833,22 @@ func TestHistoryRepository_Search_ReturnsEmptyForNonPositiveLimit(t *testing.T) | |||
| 833 | 833 | assert.Empty(t, results) | |
| 834 | 834 | } | |
| 835 | 835 | ||
| 836 | + | func TestHistoryRepository_RejectsEmptyCanonicalDomain(t *testing.T) { | |
| 837 | + | ctx := historyTestCtx() | |
| 838 | + | dbPath := filepath.Join(t.TempDir(), "dumber.db") | |
| 839 | + | ||
| 840 | + | db, err := sqlite.NewConnection(ctx, dbPath) | |
| 841 | + | require.NoError(t, err) | |
| 842 | + | t.Cleanup(func() { _ = db.Close() }) | |
| 843 | + | ||
| 844 | + | repo := sqlite.NewHistoryRepository(db) | |
| 845 | + | ||
| 846 | + | entries, err := repo.GetRecentByDomain(ctx, " ", 10, 0) | |
| 847 | + | require.Error(t, err) | |
| 848 | + | assert.Nil(t, entries) | |
| 849 | + | assert.Error(t, repo.DeleteByDomain(ctx, "/path")) | |
| 850 | + | } | |
| 851 | + | ||
| 836 | 852 | func TestHistoryRepository_DomainColumnPowersFilteringStatsAndDelete(t *testing.T) { | |
| 837 | 853 | ctx := historyTestCtx() | |
| 838 | 854 | dbPath := filepath.Join(t.TempDir(), "dumber.db") | |
| @@ -833,6 +833,22 @@ func TestHistoryRepository_Search_ReturnsEmptyForNonPositiveLimit(t *testing.T) | |||||
| 833 | assert.Empty(t, results) | 833 | assert.Empty(t, results) | ||
| 834 | } | 834 | } | ||
| 835 | 835 | ||||
| 836 | + | func TestHistoryRepository_RejectsEmptyCanonicalDomain(t *testing.T) { | |||
| 837 | + | ctx := historyTestCtx() | |||
| 838 | + | dbPath := filepath.Join(t.TempDir(), "dumber.db") | |||
| 839 | + | ||||
| 840 | + | db, err := sqlite.NewConnection(ctx, dbPath) | |||
| 841 | + | require.NoError(t, err) | |||
| 842 | + | t.Cleanup(func() { _ = db.Close() }) | |||
| 843 | + | ||||
| 844 | + | repo := sqlite.NewHistoryRepository(db) | |||
| 845 | + | ||||
| 846 | + | entries, err := repo.GetRecentByDomain(ctx, " ", 10, 0) | |||
| 847 | + | require.Error(t, err) | |||
| 848 | + | assert.Nil(t, entries) | |||
| 849 | + | assert.Error(t, repo.DeleteByDomain(ctx, "/path")) | |||
| 850 | + | } | |||
| 851 | + | ||||
| 836 | func TestHistoryRepository_DomainColumnPowersFilteringStatsAndDelete(t *testing.T) { | 852 | func TestHistoryRepository_DomainColumnPowersFilteringStatsAndDelete(t *testing.T) { | ||
| 837 | ctx := historyTestCtx() | 853 | ctx := historyTestCtx() | ||
| 838 | dbPath := filepath.Join(t.TempDir(), "dumber.db") | 854 | dbPath := filepath.Join(t.TempDir(), "dumber.db") | ||
M internal/infrastructure/persistence/sqlite/migrations/008_history_domain.sql +57 -1
| @@ -31,4 +31,60 @@ CREATE INDEX IF NOT EXISTS idx_history_domain_last_visited ON history(domain, la | |||
| 31 | 31 | ||
| 32 | 32 | -- +goose Down | |
| 33 | 33 | DROP INDEX IF EXISTS idx_history_domain_last_visited; | |
| 34 | - | ALTER TABLE history DROP COLUMN domain; | |
| 34 | + | ||
| 35 | + | -- Rebuild instead of ALTER TABLE DROP COLUMN so rollback works on SQLite | |
| 36 | + | -- versions before 3.35. Preserve the base history schema and indexes/triggers. | |
| 37 | + | DROP TRIGGER IF EXISTS history_fts_update; | |
| 38 | + | DROP TRIGGER IF EXISTS history_fts_delete; | |
| 39 | + | DROP TRIGGER IF EXISTS history_fts_insert; | |
| 40 | + | DROP TABLE IF EXISTS history_fts; | |
| 41 | + | ||
| 42 | + | CREATE TABLE history_008_down ( | |
| 43 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| 44 | + | url TEXT NOT NULL UNIQUE, | |
| 45 | + | title TEXT, | |
| 46 | + | favicon_url TEXT, | |
| 47 | + | visit_count INTEGER DEFAULT 1, | |
| 48 | + | last_visited DATETIME DEFAULT CURRENT_TIMESTAMP, | |
| 49 | + | created_at DATETIME DEFAULT CURRENT_TIMESTAMP | |
| 50 | + | ); | |
| 51 | + | ||
| 52 | + | INSERT INTO history_008_down (id, url, title, favicon_url, visit_count, last_visited, created_at) | |
| 53 | + | SELECT id, url, title, favicon_url, visit_count, last_visited, created_at | |
| 54 | + | FROM history; | |
| 55 | + | ||
| 56 | + | DROP TABLE history; | |
| 57 | + | ALTER TABLE history_008_down RENAME TO history; | |
| 58 | + | ||
| 59 | + | CREATE INDEX IF NOT EXISTS idx_history_url ON history(url); | |
| 60 | + | CREATE INDEX IF NOT EXISTS idx_history_last_visited ON history(last_visited); | |
| 61 | + | CREATE INDEX IF NOT EXISTS idx_history_visit_count_last_visited ON history(visit_count DESC, last_visited DESC); | |
| 62 | + | ||
| 63 | + | CREATE VIRTUAL TABLE IF NOT EXISTS history_fts USING fts5( | |
| 64 | + | url, | |
| 65 | + | title, | |
| 66 | + | content='history', | |
| 67 | + | content_rowid='id' | |
| 68 | + | ); | |
| 69 | + | ||
| 70 | + | -- +goose StatementBegin | |
| 71 | + | CREATE TRIGGER IF NOT EXISTS history_fts_insert AFTER INSERT ON history BEGIN | |
| 72 | + | INSERT INTO history_fts(rowid, url, title) VALUES (NEW.id, NEW.url, NEW.title); | |
| 73 | + | END; | |
| 74 | + | -- +goose StatementEnd | |
| 75 | + | ||
| 76 | + | -- +goose StatementBegin | |
| 77 | + | CREATE TRIGGER IF NOT EXISTS history_fts_delete AFTER DELETE ON history BEGIN | |
| 78 | + | INSERT INTO history_fts(history_fts, rowid, url, title) VALUES('delete', OLD.id, OLD.url, OLD.title); | |
| 79 | + | END; | |
| 80 | + | -- +goose StatementEnd | |
| 81 | + | ||
| 82 | + | -- +goose StatementBegin | |
| 83 | + | CREATE TRIGGER IF NOT EXISTS history_fts_update AFTER UPDATE ON history BEGIN | |
| 84 | + | INSERT INTO history_fts(history_fts, rowid, url, title) VALUES('delete', OLD.id, OLD.url, OLD.title); | |
| 85 | + | INSERT INTO history_fts(rowid, url, title) VALUES (NEW.id, NEW.url, NEW.title); | |
| 86 | + | END; | |
| 87 | + | -- +goose StatementEnd | |
| 88 | + | ||
| 89 | + | INSERT INTO history_fts(rowid, url, title) | |
| 90 | + | SELECT id, url, title FROM history; | |
| @@ -31,4 +31,60 @@ CREATE INDEX IF NOT EXISTS idx_history_domain_last_visited ON history(domain, la | |||||
| 31 | 31 | ||||
| 32 | -- +goose Down | 32 | -- +goose Down | ||
| 33 | DROP INDEX IF EXISTS idx_history_domain_last_visited; | 33 | DROP INDEX IF EXISTS idx_history_domain_last_visited; | ||
| 34 | - | ALTER TABLE history DROP COLUMN domain; | 34 | + | |
| 35 | + | -- Rebuild instead of ALTER TABLE DROP COLUMN so rollback works on SQLite | |||
| 36 | + | -- versions before 3.35. Preserve the base history schema and indexes/triggers. | |||
| 37 | + | DROP TRIGGER IF EXISTS history_fts_update; | |||
| 38 | + | DROP TRIGGER IF EXISTS history_fts_delete; | |||
| 39 | + | DROP TRIGGER IF EXISTS history_fts_insert; | |||
| 40 | + | DROP TABLE IF EXISTS history_fts; | |||
| 41 | + | ||||
| 42 | + | CREATE TABLE history_008_down ( | |||
| 43 | + | id INTEGER PRIMARY KEY AUTOINCREMENT, | |||
| 44 | + | url TEXT NOT NULL UNIQUE, | |||
| 45 | + | title TEXT, | |||
| 46 | + | favicon_url TEXT, | |||
| 47 | + | visit_count INTEGER DEFAULT 1, | |||
| 48 | + | last_visited DATETIME DEFAULT CURRENT_TIMESTAMP, | |||
| 49 | + | created_at DATETIME DEFAULT CURRENT_TIMESTAMP | |||
| 50 | + | ); | |||
| 51 | + | ||||
| 52 | + | INSERT INTO history_008_down (id, url, title, favicon_url, visit_count, last_visited, created_at) | |||
| 53 | + | SELECT id, url, title, favicon_url, visit_count, last_visited, created_at | |||
| 54 | + | FROM history; | |||
| 55 | + | ||||
| 56 | + | DROP TABLE history; | |||
| 57 | + | ALTER TABLE history_008_down RENAME TO history; | |||
| 58 | + | ||||
| 59 | + | CREATE INDEX IF NOT EXISTS idx_history_url ON history(url); | |||
| 60 | + | CREATE INDEX IF NOT EXISTS idx_history_last_visited ON history(last_visited); | |||
| 61 | + | CREATE INDEX IF NOT EXISTS idx_history_visit_count_last_visited ON history(visit_count DESC, last_visited DESC); | |||
| 62 | + | ||||
| 63 | + | CREATE VIRTUAL TABLE IF NOT EXISTS history_fts USING fts5( | |||
| 64 | + | url, | |||
| 65 | + | title, | |||
| 66 | + | content='history', | |||
| 67 | + | content_rowid='id' | |||
| 68 | + | ); | |||
| 69 | + | ||||
| 70 | + | -- +goose StatementBegin | |||
| 71 | + | CREATE TRIGGER IF NOT EXISTS history_fts_insert AFTER INSERT ON history BEGIN | |||
| 72 | + | INSERT INTO history_fts(rowid, url, title) VALUES (NEW.id, NEW.url, NEW.title); | |||
| 73 | + | END; | |||
| 74 | + | -- +goose StatementEnd | |||
| 75 | + | ||||
| 76 | + | -- +goose StatementBegin | |||
| 77 | + | CREATE TRIGGER IF NOT EXISTS history_fts_delete AFTER DELETE ON history BEGIN | |||
| 78 | + | INSERT INTO history_fts(history_fts, rowid, url, title) VALUES('delete', OLD.id, OLD.url, OLD.title); | |||
| 79 | + | END; | |||
| 80 | + | -- +goose StatementEnd | |||
| 81 | + | ||||
| 82 | + | -- +goose StatementBegin | |||
| 83 | + | CREATE TRIGGER IF NOT EXISTS history_fts_update AFTER UPDATE ON history BEGIN | |||
| 84 | + | INSERT INTO history_fts(history_fts, rowid, url, title) VALUES('delete', OLD.id, OLD.url, OLD.title); | |||
| 85 | + | INSERT INTO history_fts(rowid, url, title) VALUES (NEW.id, NEW.url, NEW.title); | |||
| 86 | + | END; | |||
| 87 | + | -- +goose StatementEnd | |||
| 88 | + | ||||
| 89 | + | INSERT INTO history_fts(rowid, url, title) | |||
| 90 | + | SELECT id, url, title FROM history; | |||
M internal/infrastructure/persistence/sqlite/queries/history.sql +31 -6
| @@ -19,7 +19,7 @@ DO UPDATE SET | |||
| 19 | 19 | last_visited = CURRENT_TIMESTAMP, | |
| 20 | 20 | title = COALESCE(EXCLUDED.title, history.title), | |
| 21 | 21 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | |
| 22 | - | domain = EXCLUDED.domain; | |
| 22 | + | domain = COALESCE(EXCLUDED.domain, history.domain); | |
| 23 | 23 | ||
| 24 | 24 | -- name: IncrementVisitCount :exec | |
| 25 | 25 | UPDATE history | |
| @@ -50,22 +50,47 @@ DELETE FROM history WHERE last_visited >= ?; | |||
| 50 | 50 | DELETE FROM history; | |
| 51 | 51 | ||
| 52 | 52 | -- name: GetHistoryStats :one | |
| 53 | - | SELECT COUNT(*) as total_entries, COALESCE(SUM(visit_count), 0) as total_visits, COUNT(DISTINCT date(last_visited)) as unique_days FROM history; | |
| 53 | + | SELECT | |
| 54 | + | COUNT(*) as total_entries, | |
| 55 | + | COALESCE(SUM(visit_count), 0) as total_visits, | |
| 56 | + | COUNT(DISTINCT date(last_visited)) as unique_days | |
| 57 | + | FROM history; | |
| 54 | 58 | ||
| 55 | 59 | -- name: GetDomainStats :many | |
| 56 | - | SELECT COALESCE(domain, '') as domain, COUNT(*) as page_count, SUM(visit_count) as total_visits, MAX(last_visited) as last_visit FROM history WHERE domain IS NOT NULL AND domain != '' GROUP BY domain ORDER BY total_visits DESC LIMIT ?; | |
| 60 | + | SELECT | |
| 61 | + | COALESCE(domain, '') as domain, | |
| 62 | + | COUNT(*) as page_count, | |
| 63 | + | SUM(visit_count) as total_visits, | |
| 64 | + | MAX(last_visited) as last_visit | |
| 65 | + | FROM history | |
| 66 | + | WHERE domain IS NOT NULL AND domain != '' | |
| 67 | + | GROUP BY domain | |
| 68 | + | ORDER BY total_visits DESC | |
| 69 | + | LIMIT ?; | |
| 57 | 70 | ||
| 58 | 71 | -- name: GetHourlyDistribution :many | |
| 59 | - | SELECT CAST(strftime('%H', last_visited) AS INTEGER) as hour, COUNT(*) as visit_count FROM history GROUP BY hour ORDER BY hour; | |
| 72 | + | SELECT | |
| 73 | + | CAST(strftime('%H', last_visited) AS INTEGER) as hour, | |
| 74 | + | COUNT(*) as visit_count | |
| 75 | + | FROM history | |
| 76 | + | GROUP BY hour | |
| 77 | + | ORDER BY hour; | |
| 60 | 78 | ||
| 61 | 79 | -- name: GetDailyVisitCount :many | |
| 62 | - | SELECT date(last_visited) as day, COUNT(*) as entries, SUM(visit_count) as visits FROM history WHERE last_visited >= date('now', ?) GROUP BY day ORDER BY day ASC; | |
| 80 | + | SELECT | |
| 81 | + | date(last_visited) as day, | |
| 82 | + | COUNT(*) as entries, | |
| 83 | + | SUM(visit_count) as visits | |
| 84 | + | FROM history | |
| 85 | + | WHERE last_visited >= date('now', ?) | |
| 86 | + | GROUP BY day | |
| 87 | + | ORDER BY day ASC; | |
| 63 | 88 | ||
| 64 | 89 | -- name: DeleteHistoryByDomain :exec | |
| 65 | 90 | DELETE FROM history WHERE domain = @domain; | |
| 66 | 91 | ||
| 67 | 92 | -- name: SearchHistoryFTSUrl :many | |
| 68 | - | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at | |
| 93 | + | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at, h.domain | |
| 69 | 94 | FROM history_fts fts | |
| 70 | 95 | JOIN history h ON fts.rowid = h.id | |
| 71 | 96 | WHERE fts.url MATCH @query | |
| @@ -19,7 +19,7 @@ DO UPDATE SET | |||||
| 19 | last_visited = CURRENT_TIMESTAMP, | 19 | last_visited = CURRENT_TIMESTAMP, | ||
| 20 | title = COALESCE(EXCLUDED.title, history.title), | 20 | title = COALESCE(EXCLUDED.title, history.title), | ||
| 21 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | 21 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | ||
| 22 | - | domain = EXCLUDED.domain; | 22 | + | domain = COALESCE(EXCLUDED.domain, history.domain); |
| 23 | 23 | ||||
| 24 | -- name: IncrementVisitCount :exec | 24 | -- name: IncrementVisitCount :exec | ||
| 25 | UPDATE history | 25 | UPDATE history | ||
| @@ -50,22 +50,47 @@ DELETE FROM history WHERE last_visited >= ?; | |||||
| 50 | DELETE FROM history; | 50 | DELETE FROM history; | ||
| 51 | 51 | ||||
| 52 | -- name: GetHistoryStats :one | 52 | -- name: GetHistoryStats :one | ||
| 53 | - | SELECT COUNT(*) as total_entries, COALESCE(SUM(visit_count), 0) as total_visits, COUNT(DISTINCT date(last_visited)) as unique_days FROM history; | 53 | + | SELECT |
| 54 | + | COUNT(*) as total_entries, | |||
| 55 | + | COALESCE(SUM(visit_count), 0) as total_visits, | |||
| 56 | + | COUNT(DISTINCT date(last_visited)) as unique_days | |||
| 57 | + | FROM history; | |||
| 54 | 58 | ||||
| 55 | -- name: GetDomainStats :many | 59 | -- name: GetDomainStats :many | ||
| 56 | - | SELECT COALESCE(domain, '') as domain, COUNT(*) as page_count, SUM(visit_count) as total_visits, MAX(last_visited) as last_visit FROM history WHERE domain IS NOT NULL AND domain != '' GROUP BY domain ORDER BY total_visits DESC LIMIT ?; | 60 | + | SELECT |
| 61 | + | COALESCE(domain, '') as domain, | |||
| 62 | + | COUNT(*) as page_count, | |||
| 63 | + | SUM(visit_count) as total_visits, | |||
| 64 | + | MAX(last_visited) as last_visit | |||
| 65 | + | FROM history | |||
| 66 | + | WHERE domain IS NOT NULL AND domain != '' | |||
| 67 | + | GROUP BY domain | |||
| 68 | + | ORDER BY total_visits DESC | |||
| 69 | + | LIMIT ?; | |||
| 57 | 70 | ||||
| 58 | -- name: GetHourlyDistribution :many | 71 | -- name: GetHourlyDistribution :many | ||
| 59 | - | SELECT CAST(strftime('%H', last_visited) AS INTEGER) as hour, COUNT(*) as visit_count FROM history GROUP BY hour ORDER BY hour; | 72 | + | SELECT |
| 73 | + | CAST(strftime('%H', last_visited) AS INTEGER) as hour, | |||
| 74 | + | COUNT(*) as visit_count | |||
| 75 | + | FROM history | |||
| 76 | + | GROUP BY hour | |||
| 77 | + | ORDER BY hour; | |||
| 60 | 78 | ||||
| 61 | -- name: GetDailyVisitCount :many | 79 | -- name: GetDailyVisitCount :many | ||
| 62 | - | SELECT date(last_visited) as day, COUNT(*) as entries, SUM(visit_count) as visits FROM history WHERE last_visited >= date('now', ?) GROUP BY day ORDER BY day ASC; | 80 | + | SELECT |
| 81 | + | date(last_visited) as day, | |||
| 82 | + | COUNT(*) as entries, | |||
| 83 | + | SUM(visit_count) as visits | |||
| 84 | + | FROM history | |||
| 85 | + | WHERE last_visited >= date('now', ?) | |||
| 86 | + | GROUP BY day | |||
| 87 | + | ORDER BY day ASC; | |||
| 63 | 88 | ||||
| 64 | -- name: DeleteHistoryByDomain :exec | 89 | -- name: DeleteHistoryByDomain :exec | ||
| 65 | DELETE FROM history WHERE domain = @domain; | 90 | DELETE FROM history WHERE domain = @domain; | ||
| 66 | 91 | ||||
| 67 | -- name: SearchHistoryFTSUrl :many | 92 | -- name: SearchHistoryFTSUrl :many | ||
| 68 | - | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at | 93 | + | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at, h.domain |
| 69 | FROM history_fts fts | 94 | FROM history_fts fts | ||
| 70 | JOIN history h ON fts.rowid = h.id | 95 | JOIN history h ON fts.rowid = h.id | ||
| 71 | WHERE fts.url MATCH @query | 96 | WHERE fts.url MATCH @query | ||
M internal/infrastructure/persistence/sqlite/sqlc/history.sql.go +35 -19
| @@ -145,7 +145,14 @@ func (q *Queries) GetAllRecentHistory(ctx context.Context) ([]History, error) { | |||
| 145 | 145 | } | |
| 146 | 146 | ||
| 147 | 147 | const GetDailyVisitCount = `-- name: GetDailyVisitCount :many | |
| 148 | - | SELECT date(last_visited) as day, COUNT(*) as entries, SUM(visit_count) as visits FROM history WHERE last_visited >= date('now', ?) GROUP BY day ORDER BY day ASC | |
| 148 | + | SELECT | |
| 149 | + | date(last_visited) as day, | |
| 150 | + | COUNT(*) as entries, | |
| 151 | + | SUM(visit_count) as visits | |
| 152 | + | FROM history | |
| 153 | + | WHERE last_visited >= date('now', ?) | |
| 154 | + | GROUP BY day | |
| 155 | + | ORDER BY day ASC | |
| 149 | 156 | ` | |
| 150 | 157 | ||
| 151 | 158 | type GetDailyVisitCountRow struct { | |
| @@ -178,7 +185,16 @@ func (q *Queries) GetDailyVisitCount(ctx context.Context, date interface{}) ([]G | |||
| 178 | 185 | } | |
| 179 | 186 | ||
| 180 | 187 | const GetDomainStats = `-- name: GetDomainStats :many | |
| 181 | - | SELECT COALESCE(domain, '') as domain, COUNT(*) as page_count, SUM(visit_count) as total_visits, MAX(last_visited) as last_visit FROM history WHERE domain IS NOT NULL AND domain != '' GROUP BY domain ORDER BY total_visits DESC LIMIT ? | |
| 188 | + | SELECT | |
| 189 | + | COALESCE(domain, '') as domain, | |
| 190 | + | COUNT(*) as page_count, | |
| 191 | + | SUM(visit_count) as total_visits, | |
| 192 | + | MAX(last_visited) as last_visit | |
| 193 | + | FROM history | |
| 194 | + | WHERE domain IS NOT NULL AND domain != '' | |
| 195 | + | GROUP BY domain | |
| 196 | + | ORDER BY total_visits DESC | |
| 197 | + | LIMIT ? | |
| 182 | 198 | ` | |
| 183 | 199 | ||
| 184 | 200 | type GetDomainStatsRow struct { | |
| @@ -237,7 +253,11 @@ func (q *Queries) GetHistoryByURL(ctx context.Context, url string) (History, err | |||
| 237 | 253 | } | |
| 238 | 254 | ||
| 239 | 255 | const GetHistoryStats = `-- name: GetHistoryStats :one | |
| 240 | - | SELECT COUNT(*) as total_entries, COALESCE(SUM(visit_count), 0) as total_visits, COUNT(DISTINCT date(last_visited)) as unique_days FROM history | |
| 256 | + | SELECT | |
| 257 | + | COUNT(*) as total_entries, | |
| 258 | + | COALESCE(SUM(visit_count), 0) as total_visits, | |
| 259 | + | COUNT(DISTINCT date(last_visited)) as unique_days | |
| 260 | + | FROM history | |
| 241 | 261 | ` | |
| 242 | 262 | ||
| 243 | 263 | type GetHistoryStatsRow struct { | |
| @@ -254,7 +274,12 @@ func (q *Queries) GetHistoryStats(ctx context.Context) (GetHistoryStatsRow, erro | |||
| 254 | 274 | } | |
| 255 | 275 | ||
| 256 | 276 | const GetHourlyDistribution = `-- name: GetHourlyDistribution :many | |
| 257 | - | SELECT CAST(strftime('%H', last_visited) AS INTEGER) as hour, COUNT(*) as visit_count FROM history GROUP BY hour ORDER BY hour | |
| 277 | + | SELECT | |
| 278 | + | CAST(strftime('%H', last_visited) AS INTEGER) as hour, | |
| 279 | + | COUNT(*) as visit_count | |
| 280 | + | FROM history | |
| 281 | + | GROUP BY hour | |
| 282 | + | ORDER BY hour | |
| 258 | 283 | ` | |
| 259 | 284 | ||
| 260 | 285 | type GetHourlyDistributionRow struct { | |
| @@ -566,7 +591,7 @@ func (q *Queries) SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFT | |||
| 566 | 591 | } | |
| 567 | 592 | ||
| 568 | 593 | const SearchHistoryFTSUrl = `-- name: SearchHistoryFTSUrl :many | |
| 569 | - | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at | |
| 594 | + | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at, h.domain | |
| 570 | 595 | FROM history_fts fts | |
| 571 | 596 | JOIN history h ON fts.rowid = h.id | |
| 572 | 597 | WHERE fts.url MATCH ?1 | |
| @@ -579,25 +604,15 @@ type SearchHistoryFTSUrlParams struct { | |||
| 579 | 604 | Limit int64 `json:"limit"` | |
| 580 | 605 | } | |
| 581 | 606 | ||
| 582 | - | type SearchHistoryFTSUrlRow struct { | |
| 583 | - | ID int64 `json:"id"` | |
| 584 | - | Url string `json:"url"` | |
| 585 | - | Title sql.NullString `json:"title"` | |
| 586 | - | FaviconUrl sql.NullString `json:"favicon_url"` | |
| 587 | - | VisitCount sql.NullInt64 `json:"visit_count"` | |
| 588 | - | LastVisited sql.NullTime `json:"last_visited"` | |
| 589 | - | CreatedAt sql.NullTime `json:"created_at"` | |
| 590 | - | } | |
| 591 | - | ||
| 592 | - | func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]SearchHistoryFTSUrlRow, error) { | |
| 607 | + | func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error) { | |
| 593 | 608 | rows, err := q.db.QueryContext(ctx, SearchHistoryFTSUrl, arg.Query, arg.Limit) | |
| 594 | 609 | if err != nil { | |
| 595 | 610 | return nil, err | |
| 596 | 611 | } | |
| 597 | 612 | defer rows.Close() | |
| 598 | - | items := []SearchHistoryFTSUrlRow{} | |
| 613 | + | items := []History{} | |
| 599 | 614 | for rows.Next() { | |
| 600 | - | var i SearchHistoryFTSUrlRow | |
| 615 | + | var i History | |
| 601 | 616 | if err := rows.Scan( | |
| 602 | 617 | &i.ID, | |
| 603 | 618 | &i.Url, | |
| @@ -606,6 +621,7 @@ func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSU | |||
| 606 | 621 | &i.VisitCount, | |
| 607 | 622 | &i.LastVisited, | |
| 608 | 623 | &i.CreatedAt, | |
| 624 | + | &i.Domain, | |
| 609 | 625 | ); err != nil { | |
| 610 | 626 | return nil, err | |
| 611 | 627 | } | |
| @@ -694,7 +710,7 @@ DO UPDATE SET | |||
| 694 | 710 | last_visited = CURRENT_TIMESTAMP, | |
| 695 | 711 | title = COALESCE(EXCLUDED.title, history.title), | |
| 696 | 712 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | |
| 697 | - | domain = EXCLUDED.domain | |
| 713 | + | domain = COALESCE(EXCLUDED.domain, history.domain) | |
| 698 | 714 | ` | |
| 699 | 715 | ||
| 700 | 716 | type UpsertHistoryParams struct { | |
| @@ -145,7 +145,14 @@ func (q *Queries) GetAllRecentHistory(ctx context.Context) ([]History, error) { | |||||
| 145 | } | 145 | } | ||
| 146 | 146 | ||||
| 147 | const GetDailyVisitCount = `-- name: GetDailyVisitCount :many | 147 | const GetDailyVisitCount = `-- name: GetDailyVisitCount :many | ||
| 148 | - | SELECT date(last_visited) as day, COUNT(*) as entries, SUM(visit_count) as visits FROM history WHERE last_visited >= date('now', ?) GROUP BY day ORDER BY day ASC | 148 | + | SELECT |
| 149 | + | date(last_visited) as day, | |||
| 150 | + | COUNT(*) as entries, | |||
| 151 | + | SUM(visit_count) as visits | |||
| 152 | + | FROM history | |||
| 153 | + | WHERE last_visited >= date('now', ?) | |||
| 154 | + | GROUP BY day | |||
| 155 | + | ORDER BY day ASC | |||
| 149 | ` | 156 | ` | ||
| 150 | 157 | ||||
| 151 | type GetDailyVisitCountRow struct { | 158 | type GetDailyVisitCountRow struct { | ||
| @@ -178,7 +185,16 @@ func (q *Queries) GetDailyVisitCount(ctx context.Context, date interface{}) ([]G | |||||
| 178 | } | 185 | } | ||
| 179 | 186 | ||||
| 180 | const GetDomainStats = `-- name: GetDomainStats :many | 187 | const GetDomainStats = `-- name: GetDomainStats :many | ||
| 181 | - | SELECT COALESCE(domain, '') as domain, COUNT(*) as page_count, SUM(visit_count) as total_visits, MAX(last_visited) as last_visit FROM history WHERE domain IS NOT NULL AND domain != '' GROUP BY domain ORDER BY total_visits DESC LIMIT ? | 188 | + | SELECT |
| 189 | + | COALESCE(domain, '') as domain, | |||
| 190 | + | COUNT(*) as page_count, | |||
| 191 | + | SUM(visit_count) as total_visits, | |||
| 192 | + | MAX(last_visited) as last_visit | |||
| 193 | + | FROM history | |||
| 194 | + | WHERE domain IS NOT NULL AND domain != '' | |||
| 195 | + | GROUP BY domain | |||
| 196 | + | ORDER BY total_visits DESC | |||
| 197 | + | LIMIT ? | |||
| 182 | ` | 198 | ` | ||
| 183 | 199 | ||||
| 184 | type GetDomainStatsRow struct { | 200 | type GetDomainStatsRow struct { | ||
| @@ -237,7 +253,11 @@ func (q *Queries) GetHistoryByURL(ctx context.Context, url string) (History, err | |||||
| 237 | } | 253 | } | ||
| 238 | 254 | ||||
| 239 | const GetHistoryStats = `-- name: GetHistoryStats :one | 255 | const GetHistoryStats = `-- name: GetHistoryStats :one | ||
| 240 | - | SELECT COUNT(*) as total_entries, COALESCE(SUM(visit_count), 0) as total_visits, COUNT(DISTINCT date(last_visited)) as unique_days FROM history | 256 | + | SELECT |
| 257 | + | COUNT(*) as total_entries, | |||
| 258 | + | COALESCE(SUM(visit_count), 0) as total_visits, | |||
| 259 | + | COUNT(DISTINCT date(last_visited)) as unique_days | |||
| 260 | + | FROM history | |||
| 241 | ` | 261 | ` | ||
| 242 | 262 | ||||
| 243 | type GetHistoryStatsRow struct { | 263 | type GetHistoryStatsRow struct { | ||
| @@ -254,7 +274,12 @@ func (q *Queries) GetHistoryStats(ctx context.Context) (GetHistoryStatsRow, erro | |||||
| 254 | } | 274 | } | ||
| 255 | 275 | ||||
| 256 | const GetHourlyDistribution = `-- name: GetHourlyDistribution :many | 276 | const GetHourlyDistribution = `-- name: GetHourlyDistribution :many | ||
| 257 | - | SELECT CAST(strftime('%H', last_visited) AS INTEGER) as hour, COUNT(*) as visit_count FROM history GROUP BY hour ORDER BY hour | 277 | + | SELECT |
| 278 | + | CAST(strftime('%H', last_visited) AS INTEGER) as hour, | |||
| 279 | + | COUNT(*) as visit_count | |||
| 280 | + | FROM history | |||
| 281 | + | GROUP BY hour | |||
| 282 | + | ORDER BY hour | |||
| 258 | ` | 283 | ` | ||
| 259 | 284 | ||||
| 260 | type GetHourlyDistributionRow struct { | 285 | type GetHourlyDistributionRow struct { | ||
| @@ -566,7 +591,7 @@ func (q *Queries) SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFT | |||||
| 566 | } | 591 | } | ||
| 567 | 592 | ||||
| 568 | const SearchHistoryFTSUrl = `-- name: SearchHistoryFTSUrl :many | 593 | const SearchHistoryFTSUrl = `-- name: SearchHistoryFTSUrl :many | ||
| 569 | - | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at | 594 | + | SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at, h.domain |
| 570 | FROM history_fts fts | 595 | FROM history_fts fts | ||
| 571 | JOIN history h ON fts.rowid = h.id | 596 | JOIN history h ON fts.rowid = h.id | ||
| 572 | WHERE fts.url MATCH ?1 | 597 | WHERE fts.url MATCH ?1 | ||
| @@ -579,25 +604,15 @@ type SearchHistoryFTSUrlParams struct { | |||||
| 579 | Limit int64 `json:"limit"` | 604 | Limit int64 `json:"limit"` | ||
| 580 | } | 605 | } | ||
| 581 | 606 | ||||
| 582 | - | type SearchHistoryFTSUrlRow struct { | 607 | + | func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error) { |
| 583 | - | ID int64 `json:"id"` | |||
| 584 | - | Url string `json:"url"` | |||
| 585 | - | Title sql.NullString `json:"title"` | |||
| 586 | - | FaviconUrl sql.NullString `json:"favicon_url"` | |||
| 587 | - | VisitCount sql.NullInt64 `json:"visit_count"` | |||
| 588 | - | LastVisited sql.NullTime `json:"last_visited"` | |||
| 589 | - | CreatedAt sql.NullTime `json:"created_at"` | |||
| 590 | - | } | |||
| 591 | - | ||||
| 592 | - | func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]SearchHistoryFTSUrlRow, error) { | |||
| 593 | rows, err := q.db.QueryContext(ctx, SearchHistoryFTSUrl, arg.Query, arg.Limit) | 608 | rows, err := q.db.QueryContext(ctx, SearchHistoryFTSUrl, arg.Query, arg.Limit) | ||
| 594 | if err != nil { | 609 | if err != nil { | ||
| 595 | return nil, err | 610 | return nil, err | ||
| 596 | } | 611 | } | ||
| 597 | defer rows.Close() | 612 | defer rows.Close() | ||
| 598 | - | items := []SearchHistoryFTSUrlRow{} | 613 | + | items := []History{} |
| 599 | for rows.Next() { | 614 | for rows.Next() { | ||
| 600 | - | var i SearchHistoryFTSUrlRow | 615 | + | var i History |
| 601 | if err := rows.Scan( | 616 | if err := rows.Scan( | ||
| 602 | &i.ID, | 617 | &i.ID, | ||
| 603 | &i.Url, | 618 | &i.Url, | ||
| @@ -606,6 +621,7 @@ func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSU | |||||
| 606 | &i.VisitCount, | 621 | &i.VisitCount, | ||
| 607 | &i.LastVisited, | 622 | &i.LastVisited, | ||
| 608 | &i.CreatedAt, | 623 | &i.CreatedAt, | ||
| 624 | + | &i.Domain, | |||
| 609 | ); err != nil { | 625 | ); err != nil { | ||
| 610 | return nil, err | 626 | return nil, err | ||
| 611 | } | 627 | } | ||
| @@ -694,7 +710,7 @@ DO UPDATE SET | |||||
| 694 | last_visited = CURRENT_TIMESTAMP, | 710 | last_visited = CURRENT_TIMESTAMP, | ||
| 695 | title = COALESCE(EXCLUDED.title, history.title), | 711 | title = COALESCE(EXCLUDED.title, history.title), | ||
| 696 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | 712 | favicon_url = COALESCE(EXCLUDED.favicon_url, history.favicon_url), | ||
| 697 | - | domain = EXCLUDED.domain | 713 | + | domain = COALESCE(EXCLUDED.domain, history.domain) |
| 698 | ` | 714 | ` | ||
| 699 | 715 | ||||
| 700 | type UpsertHistoryParams struct { | 716 | type UpsertHistoryParams struct { | ||
M internal/infrastructure/persistence/sqlite/sqlc/querier.go +1 -1
| @@ -79,7 +79,7 @@ type Querier interface { | |||
| 79 | 79 | RemoveTagFromFavorite(ctx context.Context, arg RemoveTagFromFavoriteParams) error | |
| 80 | 80 | SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error) | |
| 81 | 81 | SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error) | |
| 82 | - | SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]SearchHistoryFTSUrlRow, error) | |
| 82 | + | SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error) | |
| 83 | 83 | SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error) | |
| 84 | 84 | SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error | |
| 85 | 85 | SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error | |
| @@ -79,7 +79,7 @@ type Querier interface { | |||||
| 79 | RemoveTagFromFavorite(ctx context.Context, arg RemoveTagFromFavoriteParams) error | 79 | RemoveTagFromFavorite(ctx context.Context, arg RemoveTagFromFavoriteParams) error | ||
| 80 | SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error) | 80 | SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error) | ||
| 81 | SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error) | 81 | SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error) | ||
| 82 | - | SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]SearchHistoryFTSUrlRow, error) | 82 | + | SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error) |
| 83 | SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error) | 83 | SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error) | ||
| 84 | SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error | 84 | SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error | ||
| 85 | SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error | 85 | SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error | ||
M internal/infrastructure/systemviewsbridge/client.go +7 -8
| @@ -9,6 +9,7 @@ import ( | |||
| 9 | 9 | "sync/atomic" | |
| 10 | 10 | "time" | |
| 11 | 11 | ||
| 12 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 12 | 13 | "github.com/bnema/dumber/internal/application/port" | |
| 13 | 14 | "github.com/bnema/dumber/internal/domain/entity" | |
| 14 | 15 | ) | |
| @@ -143,21 +144,21 @@ func (c *Client) DeleteRange(ctx context.Context, rangeID string) error { | |||
| 143 | 144 | ||
| 144 | 145 | // Current intentionally targets the direct HTTP config endpoint rather than a | |
| 145 | 146 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config. | |
| 146 | - | func (c *Client) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 147 | - | return request[port.SystemviewConfigPayload](c, ctx, "/api/config", struct { | |
| 147 | + | func (c *Client) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 148 | + | return request[dto.SystemviewConfigPayload](c, ctx, "/api/config", struct { | |
| 148 | 149 | RequestID string `json:"requestId"` | |
| 149 | 150 | }{RequestID: nextRequestID()}) | |
| 150 | 151 | } | |
| 151 | 152 | ||
| 152 | 153 | // Default intentionally targets the direct HTTP config endpoint rather than a | |
| 153 | 154 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config/default. | |
| 154 | - | func (c *Client) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | |
| 155 | - | return request[port.SystemviewConfigPayload](c, ctx, "/api/config/default", struct { | |
| 155 | + | func (c *Client) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { | |
| 156 | + | return request[dto.SystemviewConfigPayload](c, ctx, "/api/config/default", struct { | |
| 156 | 157 | RequestID string `json:"requestId"` | |
| 157 | 158 | }{RequestID: nextRequestID()}) | |
| 158 | 159 | } | |
| 159 | 160 | ||
| 160 | - | func (c *Client) Save(ctx context.Context, cfg port.WebUIConfig) error { | |
| 161 | + | func (c *Client) Save(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 161 | 162 | _, err := request[struct{}](c, ctx, "save_config", cfg) | |
| 162 | 163 | return err | |
| 163 | 164 | } | |
| @@ -482,10 +483,8 @@ func request[T any](c *Client, ctx context.Context, msgType string, payload any) | |||
| 482 | 483 | return decodeBridgeResponse[T](raw) | |
| 483 | 484 | } | |
| 484 | 485 | ||
| 486 | + | // withBridgeRequestTimeout applies the bridge request timeout; ctx must be non-nil. | |
| 485 | 487 | func (c *Client) withBridgeRequestTimeout(ctx context.Context) (context.Context, context.CancelFunc) { | |
| 486 | - | if ctx == nil { | |
| 487 | - | ctx = context.Background() | |
| 488 | - | } | |
| 489 | 488 | timeout := defaultBridgeRequestTimeout | |
| 490 | 489 | if c != nil { | |
| 491 | 490 | timeout = c.requestTimeout | |
| @@ -9,6 +9,7 @@ import ( | |||||
| 9 | "sync/atomic" | 9 | "sync/atomic" | ||
| 10 | "time" | 10 | "time" | ||
| 11 | 11 | ||||
| 12 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 12 | "github.com/bnema/dumber/internal/application/port" | 13 | "github.com/bnema/dumber/internal/application/port" | ||
| 13 | "github.com/bnema/dumber/internal/domain/entity" | 14 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 14 | ) | 15 | ) | ||
| @@ -143,21 +144,21 @@ func (c *Client) DeleteRange(ctx context.Context, rangeID string) error { | |||||
| 143 | 144 | ||||
| 144 | // Current intentionally targets the direct HTTP config endpoint rather than a | 145 | // Current intentionally targets the direct HTTP config endpoint rather than a | ||
| 145 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config. | 146 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config. | ||
| 146 | - | func (c *Client) Current(ctx context.Context) (port.SystemviewConfigPayload, error) { | 147 | + | func (c *Client) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 147 | - | return request[port.SystemviewConfigPayload](c, ctx, "/api/config", struct { | 148 | + | return request[dto.SystemviewConfigPayload](c, ctx, "/api/config", struct { |
| 148 | RequestID string `json:"requestId"` | 149 | RequestID string `json:"requestId"` | ||
| 149 | }{RequestID: nextRequestID()}) | 150 | }{RequestID: nextRequestID()}) | ||
| 150 | } | 151 | } | ||
| 151 | 152 | ||||
| 152 | // Default intentionally targets the direct HTTP config endpoint rather than a | 153 | // Default intentionally targets the direct HTTP config endpoint rather than a | ||
| 153 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config/default. | 154 | // snake_case bridge message so CEF/WebKit fetch shims can reuse /api/config/default. | ||
| 154 | - | func (c *Client) Default(ctx context.Context) (port.SystemviewConfigPayload, error) { | 155 | + | func (c *Client) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) { |
| 155 | - | return request[port.SystemviewConfigPayload](c, ctx, "/api/config/default", struct { | 156 | + | return request[dto.SystemviewConfigPayload](c, ctx, "/api/config/default", struct { |
| 156 | RequestID string `json:"requestId"` | 157 | RequestID string `json:"requestId"` | ||
| 157 | }{RequestID: nextRequestID()}) | 158 | }{RequestID: nextRequestID()}) | ||
| 158 | } | 159 | } | ||
| 159 | 160 | ||||
| 160 | - | func (c *Client) Save(ctx context.Context, cfg port.WebUIConfig) error { | 161 | + | func (c *Client) Save(ctx context.Context, cfg dto.WebUIConfig) error { |
| 161 | _, err := request[struct{}](c, ctx, "save_config", cfg) | 162 | _, err := request[struct{}](c, ctx, "save_config", cfg) | ||
| 162 | return err | 163 | return err | ||
| 163 | } | 164 | } | ||
| @@ -482,10 +483,8 @@ func request[T any](c *Client, ctx context.Context, msgType string, payload any) | |||||
| 482 | return decodeBridgeResponse[T](raw) | 483 | return decodeBridgeResponse[T](raw) | ||
| 483 | } | 484 | } | ||
| 484 | 485 | ||||
| 486 | + | // withBridgeRequestTimeout applies the bridge request timeout; ctx must be non-nil. | |||
| 485 | func (c *Client) withBridgeRequestTimeout(ctx context.Context) (context.Context, context.CancelFunc) { | 487 | func (c *Client) withBridgeRequestTimeout(ctx context.Context) (context.Context, context.CancelFunc) { | ||
| 486 | - | if ctx == nil { | |||
| 487 | - | ctx = context.Background() | |||
| 488 | - | } | |||
| 489 | timeout := defaultBridgeRequestTimeout | 488 | timeout := defaultBridgeRequestTimeout | ||
| 490 | if c != nil { | 489 | if c != nil { | ||
| 491 | timeout = c.requestTimeout | 490 | timeout = c.requestTimeout | ||
M internal/infrastructure/systemviewsbridge/client_test.go +3 -2
| @@ -7,6 +7,7 @@ import ( | |||
| 7 | 7 | "testing" | |
| 8 | 8 | "time" | |
| 9 | 9 | ||
| 10 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 10 | 11 | "github.com/bnema/dumber/internal/application/port" | |
| 11 | 12 | ) | |
| 12 | 13 | ||
| @@ -289,10 +290,10 @@ func TestClientConfigActionsUseExpectedMessageTypes(t *testing.T) { | |||
| 289 | 290 | { | |
| 290 | 291 | name: "save config", | |
| 291 | 292 | call: func(client *Client) error { | |
| 292 | - | return client.Save(context.Background(), port.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}) | |
| 293 | + | return client.Save(context.Background(), dto.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}) | |
| 293 | 294 | }, | |
| 294 | 295 | want: "save_config", | |
| 295 | - | payload: port.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}, | |
| 296 | + | payload: dto.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}, | |
| 296 | 297 | }, | |
| 297 | 298 | { | |
| 298 | 299 | name: "get keybindings", | |
| @@ -7,6 +7,7 @@ import ( | |||||
| 7 | "testing" | 7 | "testing" | ||
| 8 | "time" | 8 | "time" | ||
| 9 | 9 | ||||
| 10 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 10 | "github.com/bnema/dumber/internal/application/port" | 11 | "github.com/bnema/dumber/internal/application/port" | ||
| 11 | ) | 12 | ) | ||
| 12 | 13 | ||||
| @@ -289,10 +290,10 @@ func TestClientConfigActionsUseExpectedMessageTypes(t *testing.T) { | |||||
| 289 | { | 290 | { | ||
| 290 | name: "save config", | 291 | name: "save config", | ||
| 291 | call: func(client *Client) error { | 292 | call: func(client *Client) error { | ||
| 292 | - | return client.Save(context.Background(), port.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}) | 293 | + | return client.Save(context.Background(), dto.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}) |
| 293 | }, | 294 | }, | ||
| 294 | want: "save_config", | 295 | want: "save_config", | ||
| 295 | - | payload: port.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}, | 296 | + | payload: dto.WebUIConfig{DefaultSearchEngine: "DuckDuckGo"}, |
| 296 | }, | 297 | }, | ||
| 297 | { | 298 | { | ||
| 298 | name: "get keybindings", | 299 | name: "get keybindings", | ||
M internal/infrastructure/webkit/scheme_handler.go +15 -7
| @@ -355,20 +355,27 @@ func safeSystemviewsAssetPath(assetDir, relPath string) (fullPath, cleanRelPath | |||
| 355 | 355 | } | |
| 356 | 356 | ||
| 357 | 357 | func readAssetWithEncoding(assets embed.FS, fullPath, relPath string) ([]byte, map[string]string, error) { | |
| 358 | + | var compressedErr error | |
| 358 | 359 | if strings.HasSuffix(relPath, ".wasm") { | |
| 359 | 360 | if compressed, err := fs.ReadFile(assets, fullPath+".br"); err == nil { | |
| 360 | 361 | data, err := io.ReadAll(io.LimitReader(brotli.NewReader(bytes.NewReader(compressed)), maxSystemviewsWASMBytes+1)) | |
| 361 | 362 | if err != nil { | |
| 362 | - | return nil, nil, err | |
| 363 | + | compressedErr = err | |
| 364 | + | } else if len(data) > maxSystemviewsWASMBytes { | |
| 365 | + | compressedErr = fmt.Errorf("decompressed asset %s exceeds %d bytes", fullPath, maxSystemviewsWASMBytes) | |
| 366 | + | } else { | |
| 367 | + | return data, nil, nil | |
| 363 | 368 | } | |
| 364 | - | if len(data) > maxSystemviewsWASMBytes { | |
| 365 | - | return nil, nil, fmt.Errorf("decompressed asset %s exceeds %d bytes", fullPath, maxSystemviewsWASMBytes) | |
| 366 | - | } | |
| 367 | - | return data, nil, nil | |
| 368 | 369 | } | |
| 369 | 370 | } | |
| 370 | 371 | data, err := fs.ReadFile(assets, fullPath) | |
| 371 | - | return data, nil, err | |
| 372 | + | if err == nil { | |
| 373 | + | return data, nil, nil | |
| 374 | + | } | |
| 375 | + | if compressedErr != nil { | |
| 376 | + | return nil, nil, compressedErr | |
| 377 | + | } | |
| 378 | + | return nil, nil, err | |
| 372 | 379 | } | |
| 373 | 380 | ||
| 374 | 381 | func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | |
| @@ -384,6 +391,7 @@ func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | |||
| 384 | 391 | FavoritesPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | |
| 385 | 392 | ConfigPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | |
| 386 | 393 | ErrorPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | |
| 394 | + | CrashPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | |
| 387 | 395 | } | |
| 388 | 396 | ||
| 389 | 397 | if root, ok := rootByHost[u.Host]; ok { | |
| @@ -395,7 +403,7 @@ func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | |||
| 395 | 403 | } | |
| 396 | 404 | ||
| 397 | 405 | switch u.Opaque { | |
| 398 | - | case HistoryPath, FavoritesPath, ConfigPath, ErrorPath: | |
| 406 | + | case HistoryPath, FavoritesPath, ConfigPath, ErrorPath, CrashPath: | |
| 399 | 407 | return systemviewsAssetDir, IndexHTML, true | |
| 400 | 408 | default: | |
| 401 | 409 | return "", "", false | |
| @@ -355,20 +355,27 @@ func safeSystemviewsAssetPath(assetDir, relPath string) (fullPath, cleanRelPath | |||||
| 355 | } | 355 | } | ||
| 356 | 356 | ||||
| 357 | func readAssetWithEncoding(assets embed.FS, fullPath, relPath string) ([]byte, map[string]string, error) { | 357 | func readAssetWithEncoding(assets embed.FS, fullPath, relPath string) ([]byte, map[string]string, error) { | ||
| 358 | + | var compressedErr error | |||
| 358 | if strings.HasSuffix(relPath, ".wasm") { | 359 | if strings.HasSuffix(relPath, ".wasm") { | ||
| 359 | if compressed, err := fs.ReadFile(assets, fullPath+".br"); err == nil { | 360 | if compressed, err := fs.ReadFile(assets, fullPath+".br"); err == nil { | ||
| 360 | data, err := io.ReadAll(io.LimitReader(brotli.NewReader(bytes.NewReader(compressed)), maxSystemviewsWASMBytes+1)) | 361 | data, err := io.ReadAll(io.LimitReader(brotli.NewReader(bytes.NewReader(compressed)), maxSystemviewsWASMBytes+1)) | ||
| 361 | if err != nil { | 362 | if err != nil { | ||
| 362 | - | return nil, nil, err | 363 | + | compressedErr = err |
| 364 | + | } else if len(data) > maxSystemviewsWASMBytes { | |||
| 365 | + | compressedErr = fmt.Errorf("decompressed asset %s exceeds %d bytes", fullPath, maxSystemviewsWASMBytes) | |||
| 366 | + | } else { | |||
| 367 | + | return data, nil, nil | |||
| 363 | } | 368 | } | ||
| 364 | - | if len(data) > maxSystemviewsWASMBytes { | |||
| 365 | - | return nil, nil, fmt.Errorf("decompressed asset %s exceeds %d bytes", fullPath, maxSystemviewsWASMBytes) | |||
| 366 | - | } | |||
| 367 | - | return data, nil, nil | |||
| 368 | } | 369 | } | ||
| 369 | } | 370 | } | ||
| 370 | data, err := fs.ReadFile(assets, fullPath) | 371 | data, err := fs.ReadFile(assets, fullPath) | ||
| 371 | - | return data, nil, err | 372 | + | if err == nil { |
| 373 | + | return data, nil, nil | |||
| 374 | + | } | |||
| 375 | + | if compressedErr != nil { | |||
| 376 | + | return nil, nil, compressedErr | |||
| 377 | + | } | |||
| 378 | + | return nil, nil, err | |||
| 372 | } | 379 | } | ||
| 373 | 380 | ||||
| 374 | func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | 381 | func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | ||
| @@ -384,6 +391,7 @@ func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | |||||
| 384 | FavoritesPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | 391 | FavoritesPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | ||
| 385 | ConfigPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | 392 | ConfigPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | ||
| 386 | ErrorPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | 393 | ErrorPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | ||
| 394 | + | CrashPath: {assetDir: systemviewsAssetDir, file: IndexHTML}, | |||
| 387 | } | 395 | } | ||
| 388 | 396 | ||||
| 389 | if root, ok := rootByHost[u.Host]; ok { | 397 | if root, ok := rootByHost[u.Host]; ok { | ||
| @@ -395,7 +403,7 @@ func resolveAssetPath(u *url.URL) (assetDir, relPath string, ok bool) { | |||||
| 395 | } | 403 | } | ||
| 396 | 404 | ||||
| 397 | switch u.Opaque { | 405 | switch u.Opaque { | ||
| 398 | - | case HistoryPath, FavoritesPath, ConfigPath, ErrorPath: | 406 | + | case HistoryPath, FavoritesPath, ConfigPath, ErrorPath, CrashPath: |
| 399 | return systemviewsAssetDir, IndexHTML, true | 407 | return systemviewsAssetDir, IndexHTML, true | ||
| 400 | default: | 408 | default: | ||
| 401 | return "", "", false | 409 | return "", "", false | ||
M internal/ui/app_clipboard_test.go +3 -2
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | 9 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 9 | 10 | "github.com/bnema/dumber/internal/domain/entity" | |
| @@ -28,7 +29,7 @@ func TestNew_DoesNotWireClipboardOrchestratorWhenClipboardMissing(t *testing.T) | |||
| 28 | 29 | Config: &config.Config{}, | |
| 29 | 30 | Engine: engine, | |
| 30 | 31 | HandlerDeps: port.HandlerDeps{ | |
| 31 | - | SaveConfig: func(context.Context, port.WebUIConfig) error { return nil }, | |
| 32 | + | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | |
| 32 | 33 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | |
| 33 | 34 | }, | |
| 34 | 35 | Clipboard: nil, | |
| @@ -57,7 +58,7 @@ func TestNew_WiresClipboardOrchestratorWhenClipboardPresent(t *testing.T) { | |||
| 57 | 58 | Config: &config.Config{}, | |
| 58 | 59 | Engine: engine, | |
| 59 | 60 | HandlerDeps: port.HandlerDeps{ | |
| 60 | - | SaveConfig: func(context.Context, port.WebUIConfig) error { return nil }, | |
| 61 | + | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | |
| 61 | 62 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | |
| 62 | 63 | }, | |
| 63 | 64 | Clipboard: clipboard, | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "testing" | 5 | "testing" | ||
| 6 | 6 | ||||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 7 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | 9 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | ||
| 9 | "github.com/bnema/dumber/internal/domain/entity" | 10 | "github.com/bnema/dumber/internal/domain/entity" | ||
| @@ -28,7 +29,7 @@ func TestNew_DoesNotWireClipboardOrchestratorWhenClipboardMissing(t *testing.T) | |||||
| 28 | Config: &config.Config{}, | 29 | Config: &config.Config{}, | ||
| 29 | Engine: engine, | 30 | Engine: engine, | ||
| 30 | HandlerDeps: port.HandlerDeps{ | 31 | HandlerDeps: port.HandlerDeps{ | ||
| 31 | - | SaveConfig: func(context.Context, port.WebUIConfig) error { return nil }, | 32 | + | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, |
| 32 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | 33 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | ||
| 33 | }, | 34 | }, | ||
| 34 | Clipboard: nil, | 35 | Clipboard: nil, | ||
| @@ -57,7 +58,7 @@ func TestNew_WiresClipboardOrchestratorWhenClipboardPresent(t *testing.T) { | |||||
| 57 | Config: &config.Config{}, | 58 | Config: &config.Config{}, | ||
| 58 | Engine: engine, | 59 | Engine: engine, | ||
| 59 | HandlerDeps: port.HandlerDeps{ | 60 | HandlerDeps: port.HandlerDeps{ | ||
| 60 | - | SaveConfig: func(context.Context, port.WebUIConfig) error { return nil }, | 61 | + | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, |
| 61 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | 62 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | ||
| 62 | }, | 63 | }, | ||
| 63 | Clipboard: clipboard, | 64 | Clipboard: clipboard, | ||
M internal/ui/coordinator/workspace.go +21 -2
| @@ -4,6 +4,8 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | 6 | "fmt" | |
| 7 | + | "net/url" | |
| 8 | + | "strings" | |
| 7 | 9 | ||
| 8 | 10 | "github.com/bnema/dumber/internal/application/usecase" | |
| 9 | 11 | "github.com/bnema/dumber/internal/domain/entity" | |
| @@ -261,21 +263,38 @@ func (c *WorkspaceCoordinator) ToggleSystemViewRight(ctx context.Context, target | |||
| 261 | 263 | } | |
| 262 | 264 | ||
| 263 | 265 | func paneMatchesURL(node *entity.PaneNode, targetURL string) bool { | |
| 264 | - | return node != nil && node.Pane != nil && domainurl.Normalize(node.Pane.URI) == targetURL | |
| 266 | + | return node != nil && node.Pane != nil && comparablePaneURL(node.Pane.URI) == comparablePaneURL(targetURL) | |
| 265 | 267 | } | |
| 266 | 268 | ||
| 267 | 269 | func findPaneByURL(ws *entity.Workspace, targetURL string) *entity.Pane { | |
| 268 | 270 | if ws == nil { | |
| 269 | 271 | return nil | |
| 270 | 272 | } | |
| 273 | + | targetURL = comparablePaneURL(targetURL) | |
| 271 | 274 | for _, pane := range ws.AllPanes() { | |
| 272 | - | if pane != nil && domainurl.Normalize(pane.URI) == targetURL { | |
| 275 | + | if pane != nil && comparablePaneURL(pane.URI) == targetURL { | |
| 273 | 276 | return pane | |
| 274 | 277 | } | |
| 275 | 278 | } | |
| 276 | 279 | return nil | |
| 277 | 280 | } | |
| 278 | 281 | ||
| 282 | + | func comparablePaneURL(raw string) string { | |
| 283 | + | normalized := domainurl.Normalize(raw) | |
| 284 | + | parsed, err := url.Parse(normalized) | |
| 285 | + | if err != nil || !strings.EqualFold(parsed.Scheme, "dumb") { | |
| 286 | + | return normalized | |
| 287 | + | } | |
| 288 | + | page := parsed.Host | |
| 289 | + | if page == "" { | |
| 290 | + | page = parsed.Opaque | |
| 291 | + | } | |
| 292 | + | if page == "" || parsed.Path != "" || parsed.RawQuery != "" || parsed.Fragment != "" { | |
| 293 | + | return normalized | |
| 294 | + | } | |
| 295 | + | return "dumb:" + page | |
| 296 | + | } | |
| 297 | + | ||
| 279 | 298 | func (c *WorkspaceCoordinator) focusExistingPane( | |
| 280 | 299 | ctx context.Context, | |
| 281 | 300 | ws *entity.Workspace, | |
| @@ -4,6 +4,8 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | "fmt" | 6 | "fmt" | ||
| 7 | + | "net/url" | |||
| 8 | + | "strings" | |||
| 7 | 9 | ||||
| 8 | "github.com/bnema/dumber/internal/application/usecase" | 10 | "github.com/bnema/dumber/internal/application/usecase" | ||
| 9 | "github.com/bnema/dumber/internal/domain/entity" | 11 | "github.com/bnema/dumber/internal/domain/entity" | ||
| @@ -261,21 +263,38 @@ func (c *WorkspaceCoordinator) ToggleSystemViewRight(ctx context.Context, target | |||||
| 261 | } | 263 | } | ||
| 262 | 264 | ||||
| 263 | func paneMatchesURL(node *entity.PaneNode, targetURL string) bool { | 265 | func paneMatchesURL(node *entity.PaneNode, targetURL string) bool { | ||
| 264 | - | return node != nil && node.Pane != nil && domainurl.Normalize(node.Pane.URI) == targetURL | 266 | + | return node != nil && node.Pane != nil && comparablePaneURL(node.Pane.URI) == comparablePaneURL(targetURL) |
| 265 | } | 267 | } | ||
| 266 | 268 | ||||
| 267 | func findPaneByURL(ws *entity.Workspace, targetURL string) *entity.Pane { | 269 | func findPaneByURL(ws *entity.Workspace, targetURL string) *entity.Pane { | ||
| 268 | if ws == nil { | 270 | if ws == nil { | ||
| 269 | return nil | 271 | return nil | ||
| 270 | } | 272 | } | ||
| 273 | + | targetURL = comparablePaneURL(targetURL) | |||
| 271 | for _, pane := range ws.AllPanes() { | 274 | for _, pane := range ws.AllPanes() { | ||
| 272 | - | if pane != nil && domainurl.Normalize(pane.URI) == targetURL { | 275 | + | if pane != nil && comparablePaneURL(pane.URI) == targetURL { |
| 273 | return pane | 276 | return pane | ||
| 274 | } | 277 | } | ||
| 275 | } | 278 | } | ||
| 276 | return nil | 279 | return nil | ||
| 277 | } | 280 | } | ||
| 278 | 281 | ||||
| 282 | + | func comparablePaneURL(raw string) string { | |||
| 283 | + | normalized := domainurl.Normalize(raw) | |||
| 284 | + | parsed, err := url.Parse(normalized) | |||
| 285 | + | if err != nil || !strings.EqualFold(parsed.Scheme, "dumb") { | |||
| 286 | + | return normalized | |||
| 287 | + | } | |||
| 288 | + | page := parsed.Host | |||
| 289 | + | if page == "" { | |||
| 290 | + | page = parsed.Opaque | |||
| 291 | + | } | |||
| 292 | + | if page == "" || parsed.Path != "" || parsed.RawQuery != "" || parsed.Fragment != "" { | |||
| 293 | + | return normalized | |||
| 294 | + | } | |||
| 295 | + | return "dumb:" + page | |||
| 296 | + | } | |||
| 297 | + | ||||
| 279 | func (c *WorkspaceCoordinator) focusExistingPane( | 298 | func (c *WorkspaceCoordinator) focusExistingPane( | ||
| 280 | ctx context.Context, | 299 | ctx context.Context, | ||
| 281 | ws *entity.Workspace, | 300 | ws *entity.Workspace, | ||
M internal/ui/systemviews/app.go +2 -1
| @@ -7,6 +7,7 @@ import ( | |||
| 7 | 7 | "strings" | |
| 8 | 8 | "sync" | |
| 9 | 9 | ||
| 10 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 10 | 11 | "github.com/bnema/dumber/internal/application/port" | |
| 11 | 12 | "github.com/bnema/dumber/internal/domain/entity" | |
| 12 | 13 | ) | |
| @@ -42,7 +43,7 @@ type App struct { | |||
| 42 | 43 | favoriteTagFilter *entity.TagID | |
| 43 | 44 | favoritesNotice string | |
| 44 | 45 | favoritesError string | |
| 45 | - | config *port.SystemviewConfigPayload | |
| 46 | + | config *dto.SystemviewConfigPayload | |
| 46 | 47 | keybindings port.KeybindingsConfig | |
| 47 | 48 | configNotice string | |
| 48 | 49 | configError string | |
| @@ -7,6 +7,7 @@ import ( | |||||
| 7 | "strings" | 7 | "strings" | ||
| 8 | "sync" | 8 | "sync" | ||
| 9 | 9 | ||||
| 10 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 10 | "github.com/bnema/dumber/internal/application/port" | 11 | "github.com/bnema/dumber/internal/application/port" | ||
| 11 | "github.com/bnema/dumber/internal/domain/entity" | 12 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 12 | ) | 13 | ) | ||
| @@ -42,7 +43,7 @@ type App struct { | |||||
| 42 | favoriteTagFilter *entity.TagID | 43 | favoriteTagFilter *entity.TagID | ||
| 43 | favoritesNotice string | 44 | favoritesNotice string | ||
| 44 | favoritesError string | 45 | favoritesError string | ||
| 45 | - | config *port.SystemviewConfigPayload | 46 | + | config *dto.SystemviewConfigPayload |
| 46 | keybindings port.KeybindingsConfig | 47 | keybindings port.KeybindingsConfig | ||
| 47 | configNotice string | 48 | configNotice string | ||
| 48 | configError string | 49 | configError string | ||
M internal/ui/systemviews/app_test.go +30 -29
| @@ -5,6 +5,7 @@ import ( | |||
| 5 | 5 | "errors" | |
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 9 | "github.com/bnema/dumber/internal/application/port" | |
| 9 | 10 | "github.com/bnema/dumber/internal/domain/entity" | |
| 10 | 11 | "github.com/stretchr/testify/assert" | |
| @@ -245,10 +246,10 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||
| 245 | 246 | URL: "https://example.com", | |
| 246 | 247 | Title: "Example", | |
| 247 | 248 | }}} | |
| 248 | - | config := &fakeConfigService{current: port.SystemviewConfigPayload{ | |
| 249 | - | Appearance: port.WebUIAppearanceConfig{ | |
| 249 | + | config := &fakeConfigService{current: dto.SystemviewConfigPayload{ | |
| 250 | + | Appearance: dto.WebUIAppearanceConfig{ | |
| 250 | 251 | ColorScheme: "prefer-light", | |
| 251 | - | LightPalette: port.ColorPalette{ | |
| 252 | + | LightPalette: dto.ColorPalette{ | |
| 252 | 253 | Background: "#ffffff", | |
| 253 | 254 | Surface: "#fafafa", | |
| 254 | 255 | SurfaceVariant: "#eeeeee", | |
| @@ -257,7 +258,7 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||
| 257 | 258 | Accent: "#0055ff", | |
| 258 | 259 | Border: "#dddddd", | |
| 259 | 260 | }, | |
| 260 | - | DarkPalette: port.ColorPalette{ | |
| 261 | + | DarkPalette: dto.ColorPalette{ | |
| 261 | 262 | Background: "#111111", | |
| 262 | 263 | Surface: "#1a1a1a", | |
| 263 | 264 | SurfaceVariant: "#2a2a2a", | |
| @@ -425,15 +426,15 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) { | |||
| 425 | 426 | func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |
| 426 | 427 | t.Parallel() | |
| 427 | 428 | ||
| 428 | - | config := port.SystemviewConfigPayload{ | |
| 429 | + | config := dto.SystemviewConfigPayload{ | |
| 429 | 430 | EngineType: "webkit", | |
| 430 | - | Appearance: port.WebUIAppearanceConfig{ | |
| 431 | + | Appearance: dto.WebUIAppearanceConfig{ | |
| 431 | 432 | ColorScheme: "prefer-dark", | |
| 432 | 433 | SansFont: "Inter", | |
| 433 | 434 | SerifFont: "Georgia", | |
| 434 | 435 | MonospaceFont: "JetBrains Mono", | |
| 435 | 436 | DefaultFontSize: 16, | |
| 436 | - | LightPalette: port.ColorPalette{ | |
| 437 | + | LightPalette: dto.ColorPalette{ | |
| 437 | 438 | Background: "#ffffff", | |
| 438 | 439 | Surface: "#fafafa", | |
| 439 | 440 | SurfaceVariant: "#eeeeee", | |
| @@ -442,7 +443,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||
| 442 | 443 | Accent: "#0055ff", | |
| 443 | 444 | Border: "#dddddd", | |
| 444 | 445 | }, | |
| 445 | - | DarkPalette: port.ColorPalette{ | |
| 446 | + | DarkPalette: dto.ColorPalette{ | |
| 446 | 447 | Background: "#111111", | |
| 447 | 448 | Surface: "#1a1a1a", | |
| 448 | 449 | SurfaceVariant: "#2a2a2a", | |
| @@ -452,16 +453,16 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||
| 452 | 453 | Border: "#333333", | |
| 453 | 454 | }, | |
| 454 | 455 | }, | |
| 455 | - | Performance: port.SystemviewPerformancePayload{ | |
| 456 | + | Performance: dto.SystemviewPerformancePayload{ | |
| 456 | 457 | Profile: "balanced", | |
| 457 | - | Custom: port.SystemviewCustomPerformancePayload{ | |
| 458 | + | Custom: dto.SystemviewCustomPerformancePayload{ | |
| 458 | 459 | SkiaCPUThreads: 4, | |
| 459 | 460 | SkiaGPUThreads: 2, | |
| 460 | 461 | WebProcessMemoryMB: 512, | |
| 461 | 462 | NetworkProcessMemoryMB: 128, | |
| 462 | 463 | WebViewPoolPrewarm: 1, | |
| 463 | 464 | }, | |
| 464 | - | Hardware: port.SystemviewHardwarePayload{ | |
| 465 | + | Hardware: dto.SystemviewHardwarePayload{ | |
| 465 | 466 | CPUCores: 8, | |
| 466 | 467 | CPUThreads: 16, | |
| 467 | 468 | TotalRAMMB: 32768, | |
| @@ -471,7 +472,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||
| 471 | 472 | }, | |
| 472 | 473 | }, | |
| 473 | 474 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | |
| 474 | - | SearchShortcuts: map[string]port.SearchShortcut{ | |
| 475 | + | SearchShortcuts: map[string]dto.SearchShortcut{ | |
| 475 | 476 | "ddg": { | |
| 476 | 477 | URL: "https://duckduckgo.com/?q=%s", | |
| 477 | 478 | Description: "DuckDuckGo", | |
| @@ -685,32 +686,32 @@ func TestAppRejectsSearchURLsWithoutPlaceholder(t *testing.T) { | |||
| 685 | 686 | assert.False(t, service.calledSave) | |
| 686 | 687 | } | |
| 687 | 688 | ||
| 688 | - | func testConfigPayload() port.SystemviewConfigPayload { | |
| 689 | - | return port.SystemviewConfigPayload{ | |
| 689 | + | func testConfigPayload() dto.SystemviewConfigPayload { | |
| 690 | + | return dto.SystemviewConfigPayload{ | |
| 690 | 691 | EngineType: "webkit", | |
| 691 | - | Appearance: port.WebUIAppearanceConfig{ | |
| 692 | + | Appearance: dto.WebUIAppearanceConfig{ | |
| 692 | 693 | ColorScheme: "prefer-dark", | |
| 693 | 694 | SansFont: "Inter", | |
| 694 | 695 | SerifFont: "Georgia", | |
| 695 | 696 | MonospaceFont: "JetBrains Mono", | |
| 696 | 697 | DefaultFontSize: 16, | |
| 697 | - | LightPalette: port.ColorPalette{Background: "#ffffff", Surface: "#fafafa", SurfaceVariant: "#eeeeee", Text: "#111111", Muted: "#666666", Accent: "#0055ff", Border: "#dddddd"}, | |
| 698 | - | DarkPalette: port.ColorPalette{Background: "#111111", Surface: "#1a1a1a", SurfaceVariant: "#2a2a2a", Text: "#f5f5f5", Muted: "#a0a0a0", Accent: "#66aaff", Border: "#333333"}, | |
| 698 | + | LightPalette: dto.ColorPalette{Background: "#ffffff", Surface: "#fafafa", SurfaceVariant: "#eeeeee", Text: "#111111", Muted: "#666666", Accent: "#0055ff", Border: "#dddddd"}, | |
| 699 | + | DarkPalette: dto.ColorPalette{Background: "#111111", Surface: "#1a1a1a", SurfaceVariant: "#2a2a2a", Text: "#f5f5f5", Muted: "#a0a0a0", Accent: "#66aaff", Border: "#333333"}, | |
| 699 | 700 | }, | |
| 700 | 701 | DefaultUIScale: 1, | |
| 701 | 702 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | |
| 702 | - | SearchShortcuts: map[string]port.SearchShortcut{ | |
| 703 | + | SearchShortcuts: map[string]dto.SearchShortcut{ | |
| 703 | 704 | "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: "DuckDuckGo"}, | |
| 704 | 705 | }, | |
| 705 | - | Performance: port.SystemviewPerformancePayload{ | |
| 706 | + | Performance: dto.SystemviewPerformancePayload{ | |
| 706 | 707 | Profile: "default", | |
| 707 | - | Custom: port.SystemviewCustomPerformancePayload{SkiaCPUThreads: 2, SkiaGPUThreads: 1, WebProcessMemoryMB: 1024, NetworkProcessMemoryMB: 256, WebViewPoolPrewarm: 2}, | |
| 708 | - | Hardware: port.SystemviewHardwarePayload{CPUCores: 4, CPUThreads: 8, TotalRAMMB: 16384, GPUVendor: "AMD", GPUName: "Radeon", VRAMMB: 4096}, | |
| 708 | + | Custom: dto.SystemviewCustomPerformancePayload{SkiaCPUThreads: 2, SkiaGPUThreads: 1, WebProcessMemoryMB: 1024, NetworkProcessMemoryMB: 256, WebViewPoolPrewarm: 2}, | |
| 709 | + | Hardware: dto.SystemviewHardwarePayload{CPUCores: 4, CPUThreads: 8, TotalRAMMB: 16384, GPUVendor: "AMD", GPUName: "Radeon", VRAMMB: 4096}, | |
| 709 | 710 | }, | |
| 710 | 711 | } | |
| 711 | 712 | } | |
| 712 | 713 | ||
| 713 | - | func testDefaultConfigPayload() port.SystemviewConfigPayload { | |
| 714 | + | func testDefaultConfigPayload() dto.SystemviewConfigPayload { | |
| 714 | 715 | cfg := testConfigPayload() | |
| 715 | 716 | cfg.Appearance.DefaultFontSize = 14 | |
| 716 | 717 | cfg.DefaultUIScale = 1 | |
| @@ -934,26 +935,26 @@ type fakeConfigService struct { | |||
| 934 | 935 | calledReset bool | |
| 935 | 936 | calledResetAll bool | |
| 936 | 937 | ||
| 937 | - | current port.SystemviewConfigPayload | |
| 938 | - | defaultCfg port.SystemviewConfigPayload | |
| 938 | + | current dto.SystemviewConfigPayload | |
| 939 | + | defaultCfg dto.SystemviewConfigPayload | |
| 939 | 940 | keybindings port.KeybindingsConfig | |
| 940 | - | savedConfig port.WebUIConfig | |
| 941 | + | savedConfig dto.WebUIConfig | |
| 941 | 942 | setReq port.SetKeybindingRequest | |
| 942 | 943 | setResp port.SetKeybindingResponse | |
| 943 | 944 | resetReq port.ResetKeybindingRequest | |
| 944 | 945 | } | |
| 945 | 946 | ||
| 946 | - | func (s *fakeConfigService) Current(context.Context) (port.SystemviewConfigPayload, error) { | |
| 947 | + | func (s *fakeConfigService) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 947 | 948 | s.calledCurrent = true | |
| 948 | 949 | return s.current, nil | |
| 949 | 950 | } | |
| 950 | 951 | ||
| 951 | - | func (s *fakeConfigService) Default(context.Context) (port.SystemviewConfigPayload, error) { | |
| 952 | + | func (s *fakeConfigService) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 952 | 953 | s.calledDefault = true | |
| 953 | 954 | return s.defaultCfg, nil | |
| 954 | 955 | } | |
| 955 | 956 | ||
| 956 | - | func (s *fakeConfigService) Save(_ context.Context, cfg port.WebUIConfig) error { | |
| 957 | + | func (s *fakeConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { | |
| 957 | 958 | s.calledSave = true | |
| 958 | 959 | s.savedConfig = cfg | |
| 959 | 960 | s.current.Appearance = cfg.Appearance | |
| @@ -961,7 +962,7 @@ func (s *fakeConfigService) Save(_ context.Context, cfg port.WebUIConfig) error | |||
| 961 | 962 | s.current.DefaultSearchEngine = cfg.DefaultSearchEngine | |
| 962 | 963 | s.current.SearchShortcuts = cfg.SearchShortcuts | |
| 963 | 964 | s.current.Performance.Profile = cfg.Performance.Profile | |
| 964 | - | s.current.Performance.Custom = port.SystemviewCustomPerformancePayload{ | |
| 965 | + | s.current.Performance.Custom = dto.SystemviewCustomPerformancePayload{ | |
| 965 | 966 | SkiaCPUThreads: cfg.Performance.Custom.SkiaCPUThreads, | |
| 966 | 967 | SkiaGPUThreads: cfg.Performance.Custom.SkiaGPUThreads, | |
| 967 | 968 | WebProcessMemoryMB: cfg.Performance.Custom.WebProcessMemoryMB, | |
| @@ -5,6 +5,7 @@ import ( | |||||
| 5 | "errors" | 5 | "errors" | ||
| 6 | "testing" | 6 | "testing" | ||
| 7 | 7 | ||||
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 8 | "github.com/bnema/dumber/internal/application/port" | 9 | "github.com/bnema/dumber/internal/application/port" | ||
| 9 | "github.com/bnema/dumber/internal/domain/entity" | 10 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 10 | "github.com/stretchr/testify/assert" | 11 | "github.com/stretchr/testify/assert" | ||
| @@ -245,10 +246,10 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||||
| 245 | URL: "https://example.com", | 246 | URL: "https://example.com", | ||
| 246 | Title: "Example", | 247 | Title: "Example", | ||
| 247 | }}} | 248 | }}} | ||
| 248 | - | config := &fakeConfigService{current: port.SystemviewConfigPayload{ | 249 | + | config := &fakeConfigService{current: dto.SystemviewConfigPayload{ |
| 249 | - | Appearance: port.WebUIAppearanceConfig{ | 250 | + | Appearance: dto.WebUIAppearanceConfig{ |
| 250 | ColorScheme: "prefer-light", | 251 | ColorScheme: "prefer-light", | ||
| 251 | - | LightPalette: port.ColorPalette{ | 252 | + | LightPalette: dto.ColorPalette{ |
| 252 | Background: "#ffffff", | 253 | Background: "#ffffff", | ||
| 253 | Surface: "#fafafa", | 254 | Surface: "#fafafa", | ||
| 254 | SurfaceVariant: "#eeeeee", | 255 | SurfaceVariant: "#eeeeee", | ||
| @@ -257,7 +258,7 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||||
| 257 | Accent: "#0055ff", | 258 | Accent: "#0055ff", | ||
| 258 | Border: "#dddddd", | 259 | Border: "#dddddd", | ||
| 259 | }, | 260 | }, | ||
| 260 | - | DarkPalette: port.ColorPalette{ | 261 | + | DarkPalette: dto.ColorPalette{ |
| 261 | Background: "#111111", | 262 | Background: "#111111", | ||
| 262 | Surface: "#1a1a1a", | 263 | Surface: "#1a1a1a", | ||
| 263 | SurfaceVariant: "#2a2a2a", | 264 | SurfaceVariant: "#2a2a2a", | ||
| @@ -425,15 +426,15 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) { | |||||
| 425 | func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | 426 | func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | ||
| 426 | t.Parallel() | 427 | t.Parallel() | ||
| 427 | 428 | ||||
| 428 | - | config := port.SystemviewConfigPayload{ | 429 | + | config := dto.SystemviewConfigPayload{ |
| 429 | EngineType: "webkit", | 430 | EngineType: "webkit", | ||
| 430 | - | Appearance: port.WebUIAppearanceConfig{ | 431 | + | Appearance: dto.WebUIAppearanceConfig{ |
| 431 | ColorScheme: "prefer-dark", | 432 | ColorScheme: "prefer-dark", | ||
| 432 | SansFont: "Inter", | 433 | SansFont: "Inter", | ||
| 433 | SerifFont: "Georgia", | 434 | SerifFont: "Georgia", | ||
| 434 | MonospaceFont: "JetBrains Mono", | 435 | MonospaceFont: "JetBrains Mono", | ||
| 435 | DefaultFontSize: 16, | 436 | DefaultFontSize: 16, | ||
| 436 | - | LightPalette: port.ColorPalette{ | 437 | + | LightPalette: dto.ColorPalette{ |
| 437 | Background: "#ffffff", | 438 | Background: "#ffffff", | ||
| 438 | Surface: "#fafafa", | 439 | Surface: "#fafafa", | ||
| 439 | SurfaceVariant: "#eeeeee", | 440 | SurfaceVariant: "#eeeeee", | ||
| @@ -442,7 +443,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||||
| 442 | Accent: "#0055ff", | 443 | Accent: "#0055ff", | ||
| 443 | Border: "#dddddd", | 444 | Border: "#dddddd", | ||
| 444 | }, | 445 | }, | ||
| 445 | - | DarkPalette: port.ColorPalette{ | 446 | + | DarkPalette: dto.ColorPalette{ |
| 446 | Background: "#111111", | 447 | Background: "#111111", | ||
| 447 | Surface: "#1a1a1a", | 448 | Surface: "#1a1a1a", | ||
| 448 | SurfaceVariant: "#2a2a2a", | 449 | SurfaceVariant: "#2a2a2a", | ||
| @@ -452,16 +453,16 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||||
| 452 | Border: "#333333", | 453 | Border: "#333333", | ||
| 453 | }, | 454 | }, | ||
| 454 | }, | 455 | }, | ||
| 455 | - | Performance: port.SystemviewPerformancePayload{ | 456 | + | Performance: dto.SystemviewPerformancePayload{ |
| 456 | Profile: "balanced", | 457 | Profile: "balanced", | ||
| 457 | - | Custom: port.SystemviewCustomPerformancePayload{ | 458 | + | Custom: dto.SystemviewCustomPerformancePayload{ |
| 458 | SkiaCPUThreads: 4, | 459 | SkiaCPUThreads: 4, | ||
| 459 | SkiaGPUThreads: 2, | 460 | SkiaGPUThreads: 2, | ||
| 460 | WebProcessMemoryMB: 512, | 461 | WebProcessMemoryMB: 512, | ||
| 461 | NetworkProcessMemoryMB: 128, | 462 | NetworkProcessMemoryMB: 128, | ||
| 462 | WebViewPoolPrewarm: 1, | 463 | WebViewPoolPrewarm: 1, | ||
| 463 | }, | 464 | }, | ||
| 464 | - | Hardware: port.SystemviewHardwarePayload{ | 465 | + | Hardware: dto.SystemviewHardwarePayload{ |
| 465 | CPUCores: 8, | 466 | CPUCores: 8, | ||
| 466 | CPUThreads: 16, | 467 | CPUThreads: 16, | ||
| 467 | TotalRAMMB: 32768, | 468 | TotalRAMMB: 32768, | ||
| @@ -471,7 +472,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||||
| 471 | }, | 472 | }, | ||
| 472 | }, | 473 | }, | ||
| 473 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | 474 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | ||
| 474 | - | SearchShortcuts: map[string]port.SearchShortcut{ | 475 | + | SearchShortcuts: map[string]dto.SearchShortcut{ |
| 475 | "ddg": { | 476 | "ddg": { | ||
| 476 | URL: "https://duckduckgo.com/?q=%s", | 477 | URL: "https://duckduckgo.com/?q=%s", | ||
| 477 | Description: "DuckDuckGo", | 478 | Description: "DuckDuckGo", | ||
| @@ -685,32 +686,32 @@ func TestAppRejectsSearchURLsWithoutPlaceholder(t *testing.T) { | |||||
| 685 | assert.False(t, service.calledSave) | 686 | assert.False(t, service.calledSave) | ||
| 686 | } | 687 | } | ||
| 687 | 688 | ||||
| 688 | - | func testConfigPayload() port.SystemviewConfigPayload { | 689 | + | func testConfigPayload() dto.SystemviewConfigPayload { |
| 689 | - | return port.SystemviewConfigPayload{ | 690 | + | return dto.SystemviewConfigPayload{ |
| 690 | EngineType: "webkit", | 691 | EngineType: "webkit", | ||
| 691 | - | Appearance: port.WebUIAppearanceConfig{ | 692 | + | Appearance: dto.WebUIAppearanceConfig{ |
| 692 | ColorScheme: "prefer-dark", | 693 | ColorScheme: "prefer-dark", | ||
| 693 | SansFont: "Inter", | 694 | SansFont: "Inter", | ||
| 694 | SerifFont: "Georgia", | 695 | SerifFont: "Georgia", | ||
| 695 | MonospaceFont: "JetBrains Mono", | 696 | MonospaceFont: "JetBrains Mono", | ||
| 696 | DefaultFontSize: 16, | 697 | DefaultFontSize: 16, | ||
| 697 | - | LightPalette: port.ColorPalette{Background: "#ffffff", Surface: "#fafafa", SurfaceVariant: "#eeeeee", Text: "#111111", Muted: "#666666", Accent: "#0055ff", Border: "#dddddd"}, | 698 | + | LightPalette: dto.ColorPalette{Background: "#ffffff", Surface: "#fafafa", SurfaceVariant: "#eeeeee", Text: "#111111", Muted: "#666666", Accent: "#0055ff", Border: "#dddddd"}, |
| 698 | - | DarkPalette: port.ColorPalette{Background: "#111111", Surface: "#1a1a1a", SurfaceVariant: "#2a2a2a", Text: "#f5f5f5", Muted: "#a0a0a0", Accent: "#66aaff", Border: "#333333"}, | 699 | + | DarkPalette: dto.ColorPalette{Background: "#111111", Surface: "#1a1a1a", SurfaceVariant: "#2a2a2a", Text: "#f5f5f5", Muted: "#a0a0a0", Accent: "#66aaff", Border: "#333333"}, |
| 699 | }, | 700 | }, | ||
| 700 | DefaultUIScale: 1, | 701 | DefaultUIScale: 1, | ||
| 701 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | 702 | DefaultSearchEngine: "https://duckduckgo.com/?q=%s", | ||
| 702 | - | SearchShortcuts: map[string]port.SearchShortcut{ | 703 | + | SearchShortcuts: map[string]dto.SearchShortcut{ |
| 703 | "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: "DuckDuckGo"}, | 704 | "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: "DuckDuckGo"}, | ||
| 704 | }, | 705 | }, | ||
| 705 | - | Performance: port.SystemviewPerformancePayload{ | 706 | + | Performance: dto.SystemviewPerformancePayload{ |
| 706 | Profile: "default", | 707 | Profile: "default", | ||
| 707 | - | Custom: port.SystemviewCustomPerformancePayload{SkiaCPUThreads: 2, SkiaGPUThreads: 1, WebProcessMemoryMB: 1024, NetworkProcessMemoryMB: 256, WebViewPoolPrewarm: 2}, | 708 | + | Custom: dto.SystemviewCustomPerformancePayload{SkiaCPUThreads: 2, SkiaGPUThreads: 1, WebProcessMemoryMB: 1024, NetworkProcessMemoryMB: 256, WebViewPoolPrewarm: 2}, |
| 708 | - | Hardware: port.SystemviewHardwarePayload{CPUCores: 4, CPUThreads: 8, TotalRAMMB: 16384, GPUVendor: "AMD", GPUName: "Radeon", VRAMMB: 4096}, | 709 | + | Hardware: dto.SystemviewHardwarePayload{CPUCores: 4, CPUThreads: 8, TotalRAMMB: 16384, GPUVendor: "AMD", GPUName: "Radeon", VRAMMB: 4096}, |
| 709 | }, | 710 | }, | ||
| 710 | } | 711 | } | ||
| 711 | } | 712 | } | ||
| 712 | 713 | ||||
| 713 | - | func testDefaultConfigPayload() port.SystemviewConfigPayload { | 714 | + | func testDefaultConfigPayload() dto.SystemviewConfigPayload { |
| 714 | cfg := testConfigPayload() | 715 | cfg := testConfigPayload() | ||
| 715 | cfg.Appearance.DefaultFontSize = 14 | 716 | cfg.Appearance.DefaultFontSize = 14 | ||
| 716 | cfg.DefaultUIScale = 1 | 717 | cfg.DefaultUIScale = 1 | ||
| @@ -934,26 +935,26 @@ type fakeConfigService struct { | |||||
| 934 | calledReset bool | 935 | calledReset bool | ||
| 935 | calledResetAll bool | 936 | calledResetAll bool | ||
| 936 | 937 | ||||
| 937 | - | current port.SystemviewConfigPayload | 938 | + | current dto.SystemviewConfigPayload |
| 938 | - | defaultCfg port.SystemviewConfigPayload | 939 | + | defaultCfg dto.SystemviewConfigPayload |
| 939 | keybindings port.KeybindingsConfig | 940 | keybindings port.KeybindingsConfig | ||
| 940 | - | savedConfig port.WebUIConfig | 941 | + | savedConfig dto.WebUIConfig |
| 941 | setReq port.SetKeybindingRequest | 942 | setReq port.SetKeybindingRequest | ||
| 942 | setResp port.SetKeybindingResponse | 943 | setResp port.SetKeybindingResponse | ||
| 943 | resetReq port.ResetKeybindingRequest | 944 | resetReq port.ResetKeybindingRequest | ||
| 944 | } | 945 | } | ||
| 945 | 946 | ||||
| 946 | - | func (s *fakeConfigService) Current(context.Context) (port.SystemviewConfigPayload, error) { | 947 | + | func (s *fakeConfigService) Current(context.Context) (dto.SystemviewConfigPayload, error) { |
| 947 | s.calledCurrent = true | 948 | s.calledCurrent = true | ||
| 948 | return s.current, nil | 949 | return s.current, nil | ||
| 949 | } | 950 | } | ||
| 950 | 951 | ||||
| 951 | - | func (s *fakeConfigService) Default(context.Context) (port.SystemviewConfigPayload, error) { | 952 | + | func (s *fakeConfigService) Default(context.Context) (dto.SystemviewConfigPayload, error) { |
| 952 | s.calledDefault = true | 953 | s.calledDefault = true | ||
| 953 | return s.defaultCfg, nil | 954 | return s.defaultCfg, nil | ||
| 954 | } | 955 | } | ||
| 955 | 956 | ||||
| 956 | - | func (s *fakeConfigService) Save(_ context.Context, cfg port.WebUIConfig) error { | 957 | + | func (s *fakeConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { |
| 957 | s.calledSave = true | 958 | s.calledSave = true | ||
| 958 | s.savedConfig = cfg | 959 | s.savedConfig = cfg | ||
| 959 | s.current.Appearance = cfg.Appearance | 960 | s.current.Appearance = cfg.Appearance | ||
| @@ -961,7 +962,7 @@ func (s *fakeConfigService) Save(_ context.Context, cfg port.WebUIConfig) error | |||||
| 961 | s.current.DefaultSearchEngine = cfg.DefaultSearchEngine | 962 | s.current.DefaultSearchEngine = cfg.DefaultSearchEngine | ||
| 962 | s.current.SearchShortcuts = cfg.SearchShortcuts | 963 | s.current.SearchShortcuts = cfg.SearchShortcuts | ||
| 963 | s.current.Performance.Profile = cfg.Performance.Profile | 964 | s.current.Performance.Profile = cfg.Performance.Profile | ||
| 964 | - | s.current.Performance.Custom = port.SystemviewCustomPerformancePayload{ | 965 | + | s.current.Performance.Custom = dto.SystemviewCustomPerformancePayload{ |
| 965 | SkiaCPUThreads: cfg.Performance.Custom.SkiaCPUThreads, | 966 | SkiaCPUThreads: cfg.Performance.Custom.SkiaCPUThreads, | ||
| 966 | SkiaGPUThreads: cfg.Performance.Custom.SkiaGPUThreads, | 967 | SkiaGPUThreads: cfg.Performance.Custom.SkiaGPUThreads, | ||
| 967 | WebProcessMemoryMB: cfg.Performance.Custom.WebProcessMemoryMB, | 968 | WebProcessMemoryMB: cfg.Performance.Custom.WebProcessMemoryMB, | ||
M internal/ui/systemviews/config.templ +15 -12
| @@ -1,6 +1,9 @@ | |||
| 1 | 1 | package systemviews | |
| 2 | 2 | ||
| 3 | - | import "github.com/bnema/dumber/internal/application/port" | |
| 3 | + | import ( | |
| 4 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 5 | + | "github.com/bnema/dumber/internal/application/port" | |
| 6 | + | ) | |
| 4 | 7 | ||
| 5 | 8 | templ ConfigView(data configRenderData) { | |
| 6 | 9 | @Alert("success", data.Notice) | |
| @@ -23,7 +26,7 @@ templ ConfigView(data configRenderData) { | |||
| 23 | 26 | } | |
| 24 | 27 | } | |
| 25 | 28 | ||
| 26 | - | templ AppearanceForm(config port.SystemviewConfigPayload) { | |
| 29 | + | templ AppearanceForm(config dto.SystemviewConfigPayload) { | |
| 27 | 30 | <form class="sv-card-form sv-config-form" data-sv-action="config.appearance.save"> | |
| 28 | 31 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | |
| 29 | 32 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | |
| @@ -44,20 +47,20 @@ templ AppearanceForm(config port.SystemviewConfigPayload) { | |||
| 44 | 47 | </form> | |
| 45 | 48 | } | |
| 46 | 49 | ||
| 47 | - | templ PaletteFields(title string, prefix string, palette port.ColorPalette) { | |
| 50 | + | templ PaletteFields(title string, prefix string, palette dto.ColorPalette) { | |
| 48 | 51 | <fieldset class="sv-fieldset"> | |
| 49 | 52 | <legend>{ title }</legend> | |
| 50 | - | <label><span>Background</span><input name={ prefix + "_background" } type="color" value={ palette.Background } required/></label> | |
| 51 | - | <label><span>Surface</span><input name={ prefix + "_surface" } type="color" value={ palette.Surface } required/></label> | |
| 52 | - | <label><span>Surface variant</span><input name={ prefix + "_surface_variant" } type="color" value={ palette.SurfaceVariant } required/></label> | |
| 53 | - | <label><span>Text</span><input name={ prefix + "_text" } type="color" value={ palette.Text } required/></label> | |
| 54 | - | <label><span>Muted</span><input name={ prefix + "_muted" } type="color" value={ palette.Muted } required/></label> | |
| 55 | - | <label><span>Accent</span><input name={ prefix + "_accent" } type="color" value={ palette.Accent } required/></label> | |
| 56 | - | <label><span>Border</span><input name={ prefix + "_border" } type="color" value={ palette.Border } required/></label> | |
| 53 | + | <label><span>Background</span><input name={ prefix + "_background" } type="text" value={ palette.Background } required/></label> | |
| 54 | + | <label><span>Surface</span><input name={ prefix + "_surface" } type="text" value={ palette.Surface } required/></label> | |
| 55 | + | <label><span>Surface variant</span><input name={ prefix + "_surface_variant" } type="text" value={ palette.SurfaceVariant } required/></label> | |
| 56 | + | <label><span>Text</span><input name={ prefix + "_text" } type="text" value={ palette.Text } required/></label> | |
| 57 | + | <label><span>Muted</span><input name={ prefix + "_muted" } type="text" value={ palette.Muted } required/></label> | |
| 58 | + | <label><span>Accent</span><input name={ prefix + "_accent" } type="text" value={ palette.Accent } required/></label> | |
| 59 | + | <label><span>Border</span><input name={ prefix + "_border" } type="text" value={ palette.Border } required/></label> | |
| 57 | 60 | </fieldset> | |
| 58 | 61 | } | |
| 59 | 62 | ||
| 60 | - | templ SearchSettings(config port.SystemviewConfigPayload) { | |
| 63 | + | templ SearchSettings(config dto.SystemviewConfigPayload) { | |
| 61 | 64 | <form class="sv-card-form" data-sv-action="config.search.save"> | |
| 62 | 65 | <label><span>Default search engine</span><input name="default_search_engine" type="text" value={ config.DefaultSearchEngine } placeholder="https://duckduckgo.com/?q=%s" required/></label> | |
| 63 | 66 | <button class="sv-button" type="submit">Save search default</button> | |
| @@ -97,7 +100,7 @@ templ SearchShortcutItem(shortcut searchShortcutRow) { | |||
| 97 | 100 | </li> | |
| 98 | 101 | } | |
| 99 | 102 | ||
| 100 | - | templ PerformanceSettings(config port.SystemviewConfigPayload) { | |
| 103 | + | templ PerformanceSettings(config dto.SystemviewConfigPayload) { | |
| 101 | 104 | {{ customProfile := performanceProfileSelected(config.Performance.Profile, "custom") }} | |
| 102 | 105 | <form class="sv-card-form sv-config-form" data-sv-action="config.performance.save"> | |
| 103 | 106 | <label><span>Profile</span><select name="profile"> | |
| @@ -1,6 +1,9 @@ | |||||
| 1 | package systemviews | 1 | package systemviews | ||
| 2 | 2 | ||||
| 3 | - | import "github.com/bnema/dumber/internal/application/port" | 3 | + | import ( |
| 4 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 5 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 6 | + | ) | |||
| 4 | 7 | ||||
| 5 | templ ConfigView(data configRenderData) { | 8 | templ ConfigView(data configRenderData) { | ||
| 6 | @Alert("success", data.Notice) | 9 | @Alert("success", data.Notice) | ||
| @@ -23,7 +26,7 @@ templ ConfigView(data configRenderData) { | |||||
| 23 | } | 26 | } | ||
| 24 | } | 27 | } | ||
| 25 | 28 | ||||
| 26 | - | templ AppearanceForm(config port.SystemviewConfigPayload) { | 29 | + | templ AppearanceForm(config dto.SystemviewConfigPayload) { |
| 27 | <form class="sv-card-form sv-config-form" data-sv-action="config.appearance.save"> | 30 | <form class="sv-card-form sv-config-form" data-sv-action="config.appearance.save"> | ||
| 28 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | 31 | <label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label> | ||
| 29 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | 32 | <label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label> | ||
| @@ -44,20 +47,20 @@ templ AppearanceForm(config port.SystemviewConfigPayload) { | |||||
| 44 | </form> | 47 | </form> | ||
| 45 | } | 48 | } | ||
| 46 | 49 | ||||
| 47 | - | templ PaletteFields(title string, prefix string, palette port.ColorPalette) { | 50 | + | templ PaletteFields(title string, prefix string, palette dto.ColorPalette) { |
| 48 | <fieldset class="sv-fieldset"> | 51 | <fieldset class="sv-fieldset"> | ||
| 49 | <legend>{ title }</legend> | 52 | <legend>{ title }</legend> | ||
| 50 | - | <label><span>Background</span><input name={ prefix + "_background" } type="color" value={ palette.Background } required/></label> | 53 | + | <label><span>Background</span><input name={ prefix + "_background" } type="text" value={ palette.Background } required/></label> |
| 51 | - | <label><span>Surface</span><input name={ prefix + "_surface" } type="color" value={ palette.Surface } required/></label> | 54 | + | <label><span>Surface</span><input name={ prefix + "_surface" } type="text" value={ palette.Surface } required/></label> |
| 52 | - | <label><span>Surface variant</span><input name={ prefix + "_surface_variant" } type="color" value={ palette.SurfaceVariant } required/></label> | 55 | + | <label><span>Surface variant</span><input name={ prefix + "_surface_variant" } type="text" value={ palette.SurfaceVariant } required/></label> |
| 53 | - | <label><span>Text</span><input name={ prefix + "_text" } type="color" value={ palette.Text } required/></label> | 56 | + | <label><span>Text</span><input name={ prefix + "_text" } type="text" value={ palette.Text } required/></label> |
| 54 | - | <label><span>Muted</span><input name={ prefix + "_muted" } type="color" value={ palette.Muted } required/></label> | 57 | + | <label><span>Muted</span><input name={ prefix + "_muted" } type="text" value={ palette.Muted } required/></label> |
| 55 | - | <label><span>Accent</span><input name={ prefix + "_accent" } type="color" value={ palette.Accent } required/></label> | 58 | + | <label><span>Accent</span><input name={ prefix + "_accent" } type="text" value={ palette.Accent } required/></label> |
| 56 | - | <label><span>Border</span><input name={ prefix + "_border" } type="color" value={ palette.Border } required/></label> | 59 | + | <label><span>Border</span><input name={ prefix + "_border" } type="text" value={ palette.Border } required/></label> |
| 57 | </fieldset> | 60 | </fieldset> | ||
| 58 | } | 61 | } | ||
| 59 | 62 | ||||
| 60 | - | templ SearchSettings(config port.SystemviewConfigPayload) { | 63 | + | templ SearchSettings(config dto.SystemviewConfigPayload) { |
| 61 | <form class="sv-card-form" data-sv-action="config.search.save"> | 64 | <form class="sv-card-form" data-sv-action="config.search.save"> | ||
| 62 | <label><span>Default search engine</span><input name="default_search_engine" type="text" value={ config.DefaultSearchEngine } placeholder="https://duckduckgo.com/?q=%s" required/></label> | 65 | <label><span>Default search engine</span><input name="default_search_engine" type="text" value={ config.DefaultSearchEngine } placeholder="https://duckduckgo.com/?q=%s" required/></label> | ||
| 63 | <button class="sv-button" type="submit">Save search default</button> | 66 | <button class="sv-button" type="submit">Save search default</button> | ||
| @@ -97,7 +100,7 @@ templ SearchShortcutItem(shortcut searchShortcutRow) { | |||||
| 97 | </li> | 100 | </li> | ||
| 98 | } | 101 | } | ||
| 99 | 102 | ||||
| 100 | - | templ PerformanceSettings(config port.SystemviewConfigPayload) { | 103 | + | templ PerformanceSettings(config dto.SystemviewConfigPayload) { |
| 101 | {{ customProfile := performanceProfileSelected(config.Performance.Profile, "custom") }} | 104 | {{ customProfile := performanceProfileSelected(config.Performance.Profile, "custom") }} | ||
| 102 | <form class="sv-card-form sv-config-form" data-sv-action="config.performance.save"> | 105 | <form class="sv-card-form sv-config-form" data-sv-action="config.performance.save"> | ||
| 103 | <label><span>Profile</span><select name="profile"> | 106 | <label><span>Profile</span><select name="profile"> | ||
M internal/ui/systemviews/config_actions.go +34 -21
| @@ -9,6 +9,7 @@ import ( | |||
| 9 | 9 | "strings" | |
| 10 | 10 | "sync/atomic" | |
| 11 | 11 | ||
| 12 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 12 | 13 | "github.com/bnema/dumber/internal/application/port" | |
| 13 | 14 | ) | |
| 14 | 15 | ||
| @@ -236,6 +237,9 @@ func (a *App) savePerformanceConfig(ctx context.Context, data map[string]string) | |||
| 236 | 237 | return err | |
| 237 | 238 | } | |
| 238 | 239 | profile := strings.TrimSpace(data["profile"]) | |
| 240 | + | if !isAllowedPerformanceProfile(profile) { | |
| 241 | + | return fmt.Errorf("unknown performance profile: %q", profile) | |
| 242 | + | } | |
| 239 | 243 | cfg.Performance.Profile = profile | |
| 240 | 244 | if profile != "custom" { | |
| 241 | 245 | if err := a.saveEditableConfig(ctx, cfg); err != nil { | |
| @@ -281,7 +285,7 @@ func (a *App) savePerformanceConfig(ctx context.Context, data map[string]string) | |||
| 281 | 285 | return err | |
| 282 | 286 | } | |
| 283 | 287 | ||
| 284 | - | cfg.Performance.Custom = port.WebUICustomPerformanceConfig{ | |
| 288 | + | cfg.Performance.Custom = dto.WebUICustomPerformanceConfig{ | |
| 285 | 289 | SkiaCPUThreads: skiaCPU, | |
| 286 | 290 | SkiaGPUThreads: skiaGPU, | |
| 287 | 291 | WebProcessMemoryMB: webMemory, | |
| @@ -342,10 +346,10 @@ func (a *App) resetConfigKeybinding(ctx context.Context, data map[string]string) | |||
| 342 | 346 | return nil | |
| 343 | 347 | } | |
| 344 | 348 | ||
| 345 | - | func (a *App) editableConfig(ctx context.Context) (port.WebUIConfig, error) { | |
| 349 | + | func (a *App) editableConfig(ctx context.Context) (dto.WebUIConfig, error) { | |
| 346 | 350 | cfg, err := a.deps.Config.Current(ctx) | |
| 347 | 351 | if err != nil { | |
| 348 | - | return port.WebUIConfig{}, err | |
| 352 | + | return dto.WebUIConfig{}, err | |
| 349 | 353 | } | |
| 350 | 354 | a.config = &cfg | |
| 351 | 355 | return webUIConfigFromPayload(cfg), nil | |
| @@ -371,7 +375,7 @@ func (a *App) requireKeybindingTarget(ctx context.Context, mode, action string) | |||
| 371 | 375 | return fmt.Errorf("keybinding %s/%s not found", mode, action) | |
| 372 | 376 | } | |
| 373 | 377 | ||
| 374 | - | func (a *App) saveEditableConfig(ctx context.Context, cfg port.WebUIConfig) error { | |
| 378 | + | func (a *App) saveEditableConfig(ctx context.Context, cfg dto.WebUIConfig) error { | |
| 375 | 379 | if err := a.deps.Config.Save(ctx, cfg); err != nil { | |
| 376 | 380 | return err | |
| 377 | 381 | } | |
| @@ -379,15 +383,15 @@ func (a *App) saveEditableConfig(ctx context.Context, cfg port.WebUIConfig) erro | |||
| 379 | 383 | return nil | |
| 380 | 384 | } | |
| 381 | 385 | ||
| 382 | - | func webUIConfigFromPayload(payload port.SystemviewConfigPayload) port.WebUIConfig { | |
| 383 | - | return port.WebUIConfig{ | |
| 386 | + | func webUIConfigFromPayload(payload dto.SystemviewConfigPayload) dto.WebUIConfig { | |
| 387 | + | return dto.WebUIConfig{ | |
| 384 | 388 | Appearance: payload.Appearance, | |
| 385 | 389 | DefaultUIScale: payload.DefaultUIScale, | |
| 386 | 390 | DefaultSearchEngine: payload.DefaultSearchEngine, | |
| 387 | 391 | SearchShortcuts: cloneSearchShortcuts(payload.SearchShortcuts), | |
| 388 | - | Performance: port.WebUIPerformanceConfig{ | |
| 392 | + | Performance: dto.WebUIPerformanceConfig{ | |
| 389 | 393 | Profile: payload.Performance.Profile, | |
| 390 | - | Custom: port.WebUICustomPerformanceConfig{ | |
| 394 | + | Custom: dto.WebUICustomPerformanceConfig{ | |
| 391 | 395 | SkiaCPUThreads: payload.Performance.Custom.SkiaCPUThreads, | |
| 392 | 396 | SkiaGPUThreads: payload.Performance.Custom.SkiaGPUThreads, | |
| 393 | 397 | WebProcessMemoryMB: payload.Performance.Custom.WebProcessMemoryMB, | |
| @@ -398,15 +402,15 @@ func webUIConfigFromPayload(payload port.SystemviewConfigPayload) port.WebUIConf | |||
| 398 | 402 | } | |
| 399 | 403 | } | |
| 400 | 404 | ||
| 401 | - | func cloneSearchShortcuts(shortcuts map[string]port.SearchShortcut) map[string]port.SearchShortcut { | |
| 402 | - | clone := make(map[string]port.SearchShortcut, len(shortcuts)) | |
| 405 | + | func cloneSearchShortcuts(shortcuts map[string]dto.SearchShortcut) map[string]dto.SearchShortcut { | |
| 406 | + | clone := make(map[string]dto.SearchShortcut, len(shortcuts)) | |
| 403 | 407 | for key, shortcut := range shortcuts { | |
| 404 | 408 | clone[key] = shortcut | |
| 405 | 409 | } | |
| 406 | 410 | return clone | |
| 407 | 411 | } | |
| 408 | 412 | ||
| 409 | - | func paletteFromForm(data map[string]string, prefix string) (port.ColorPalette, error) { | |
| 413 | + | func paletteFromForm(data map[string]string, prefix string) (dto.ColorPalette, error) { | |
| 410 | 414 | color := func(field string) (string, error) { | |
| 411 | 415 | value := strings.TrimSpace(data[prefix+"_"+field]) | |
| 412 | 416 | if value == "" || isValidHexColor(value) { | |
| @@ -417,34 +421,34 @@ func paletteFromForm(data map[string]string, prefix string) (port.ColorPalette, | |||
| 417 | 421 | ||
| 418 | 422 | background, err := color("background") | |
| 419 | 423 | if err != nil { | |
| 420 | - | return port.ColorPalette{}, err | |
| 424 | + | return dto.ColorPalette{}, err | |
| 421 | 425 | } | |
| 422 | 426 | surface, err := color("surface") | |
| 423 | 427 | if err != nil { | |
| 424 | - | return port.ColorPalette{}, err | |
| 428 | + | return dto.ColorPalette{}, err | |
| 425 | 429 | } | |
| 426 | 430 | surfaceVariant, err := color("surface_variant") | |
| 427 | 431 | if err != nil { | |
| 428 | - | return port.ColorPalette{}, err | |
| 432 | + | return dto.ColorPalette{}, err | |
| 429 | 433 | } | |
| 430 | 434 | text, err := color("text") | |
| 431 | 435 | if err != nil { | |
| 432 | - | return port.ColorPalette{}, err | |
| 436 | + | return dto.ColorPalette{}, err | |
| 433 | 437 | } | |
| 434 | 438 | muted, err := color("muted") | |
| 435 | 439 | if err != nil { | |
| 436 | - | return port.ColorPalette{}, err | |
| 440 | + | return dto.ColorPalette{}, err | |
| 437 | 441 | } | |
| 438 | 442 | accent, err := color("accent") | |
| 439 | 443 | if err != nil { | |
| 440 | - | return port.ColorPalette{}, err | |
| 444 | + | return dto.ColorPalette{}, err | |
| 441 | 445 | } | |
| 442 | 446 | border, err := color("border") | |
| 443 | 447 | if err != nil { | |
| 444 | - | return port.ColorPalette{}, err | |
| 448 | + | return dto.ColorPalette{}, err | |
| 445 | 449 | } | |
| 446 | 450 | ||
| 447 | - | return port.ColorPalette{ | |
| 451 | + | return dto.ColorPalette{ | |
| 448 | 452 | Background: background, | |
| 449 | 453 | Surface: surface, | |
| 450 | 454 | SurfaceVariant: surfaceVariant, | |
| @@ -459,8 +463,8 @@ func isValidHexColor(value string) bool { | |||
| 459 | 463 | return isHexColor(strings.TrimSpace(value)) | |
| 460 | 464 | } | |
| 461 | 465 | ||
| 462 | - | func searchShortcutFromForm(data map[string]string, keyField string) (string, port.SearchShortcut) { | |
| 463 | - | return strings.TrimSpace(data[keyField]), port.SearchShortcut{ | |
| 466 | + | func searchShortcutFromForm(data map[string]string, keyField string) (string, dto.SearchShortcut) { | |
| 467 | + | return strings.TrimSpace(data[keyField]), dto.SearchShortcut{ | |
| 464 | 468 | URL: strings.TrimSpace(data["url"]), | |
| 465 | 469 | Description: strings.TrimSpace(data["description"]), | |
| 466 | 470 | } | |
| @@ -497,6 +501,15 @@ func parseConfigFloat(raw, label string) (float64, error) { | |||
| 497 | 501 | return value, nil | |
| 498 | 502 | } | |
| 499 | 503 | ||
| 504 | + | func isAllowedPerformanceProfile(profile string) bool { | |
| 505 | + | switch profile { | |
| 506 | + | case "default", "lite", "max", "custom": | |
| 507 | + | return true | |
| 508 | + | default: | |
| 509 | + | return false | |
| 510 | + | } | |
| 511 | + | } | |
| 512 | + | ||
| 500 | 513 | func validatePerformanceInt(value int, label string, minValue, maxValue int) error { | |
| 501 | 514 | if value < minValue || value > maxValue { | |
| 502 | 515 | return fmt.Errorf("%s must be between %d and %d", label, minValue, maxValue) | |
| @@ -9,6 +9,7 @@ import ( | |||||
| 9 | "strings" | 9 | "strings" | ||
| 10 | "sync/atomic" | 10 | "sync/atomic" | ||
| 11 | 11 | ||||
| 12 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 12 | "github.com/bnema/dumber/internal/application/port" | 13 | "github.com/bnema/dumber/internal/application/port" | ||
| 13 | ) | 14 | ) | ||
| 14 | 15 | ||||
| @@ -236,6 +237,9 @@ func (a *App) savePerformanceConfig(ctx context.Context, data map[string]string) | |||||
| 236 | return err | 237 | return err | ||
| 237 | } | 238 | } | ||
| 238 | profile := strings.TrimSpace(data["profile"]) | 239 | profile := strings.TrimSpace(data["profile"]) | ||
| 240 | + | if !isAllowedPerformanceProfile(profile) { | |||
| 241 | + | return fmt.Errorf("unknown performance profile: %q", profile) | |||
| 242 | + | } | |||
| 239 | cfg.Performance.Profile = profile | 243 | cfg.Performance.Profile = profile | ||
| 240 | if profile != "custom" { | 244 | if profile != "custom" { | ||
| 241 | if err := a.saveEditableConfig(ctx, cfg); err != nil { | 245 | if err := a.saveEditableConfig(ctx, cfg); err != nil { | ||
| @@ -281,7 +285,7 @@ func (a *App) savePerformanceConfig(ctx context.Context, data map[string]string) | |||||
| 281 | return err | 285 | return err | ||
| 282 | } | 286 | } | ||
| 283 | 287 | ||||
| 284 | - | cfg.Performance.Custom = port.WebUICustomPerformanceConfig{ | 288 | + | cfg.Performance.Custom = dto.WebUICustomPerformanceConfig{ |
| 285 | SkiaCPUThreads: skiaCPU, | 289 | SkiaCPUThreads: skiaCPU, | ||
| 286 | SkiaGPUThreads: skiaGPU, | 290 | SkiaGPUThreads: skiaGPU, | ||
| 287 | WebProcessMemoryMB: webMemory, | 291 | WebProcessMemoryMB: webMemory, | ||
| @@ -342,10 +346,10 @@ func (a *App) resetConfigKeybinding(ctx context.Context, data map[string]string) | |||||
| 342 | return nil | 346 | return nil | ||
| 343 | } | 347 | } | ||
| 344 | 348 | ||||
| 345 | - | func (a *App) editableConfig(ctx context.Context) (port.WebUIConfig, error) { | 349 | + | func (a *App) editableConfig(ctx context.Context) (dto.WebUIConfig, error) { |
| 346 | cfg, err := a.deps.Config.Current(ctx) | 350 | cfg, err := a.deps.Config.Current(ctx) | ||
| 347 | if err != nil { | 351 | if err != nil { | ||
| 348 | - | return port.WebUIConfig{}, err | 352 | + | return dto.WebUIConfig{}, err |
| 349 | } | 353 | } | ||
| 350 | a.config = &cfg | 354 | a.config = &cfg | ||
| 351 | return webUIConfigFromPayload(cfg), nil | 355 | return webUIConfigFromPayload(cfg), nil | ||
| @@ -371,7 +375,7 @@ func (a *App) requireKeybindingTarget(ctx context.Context, mode, action string) | |||||
| 371 | return fmt.Errorf("keybinding %s/%s not found", mode, action) | 375 | return fmt.Errorf("keybinding %s/%s not found", mode, action) | ||
| 372 | } | 376 | } | ||
| 373 | 377 | ||||
| 374 | - | func (a *App) saveEditableConfig(ctx context.Context, cfg port.WebUIConfig) error { | 378 | + | func (a *App) saveEditableConfig(ctx context.Context, cfg dto.WebUIConfig) error { |
| 375 | if err := a.deps.Config.Save(ctx, cfg); err != nil { | 379 | if err := a.deps.Config.Save(ctx, cfg); err != nil { | ||
| 376 | return err | 380 | return err | ||
| 377 | } | 381 | } | ||
| @@ -379,15 +383,15 @@ func (a *App) saveEditableConfig(ctx context.Context, cfg port.WebUIConfig) erro | |||||
| 379 | return nil | 383 | return nil | ||
| 380 | } | 384 | } | ||
| 381 | 385 | ||||
| 382 | - | func webUIConfigFromPayload(payload port.SystemviewConfigPayload) port.WebUIConfig { | 386 | + | func webUIConfigFromPayload(payload dto.SystemviewConfigPayload) dto.WebUIConfig { |
| 383 | - | return port.WebUIConfig{ | 387 | + | return dto.WebUIConfig{ |
| 384 | Appearance: payload.Appearance, | 388 | Appearance: payload.Appearance, | ||
| 385 | DefaultUIScale: payload.DefaultUIScale, | 389 | DefaultUIScale: payload.DefaultUIScale, | ||
| 386 | DefaultSearchEngine: payload.DefaultSearchEngine, | 390 | DefaultSearchEngine: payload.DefaultSearchEngine, | ||
| 387 | SearchShortcuts: cloneSearchShortcuts(payload.SearchShortcuts), | 391 | SearchShortcuts: cloneSearchShortcuts(payload.SearchShortcuts), | ||
| 388 | - | Performance: port.WebUIPerformanceConfig{ | 392 | + | Performance: dto.WebUIPerformanceConfig{ |
| 389 | Profile: payload.Performance.Profile, | 393 | Profile: payload.Performance.Profile, | ||
| 390 | - | Custom: port.WebUICustomPerformanceConfig{ | 394 | + | Custom: dto.WebUICustomPerformanceConfig{ |
| 391 | SkiaCPUThreads: payload.Performance.Custom.SkiaCPUThreads, | 395 | SkiaCPUThreads: payload.Performance.Custom.SkiaCPUThreads, | ||
| 392 | SkiaGPUThreads: payload.Performance.Custom.SkiaGPUThreads, | 396 | SkiaGPUThreads: payload.Performance.Custom.SkiaGPUThreads, | ||
| 393 | WebProcessMemoryMB: payload.Performance.Custom.WebProcessMemoryMB, | 397 | WebProcessMemoryMB: payload.Performance.Custom.WebProcessMemoryMB, | ||
| @@ -398,15 +402,15 @@ func webUIConfigFromPayload(payload port.SystemviewConfigPayload) port.WebUIConf | |||||
| 398 | } | 402 | } | ||
| 399 | } | 403 | } | ||
| 400 | 404 | ||||
| 401 | - | func cloneSearchShortcuts(shortcuts map[string]port.SearchShortcut) map[string]port.SearchShortcut { | 405 | + | func cloneSearchShortcuts(shortcuts map[string]dto.SearchShortcut) map[string]dto.SearchShortcut { |
| 402 | - | clone := make(map[string]port.SearchShortcut, len(shortcuts)) | 406 | + | clone := make(map[string]dto.SearchShortcut, len(shortcuts)) |
| 403 | for key, shortcut := range shortcuts { | 407 | for key, shortcut := range shortcuts { | ||
| 404 | clone[key] = shortcut | 408 | clone[key] = shortcut | ||
| 405 | } | 409 | } | ||
| 406 | return clone | 410 | return clone | ||
| 407 | } | 411 | } | ||
| 408 | 412 | ||||
| 409 | - | func paletteFromForm(data map[string]string, prefix string) (port.ColorPalette, error) { | 413 | + | func paletteFromForm(data map[string]string, prefix string) (dto.ColorPalette, error) { |
| 410 | color := func(field string) (string, error) { | 414 | color := func(field string) (string, error) { | ||
| 411 | value := strings.TrimSpace(data[prefix+"_"+field]) | 415 | value := strings.TrimSpace(data[prefix+"_"+field]) | ||
| 412 | if value == "" || isValidHexColor(value) { | 416 | if value == "" || isValidHexColor(value) { | ||
| @@ -417,34 +421,34 @@ func paletteFromForm(data map[string]string, prefix string) (port.ColorPalette, | |||||
| 417 | 421 | ||||
| 418 | background, err := color("background") | 422 | background, err := color("background") | ||
| 419 | if err != nil { | 423 | if err != nil { | ||
| 420 | - | return port.ColorPalette{}, err | 424 | + | return dto.ColorPalette{}, err |
| 421 | } | 425 | } | ||
| 422 | surface, err := color("surface") | 426 | surface, err := color("surface") | ||
| 423 | if err != nil { | 427 | if err != nil { | ||
| 424 | - | return port.ColorPalette{}, err | 428 | + | return dto.ColorPalette{}, err |
| 425 | } | 429 | } | ||
| 426 | surfaceVariant, err := color("surface_variant") | 430 | surfaceVariant, err := color("surface_variant") | ||
| 427 | if err != nil { | 431 | if err != nil { | ||
| 428 | - | return port.ColorPalette{}, err | 432 | + | return dto.ColorPalette{}, err |
| 429 | } | 433 | } | ||
| 430 | text, err := color("text") | 434 | text, err := color("text") | ||
| 431 | if err != nil { | 435 | if err != nil { | ||
| 432 | - | return port.ColorPalette{}, err | 436 | + | return dto.ColorPalette{}, err |
| 433 | } | 437 | } | ||
| 434 | muted, err := color("muted") | 438 | muted, err := color("muted") | ||
| 435 | if err != nil { | 439 | if err != nil { | ||
| 436 | - | return port.ColorPalette{}, err | 440 | + | return dto.ColorPalette{}, err |
| 437 | } | 441 | } | ||
| 438 | accent, err := color("accent") | 442 | accent, err := color("accent") | ||
| 439 | if err != nil { | 443 | if err != nil { | ||
| 440 | - | return port.ColorPalette{}, err | 444 | + | return dto.ColorPalette{}, err |
| 441 | } | 445 | } | ||
| 442 | border, err := color("border") | 446 | border, err := color("border") | ||
| 443 | if err != nil { | 447 | if err != nil { | ||
| 444 | - | return port.ColorPalette{}, err | 448 | + | return dto.ColorPalette{}, err |
| 445 | } | 449 | } | ||
| 446 | 450 | ||||
| 447 | - | return port.ColorPalette{ | 451 | + | return dto.ColorPalette{ |
| 448 | Background: background, | 452 | Background: background, | ||
| 449 | Surface: surface, | 453 | Surface: surface, | ||
| 450 | SurfaceVariant: surfaceVariant, | 454 | SurfaceVariant: surfaceVariant, | ||
| @@ -459,8 +463,8 @@ func isValidHexColor(value string) bool { | |||||
| 459 | return isHexColor(strings.TrimSpace(value)) | 463 | return isHexColor(strings.TrimSpace(value)) | ||
| 460 | } | 464 | } | ||
| 461 | 465 | ||||
| 462 | - | func searchShortcutFromForm(data map[string]string, keyField string) (string, port.SearchShortcut) { | 466 | + | func searchShortcutFromForm(data map[string]string, keyField string) (string, dto.SearchShortcut) { |
| 463 | - | return strings.TrimSpace(data[keyField]), port.SearchShortcut{ | 467 | + | return strings.TrimSpace(data[keyField]), dto.SearchShortcut{ |
| 464 | URL: strings.TrimSpace(data["url"]), | 468 | URL: strings.TrimSpace(data["url"]), | ||
| 465 | Description: strings.TrimSpace(data["description"]), | 469 | Description: strings.TrimSpace(data["description"]), | ||
| 466 | } | 470 | } | ||
| @@ -497,6 +501,15 @@ func parseConfigFloat(raw, label string) (float64, error) { | |||||
| 497 | return value, nil | 501 | return value, nil | ||
| 498 | } | 502 | } | ||
| 499 | 503 | ||||
| 504 | + | func isAllowedPerformanceProfile(profile string) bool { | |||
| 505 | + | switch profile { | |||
| 506 | + | case "default", "lite", "max", "custom": | |||
| 507 | + | return true | |||
| 508 | + | default: | |||
| 509 | + | return false | |||
| 510 | + | } | |||
| 511 | + | } | |||
| 512 | + | ||||
| 500 | func validatePerformanceInt(value int, label string, minValue, maxValue int) error { | 513 | func validatePerformanceInt(value int, label string, minValue, maxValue int) error { | ||
| 501 | if value < minValue || value > maxValue { | 514 | if value < minValue || value > maxValue { | ||
| 502 | return fmt.Errorf("%s must be between %d and %d", label, minValue, maxValue) | 515 | return fmt.Errorf("%s must be between %d and %d", label, minValue, maxValue) | ||
M internal/ui/systemviews/config_templ.go +60 -57
| @@ -7,7 +7,10 @@ package systemviews | |||
| 7 | 7 | import "github.com/a-h/templ" | |
| 8 | 8 | import templruntime "github.com/a-h/templ/runtime" | |
| 9 | 9 | ||
| 10 | - | import "github.com/bnema/dumber/internal/application/port" | |
| 10 | + | import ( | |
| 11 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 12 | + | "github.com/bnema/dumber/internal/application/port" | |
| 13 | + | ) | |
| 11 | 14 | ||
| 12 | 15 | func ConfigView(data configRenderData) templ.Component { | |
| 13 | 16 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| @@ -160,7 +163,7 @@ func ConfigView(data configRenderData) templ.Component { | |||
| 160 | 163 | }) | |
| 161 | 164 | } | |
| 162 | 165 | ||
| 163 | - | func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |
| 166 | + | func AppearanceForm(config dto.SystemviewConfigPayload) templ.Component { | |
| 164 | 167 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 165 | 168 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 166 | 169 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| @@ -188,7 +191,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 188 | 191 | var templ_7745c5c3_Var8 string | |
| 189 | 192 | templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SansFont) | |
| 190 | 193 | if templ_7745c5c3_Err != nil { | |
| 191 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 28, Col: 101} | |
| 194 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 31, Col: 101} | |
| 192 | 195 | } | |
| 193 | 196 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) | |
| 194 | 197 | if templ_7745c5c3_Err != nil { | |
| @@ -201,7 +204,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 201 | 204 | var templ_7745c5c3_Var9 string | |
| 202 | 205 | templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SerifFont) | |
| 203 | 206 | if templ_7745c5c3_Err != nil { | |
| 204 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 29, Col: 104} | |
| 207 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 104} | |
| 205 | 208 | } | |
| 206 | 209 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) | |
| 207 | 210 | if templ_7745c5c3_Err != nil { | |
| @@ -214,7 +217,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 214 | 217 | var templ_7745c5c3_Var10 string | |
| 215 | 218 | templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.MonospaceFont) | |
| 216 | 219 | if templ_7745c5c3_Err != nil { | |
| 217 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 30, Col: 116} | |
| 220 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 33, Col: 116} | |
| 218 | 221 | } | |
| 219 | 222 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) | |
| 220 | 223 | if templ_7745c5c3_Err != nil { | |
| @@ -227,7 +230,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 227 | 230 | var templ_7745c5c3_Var11 string | |
| 228 | 231 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Appearance.DefaultFontSize)) | |
| 229 | 232 | if templ_7745c5c3_Err != nil { | |
| 230 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 31, Col: 160} | |
| 233 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 34, Col: 160} | |
| 231 | 234 | } | |
| 232 | 235 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | |
| 233 | 236 | if templ_7745c5c3_Err != nil { | |
| @@ -240,7 +243,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 240 | 243 | var templ_7745c5c3_Var12 string | |
| 241 | 244 | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigFloat(config.DefaultUIScale)) | |
| 242 | 245 | if templ_7745c5c3_Err != nil { | |
| 243 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 153} | |
| 246 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 35, Col: 153} | |
| 244 | 247 | } | |
| 245 | 248 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | |
| 246 | 249 | if templ_7745c5c3_Err != nil { | |
| @@ -296,7 +299,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||
| 296 | 299 | }) | |
| 297 | 300 | } | |
| 298 | 301 | ||
| 299 | - | func PaletteFields(title string, prefix string, palette port.ColorPalette) templ.Component { | |
| 302 | + | func PaletteFields(title string, prefix string, palette dto.ColorPalette) templ.Component { | |
| 300 | 303 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 301 | 304 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 302 | 305 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| @@ -324,7 +327,7 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 324 | 327 | var templ_7745c5c3_Var14 string | |
| 325 | 328 | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(title) | |
| 326 | 329 | if templ_7745c5c3_Err != nil { | |
| 327 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 49, Col: 17} | |
| 330 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 17} | |
| 328 | 331 | } | |
| 329 | 332 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | |
| 330 | 333 | if templ_7745c5c3_Err != nil { | |
| @@ -337,20 +340,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 337 | 340 | var templ_7745c5c3_Var15 string | |
| 338 | 341 | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_background") | |
| 339 | 342 | if templ_7745c5c3_Err != nil { | |
| 340 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 50, Col: 68} | |
| 343 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 68} | |
| 341 | 344 | } | |
| 342 | 345 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | |
| 343 | 346 | if templ_7745c5c3_Err != nil { | |
| 344 | 347 | return templ_7745c5c3_Err | |
| 345 | 348 | } | |
| 346 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" type=\"color\" value=\"") | |
| 349 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" type=\"text\" value=\"") | |
| 347 | 350 | if templ_7745c5c3_Err != nil { | |
| 348 | 351 | return templ_7745c5c3_Err | |
| 349 | 352 | } | |
| 350 | 353 | var templ_7745c5c3_Var16 string | |
| 351 | 354 | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Background) | |
| 352 | 355 | if templ_7745c5c3_Err != nil { | |
| 353 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 50, Col: 110} | |
| 356 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 109} | |
| 354 | 357 | } | |
| 355 | 358 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | |
| 356 | 359 | if templ_7745c5c3_Err != nil { | |
| @@ -363,20 +366,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 363 | 366 | var templ_7745c5c3_Var17 string | |
| 364 | 367 | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface") | |
| 365 | 368 | if templ_7745c5c3_Err != nil { | |
| 366 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 51, Col: 62} | |
| 369 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 62} | |
| 367 | 370 | } | |
| 368 | 371 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | |
| 369 | 372 | if templ_7745c5c3_Err != nil { | |
| 370 | 373 | return templ_7745c5c3_Err | |
| 371 | 374 | } | |
| 372 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" type=\"color\" value=\"") | |
| 375 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" type=\"text\" value=\"") | |
| 373 | 376 | if templ_7745c5c3_Err != nil { | |
| 374 | 377 | return templ_7745c5c3_Err | |
| 375 | 378 | } | |
| 376 | 379 | var templ_7745c5c3_Var18 string | |
| 377 | 380 | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Surface) | |
| 378 | 381 | if templ_7745c5c3_Err != nil { | |
| 379 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 51, Col: 101} | |
| 382 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 100} | |
| 380 | 383 | } | |
| 381 | 384 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | |
| 382 | 385 | if templ_7745c5c3_Err != nil { | |
| @@ -389,20 +392,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 389 | 392 | var templ_7745c5c3_Var19 string | |
| 390 | 393 | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface_variant") | |
| 391 | 394 | if templ_7745c5c3_Err != nil { | |
| 392 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 78} | |
| 395 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 78} | |
| 393 | 396 | } | |
| 394 | 397 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | |
| 395 | 398 | if templ_7745c5c3_Err != nil { | |
| 396 | 399 | return templ_7745c5c3_Err | |
| 397 | 400 | } | |
| 398 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" type=\"color\" value=\"") | |
| 401 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" type=\"text\" value=\"") | |
| 399 | 402 | if templ_7745c5c3_Err != nil { | |
| 400 | 403 | return templ_7745c5c3_Err | |
| 401 | 404 | } | |
| 402 | 405 | var templ_7745c5c3_Var20 string | |
| 403 | 406 | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(palette.SurfaceVariant) | |
| 404 | 407 | if templ_7745c5c3_Err != nil { | |
| 405 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 124} | |
| 408 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 123} | |
| 406 | 409 | } | |
| 407 | 410 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | |
| 408 | 411 | if templ_7745c5c3_Err != nil { | |
| @@ -415,20 +418,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 415 | 418 | var templ_7745c5c3_Var21 string | |
| 416 | 419 | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_text") | |
| 417 | 420 | if templ_7745c5c3_Err != nil { | |
| 418 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 56} | |
| 421 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 56} | |
| 419 | 422 | } | |
| 420 | 423 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) | |
| 421 | 424 | if templ_7745c5c3_Err != nil { | |
| 422 | 425 | return templ_7745c5c3_Err | |
| 423 | 426 | } | |
| 424 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" type=\"color\" value=\"") | |
| 427 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" type=\"text\" value=\"") | |
| 425 | 428 | if templ_7745c5c3_Err != nil { | |
| 426 | 429 | return templ_7745c5c3_Err | |
| 427 | 430 | } | |
| 428 | 431 | var templ_7745c5c3_Var22 string | |
| 429 | 432 | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Text) | |
| 430 | 433 | if templ_7745c5c3_Err != nil { | |
| 431 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 92} | |
| 434 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 91} | |
| 432 | 435 | } | |
| 433 | 436 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | |
| 434 | 437 | if templ_7745c5c3_Err != nil { | |
| @@ -441,20 +444,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 441 | 444 | var templ_7745c5c3_Var23 string | |
| 442 | 445 | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_muted") | |
| 443 | 446 | if templ_7745c5c3_Err != nil { | |
| 444 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 58} | |
| 447 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 57, Col: 58} | |
| 445 | 448 | } | |
| 446 | 449 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | |
| 447 | 450 | if templ_7745c5c3_Err != nil { | |
| 448 | 451 | return templ_7745c5c3_Err | |
| 449 | 452 | } | |
| 450 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" type=\"color\" value=\"") | |
| 453 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" type=\"text\" value=\"") | |
| 451 | 454 | if templ_7745c5c3_Err != nil { | |
| 452 | 455 | return templ_7745c5c3_Err | |
| 453 | 456 | } | |
| 454 | 457 | var templ_7745c5c3_Var24 string | |
| 455 | 458 | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Muted) | |
| 456 | 459 | if templ_7745c5c3_Err != nil { | |
| 457 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 95} | |
| 460 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 57, Col: 94} | |
| 458 | 461 | } | |
| 459 | 462 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) | |
| 460 | 463 | if templ_7745c5c3_Err != nil { | |
| @@ -467,20 +470,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 467 | 470 | var templ_7745c5c3_Var25 string | |
| 468 | 471 | templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_accent") | |
| 469 | 472 | if templ_7745c5c3_Err != nil { | |
| 470 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 60} | |
| 473 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 58, Col: 60} | |
| 471 | 474 | } | |
| 472 | 475 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | |
| 473 | 476 | if templ_7745c5c3_Err != nil { | |
| 474 | 477 | return templ_7745c5c3_Err | |
| 475 | 478 | } | |
| 476 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" type=\"color\" value=\"") | |
| 479 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" type=\"text\" value=\"") | |
| 477 | 480 | if templ_7745c5c3_Err != nil { | |
| 478 | 481 | return templ_7745c5c3_Err | |
| 479 | 482 | } | |
| 480 | 483 | var templ_7745c5c3_Var26 string | |
| 481 | 484 | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Accent) | |
| 482 | 485 | if templ_7745c5c3_Err != nil { | |
| 483 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 98} | |
| 486 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 58, Col: 97} | |
| 484 | 487 | } | |
| 485 | 488 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) | |
| 486 | 489 | if templ_7745c5c3_Err != nil { | |
| @@ -493,20 +496,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 493 | 496 | var templ_7745c5c3_Var27 string | |
| 494 | 497 | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_border") | |
| 495 | 498 | if templ_7745c5c3_Err != nil { | |
| 496 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 60} | |
| 499 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 60} | |
| 497 | 500 | } | |
| 498 | 501 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | |
| 499 | 502 | if templ_7745c5c3_Err != nil { | |
| 500 | 503 | return templ_7745c5c3_Err | |
| 501 | 504 | } | |
| 502 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" type=\"color\" value=\"") | |
| 505 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" type=\"text\" value=\"") | |
| 503 | 506 | if templ_7745c5c3_Err != nil { | |
| 504 | 507 | return templ_7745c5c3_Err | |
| 505 | 508 | } | |
| 506 | 509 | var templ_7745c5c3_Var28 string | |
| 507 | 510 | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Border) | |
| 508 | 511 | if templ_7745c5c3_Err != nil { | |
| 509 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 98} | |
| 512 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 97} | |
| 510 | 513 | } | |
| 511 | 514 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | |
| 512 | 515 | if templ_7745c5c3_Err != nil { | |
| @@ -520,7 +523,7 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||
| 520 | 523 | }) | |
| 521 | 524 | } | |
| 522 | 525 | ||
| 523 | - | func SearchSettings(config port.SystemviewConfigPayload) templ.Component { | |
| 526 | + | func SearchSettings(config dto.SystemviewConfigPayload) templ.Component { | |
| 524 | 527 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 525 | 528 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 526 | 529 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| @@ -548,7 +551,7 @@ func SearchSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 548 | 551 | var templ_7745c5c3_Var30 string | |
| 549 | 552 | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(config.DefaultSearchEngine) | |
| 550 | 553 | if templ_7745c5c3_Err != nil { | |
| 551 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 62, Col: 125} | |
| 554 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 65, Col: 125} | |
| 552 | 555 | } | |
| 553 | 556 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | |
| 554 | 557 | if templ_7745c5c3_Err != nil { | |
| @@ -616,7 +619,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 616 | 619 | var templ_7745c5c3_Var32 string | |
| 617 | 620 | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | |
| 618 | 621 | if templ_7745c5c3_Err != nil { | |
| 619 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 90, Col: 55} | |
| 622 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 93, Col: 55} | |
| 620 | 623 | } | |
| 621 | 624 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | |
| 622 | 625 | if templ_7745c5c3_Err != nil { | |
| @@ -629,7 +632,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 629 | 632 | var templ_7745c5c3_Var33 string | |
| 630 | 633 | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | |
| 631 | 634 | if templ_7745c5c3_Err != nil { | |
| 632 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 91, Col: 80} | |
| 635 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 94, Col: 80} | |
| 633 | 636 | } | |
| 634 | 637 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | |
| 635 | 638 | if templ_7745c5c3_Err != nil { | |
| @@ -642,7 +645,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 642 | 645 | var templ_7745c5c3_Var34 string | |
| 643 | 646 | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.URL) | |
| 644 | 647 | if templ_7745c5c3_Err != nil { | |
| 645 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 92, Col: 76} | |
| 648 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 95, Col: 76} | |
| 646 | 649 | } | |
| 647 | 650 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) | |
| 648 | 651 | if templ_7745c5c3_Err != nil { | |
| @@ -655,7 +658,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 655 | 658 | var templ_7745c5c3_Var35 string | |
| 656 | 659 | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Description) | |
| 657 | 660 | if templ_7745c5c3_Err != nil { | |
| 658 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 93, Col: 100} | |
| 661 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 96, Col: 100} | |
| 659 | 662 | } | |
| 660 | 663 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | |
| 661 | 664 | if templ_7745c5c3_Err != nil { | |
| @@ -668,7 +671,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 668 | 671 | var templ_7745c5c3_Var36 string | |
| 669 | 672 | templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | |
| 670 | 673 | if templ_7745c5c3_Err != nil { | |
| 671 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 95, Col: 149} | |
| 674 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 98, Col: 149} | |
| 672 | 675 | } | |
| 673 | 676 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) | |
| 674 | 677 | if templ_7745c5c3_Err != nil { | |
| @@ -682,7 +685,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||
| 682 | 685 | }) | |
| 683 | 686 | } | |
| 684 | 687 | ||
| 685 | - | func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |
| 688 | + | func PerformanceSettings(config dto.SystemviewConfigPayload) templ.Component { | |
| 686 | 689 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | |
| 687 | 690 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | |
| 688 | 691 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | |
| @@ -751,7 +754,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 751 | 754 | var templ_7745c5c3_Var38 string | |
| 752 | 755 | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaCPUThreads)) | |
| 753 | 756 | if templ_7745c5c3_Err != nil { | |
| 754 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 110, Col: 164} | |
| 757 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 113, Col: 164} | |
| 755 | 758 | } | |
| 756 | 759 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) | |
| 757 | 760 | if templ_7745c5c3_Err != nil { | |
| @@ -774,7 +777,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 774 | 777 | var templ_7745c5c3_Var39 string | |
| 775 | 778 | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaGPUThreads)) | |
| 776 | 779 | if templ_7745c5c3_Err != nil { | |
| 777 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 111, Col: 165} | |
| 780 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 114, Col: 165} | |
| 778 | 781 | } | |
| 779 | 782 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | |
| 780 | 783 | if templ_7745c5c3_Err != nil { | |
| @@ -797,7 +800,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 797 | 800 | var templ_7745c5c3_Var40 string | |
| 798 | 801 | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebProcessMemoryMB)) | |
| 799 | 802 | if templ_7745c5c3_Err != nil { | |
| 800 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 112, Col: 182} | |
| 803 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 115, Col: 182} | |
| 801 | 804 | } | |
| 802 | 805 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | |
| 803 | 806 | if templ_7745c5c3_Err != nil { | |
| @@ -820,7 +823,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 820 | 823 | var templ_7745c5c3_Var41 string | |
| 821 | 824 | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.NetworkProcessMemoryMB)) | |
| 822 | 825 | if templ_7745c5c3_Err != nil { | |
| 823 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 113, Col: 193} | |
| 826 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 116, Col: 193} | |
| 824 | 827 | } | |
| 825 | 828 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | |
| 826 | 829 | if templ_7745c5c3_Err != nil { | |
| @@ -843,7 +846,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 843 | 846 | var templ_7745c5c3_Var42 string | |
| 844 | 847 | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebViewPoolPrewarm)) | |
| 845 | 848 | if templ_7745c5c3_Err != nil { | |
| 846 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 114, Col: 177} | |
| 849 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 117, Col: 177} | |
| 847 | 850 | } | |
| 848 | 851 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 849 | 852 | if templ_7745c5c3_Err != nil { | |
| @@ -866,7 +869,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 866 | 869 | var templ_7745c5c3_Var43 string | |
| 867 | 870 | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.CPUThreads)) | |
| 868 | 871 | if templ_7745c5c3_Err != nil { | |
| 869 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 118, Col: 113} | |
| 872 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 121, Col: 113} | |
| 870 | 873 | } | |
| 871 | 874 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | |
| 872 | 875 | if templ_7745c5c3_Err != nil { | |
| @@ -879,7 +882,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 879 | 882 | var templ_7745c5c3_Var44 string | |
| 880 | 883 | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.TotalRAMMB)) | |
| 881 | 884 | if templ_7745c5c3_Err != nil { | |
| 882 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 119, Col: 113} | |
| 885 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 122, Col: 113} | |
| 883 | 886 | } | |
| 884 | 887 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | |
| 885 | 888 | if templ_7745c5c3_Err != nil { | |
| @@ -892,7 +895,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 892 | 895 | var templ_7745c5c3_Var45 string | |
| 893 | 896 | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(config.Performance.Hardware.GPUName) | |
| 894 | 897 | if templ_7745c5c3_Err != nil { | |
| 895 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 120, Col: 93} | |
| 898 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 123, Col: 93} | |
| 896 | 899 | } | |
| 897 | 900 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 898 | 901 | if templ_7745c5c3_Err != nil { | |
| @@ -905,7 +908,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||
| 905 | 908 | var templ_7745c5c3_Var46 string | |
| 906 | 909 | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(config.EngineType) | |
| 907 | 910 | if templ_7745c5c3_Err != nil { | |
| 908 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 121, Col: 75} | |
| 911 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 124, Col: 75} | |
| 909 | 912 | } | |
| 910 | 913 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | |
| 911 | 914 | if templ_7745c5c3_Err != nil { | |
| @@ -1006,7 +1009,7 @@ func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component { | |||
| 1006 | 1009 | var templ_7745c5c3_Var49 string | |
| 1007 | 1010 | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupTitle(group)) | |
| 1008 | 1011 | if templ_7745c5c3_Err != nil { | |
| 1009 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 150, Col: 37} | |
| 1012 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 153, Col: 37} | |
| 1010 | 1013 | } | |
| 1011 | 1014 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | |
| 1012 | 1015 | if templ_7745c5c3_Err != nil { | |
| @@ -1024,7 +1027,7 @@ func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component { | |||
| 1024 | 1027 | var templ_7745c5c3_Var50 string | |
| 1025 | 1028 | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupMeta(group)) | |
| 1026 | 1029 | if templ_7745c5c3_Err != nil { | |
| 1027 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 152, Col: 52} | |
| 1030 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 155, Col: 52} | |
| 1028 | 1031 | } | |
| 1029 | 1032 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) | |
| 1030 | 1033 | if templ_7745c5c3_Err != nil { | |
| @@ -1096,7 +1099,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1096 | 1099 | var templ_7745c5c3_Var52 string | |
| 1097 | 1100 | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingDescription(binding)) | |
| 1098 | 1101 | if templ_7745c5c3_Err != nil { | |
| 1099 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 174, Col: 44} | |
| 1102 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 177, Col: 44} | |
| 1100 | 1103 | } | |
| 1101 | 1104 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) | |
| 1102 | 1105 | if templ_7745c5c3_Err != nil { | |
| @@ -1109,7 +1112,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1109 | 1112 | var templ_7745c5c3_Var53 string | |
| 1110 | 1113 | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingAction(binding)) | |
| 1111 | 1114 | if templ_7745c5c3_Err != nil { | |
| 1112 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 175, Col: 50} | |
| 1115 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 178, Col: 50} | |
| 1113 | 1116 | } | |
| 1114 | 1117 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) | |
| 1115 | 1118 | if templ_7745c5c3_Err != nil { | |
| @@ -1130,7 +1133,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1130 | 1133 | var templ_7745c5c3_Var54 string | |
| 1131 | 1134 | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingStatus(binding)) | |
| 1132 | 1135 | if templ_7745c5c3_Err != nil { | |
| 1133 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 178, Col: 55} | |
| 1136 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 181, Col: 55} | |
| 1134 | 1137 | } | |
| 1135 | 1138 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54)) | |
| 1136 | 1139 | if templ_7745c5c3_Err != nil { | |
| @@ -1157,7 +1160,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1157 | 1160 | var templ_7745c5c3_Var55 string | |
| 1158 | 1161 | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | |
| 1159 | 1162 | if templ_7745c5c3_Err != nil { | |
| 1160 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 186, Col: 55} | |
| 1163 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 189, Col: 55} | |
| 1161 | 1164 | } | |
| 1162 | 1165 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55)) | |
| 1163 | 1166 | if templ_7745c5c3_Err != nil { | |
| @@ -1170,7 +1173,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1170 | 1173 | var templ_7745c5c3_Var56 string | |
| 1171 | 1174 | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | |
| 1172 | 1175 | if templ_7745c5c3_Err != nil { | |
| 1173 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 187, Col: 61} | |
| 1176 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 61} | |
| 1174 | 1177 | } | |
| 1175 | 1178 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | |
| 1176 | 1179 | if templ_7745c5c3_Err != nil { | |
| @@ -1183,7 +1186,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1183 | 1186 | var templ_7745c5c3_Var57 string | |
| 1184 | 1187 | templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingKeysValue(binding.Keys)) | |
| 1185 | 1188 | if templ_7745c5c3_Err != nil { | |
| 1186 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 188, Col: 100} | |
| 1189 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 191, Col: 100} | |
| 1187 | 1190 | } | |
| 1188 | 1191 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) | |
| 1189 | 1192 | if templ_7745c5c3_Err != nil { | |
| @@ -1196,7 +1199,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1196 | 1199 | var templ_7745c5c3_Var58 string | |
| 1197 | 1200 | templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | |
| 1198 | 1201 | if templ_7745c5c3_Err != nil { | |
| 1199 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 127} | |
| 1202 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 193, Col: 127} | |
| 1200 | 1203 | } | |
| 1201 | 1204 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) | |
| 1202 | 1205 | if templ_7745c5c3_Err != nil { | |
| @@ -1209,7 +1212,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||
| 1209 | 1212 | var templ_7745c5c3_Var59 string | |
| 1210 | 1213 | templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | |
| 1211 | 1214 | if templ_7745c5c3_Err != nil { | |
| 1212 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 158} | |
| 1215 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 193, Col: 158} | |
| 1213 | 1216 | } | |
| 1214 | 1217 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59)) | |
| 1215 | 1218 | if templ_7745c5c3_Err != nil { | |
| @@ -7,7 +7,10 @@ package systemviews | |||||
| 7 | import "github.com/a-h/templ" | 7 | import "github.com/a-h/templ" | ||
| 8 | import templruntime "github.com/a-h/templ/runtime" | 8 | import templruntime "github.com/a-h/templ/runtime" | ||
| 9 | 9 | ||||
| 10 | - | import "github.com/bnema/dumber/internal/application/port" | 10 | + | import ( |
| 11 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 12 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 13 | + | ) | |||
| 11 | 14 | ||||
| 12 | func ConfigView(data configRenderData) templ.Component { | 15 | func ConfigView(data configRenderData) templ.Component { | ||
| 13 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 16 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | ||
| @@ -160,7 +163,7 @@ func ConfigView(data configRenderData) templ.Component { | |||||
| 160 | }) | 163 | }) | ||
| 161 | } | 164 | } | ||
| 162 | 165 | ||||
| 163 | - | func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | 166 | + | func AppearanceForm(config dto.SystemviewConfigPayload) templ.Component { |
| 164 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 167 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | ||
| 165 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | 168 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||
| 166 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 169 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | ||
| @@ -188,7 +191,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 188 | var templ_7745c5c3_Var8 string | 191 | var templ_7745c5c3_Var8 string | ||
| 189 | templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SansFont) | 192 | templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SansFont) | ||
| 190 | if templ_7745c5c3_Err != nil { | 193 | if templ_7745c5c3_Err != nil { | ||
| 191 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 28, Col: 101} | 194 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 31, Col: 101} |
| 192 | } | 195 | } | ||
| 193 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) | 196 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) | ||
| 194 | if templ_7745c5c3_Err != nil { | 197 | if templ_7745c5c3_Err != nil { | ||
| @@ -201,7 +204,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 201 | var templ_7745c5c3_Var9 string | 204 | var templ_7745c5c3_Var9 string | ||
| 202 | templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SerifFont) | 205 | templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.SerifFont) | ||
| 203 | if templ_7745c5c3_Err != nil { | 206 | if templ_7745c5c3_Err != nil { | ||
| 204 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 29, Col: 104} | 207 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 104} |
| 205 | } | 208 | } | ||
| 206 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) | 209 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) | ||
| 207 | if templ_7745c5c3_Err != nil { | 210 | if templ_7745c5c3_Err != nil { | ||
| @@ -214,7 +217,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 214 | var templ_7745c5c3_Var10 string | 217 | var templ_7745c5c3_Var10 string | ||
| 215 | templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.MonospaceFont) | 218 | templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(config.Appearance.MonospaceFont) | ||
| 216 | if templ_7745c5c3_Err != nil { | 219 | if templ_7745c5c3_Err != nil { | ||
| 217 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 30, Col: 116} | 220 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 33, Col: 116} |
| 218 | } | 221 | } | ||
| 219 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) | 222 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) | ||
| 220 | if templ_7745c5c3_Err != nil { | 223 | if templ_7745c5c3_Err != nil { | ||
| @@ -227,7 +230,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 227 | var templ_7745c5c3_Var11 string | 230 | var templ_7745c5c3_Var11 string | ||
| 228 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Appearance.DefaultFontSize)) | 231 | templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Appearance.DefaultFontSize)) | ||
| 229 | if templ_7745c5c3_Err != nil { | 232 | if templ_7745c5c3_Err != nil { | ||
| 230 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 31, Col: 160} | 233 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 34, Col: 160} |
| 231 | } | 234 | } | ||
| 232 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | 235 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) | ||
| 233 | if templ_7745c5c3_Err != nil { | 236 | if templ_7745c5c3_Err != nil { | ||
| @@ -240,7 +243,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 240 | var templ_7745c5c3_Var12 string | 243 | var templ_7745c5c3_Var12 string | ||
| 241 | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigFloat(config.DefaultUIScale)) | 244 | templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigFloat(config.DefaultUIScale)) | ||
| 242 | if templ_7745c5c3_Err != nil { | 245 | if templ_7745c5c3_Err != nil { | ||
| 243 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 32, Col: 153} | 246 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 35, Col: 153} |
| 244 | } | 247 | } | ||
| 245 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | 248 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) | ||
| 246 | if templ_7745c5c3_Err != nil { | 249 | if templ_7745c5c3_Err != nil { | ||
| @@ -296,7 +299,7 @@ func AppearanceForm(config port.SystemviewConfigPayload) templ.Component { | |||||
| 296 | }) | 299 | }) | ||
| 297 | } | 300 | } | ||
| 298 | 301 | ||||
| 299 | - | func PaletteFields(title string, prefix string, palette port.ColorPalette) templ.Component { | 302 | + | func PaletteFields(title string, prefix string, palette dto.ColorPalette) templ.Component { |
| 300 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 303 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | ||
| 301 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | 304 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||
| 302 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 305 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | ||
| @@ -324,7 +327,7 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 324 | var templ_7745c5c3_Var14 string | 327 | var templ_7745c5c3_Var14 string | ||
| 325 | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(title) | 328 | templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(title) | ||
| 326 | if templ_7745c5c3_Err != nil { | 329 | if templ_7745c5c3_Err != nil { | ||
| 327 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 49, Col: 17} | 330 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 17} |
| 328 | } | 331 | } | ||
| 329 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | 332 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) | ||
| 330 | if templ_7745c5c3_Err != nil { | 333 | if templ_7745c5c3_Err != nil { | ||
| @@ -337,20 +340,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 337 | var templ_7745c5c3_Var15 string | 340 | var templ_7745c5c3_Var15 string | ||
| 338 | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_background") | 341 | templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_background") | ||
| 339 | if templ_7745c5c3_Err != nil { | 342 | if templ_7745c5c3_Err != nil { | ||
| 340 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 50, Col: 68} | 343 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 68} |
| 341 | } | 344 | } | ||
| 342 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | 345 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) | ||
| 343 | if templ_7745c5c3_Err != nil { | 346 | if templ_7745c5c3_Err != nil { | ||
| 344 | return templ_7745c5c3_Err | 347 | return templ_7745c5c3_Err | ||
| 345 | } | 348 | } | ||
| 346 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" type=\"color\" value=\"") | 349 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" type=\"text\" value=\"") |
| 347 | if templ_7745c5c3_Err != nil { | 350 | if templ_7745c5c3_Err != nil { | ||
| 348 | return templ_7745c5c3_Err | 351 | return templ_7745c5c3_Err | ||
| 349 | } | 352 | } | ||
| 350 | var templ_7745c5c3_Var16 string | 353 | var templ_7745c5c3_Var16 string | ||
| 351 | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Background) | 354 | templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Background) | ||
| 352 | if templ_7745c5c3_Err != nil { | 355 | if templ_7745c5c3_Err != nil { | ||
| 353 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 50, Col: 110} | 356 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 109} |
| 354 | } | 357 | } | ||
| 355 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | 358 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) | ||
| 356 | if templ_7745c5c3_Err != nil { | 359 | if templ_7745c5c3_Err != nil { | ||
| @@ -363,20 +366,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 363 | var templ_7745c5c3_Var17 string | 366 | var templ_7745c5c3_Var17 string | ||
| 364 | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface") | 367 | templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface") | ||
| 365 | if templ_7745c5c3_Err != nil { | 368 | if templ_7745c5c3_Err != nil { | ||
| 366 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 51, Col: 62} | 369 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 62} |
| 367 | } | 370 | } | ||
| 368 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | 371 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) | ||
| 369 | if templ_7745c5c3_Err != nil { | 372 | if templ_7745c5c3_Err != nil { | ||
| 370 | return templ_7745c5c3_Err | 373 | return templ_7745c5c3_Err | ||
| 371 | } | 374 | } | ||
| 372 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" type=\"color\" value=\"") | 375 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" type=\"text\" value=\"") |
| 373 | if templ_7745c5c3_Err != nil { | 376 | if templ_7745c5c3_Err != nil { | ||
| 374 | return templ_7745c5c3_Err | 377 | return templ_7745c5c3_Err | ||
| 375 | } | 378 | } | ||
| 376 | var templ_7745c5c3_Var18 string | 379 | var templ_7745c5c3_Var18 string | ||
| 377 | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Surface) | 380 | templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Surface) | ||
| 378 | if templ_7745c5c3_Err != nil { | 381 | if templ_7745c5c3_Err != nil { | ||
| 379 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 51, Col: 101} | 382 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 100} |
| 380 | } | 383 | } | ||
| 381 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | 384 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) | ||
| 382 | if templ_7745c5c3_Err != nil { | 385 | if templ_7745c5c3_Err != nil { | ||
| @@ -389,20 +392,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 389 | var templ_7745c5c3_Var19 string | 392 | var templ_7745c5c3_Var19 string | ||
| 390 | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface_variant") | 393 | templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_surface_variant") | ||
| 391 | if templ_7745c5c3_Err != nil { | 394 | if templ_7745c5c3_Err != nil { | ||
| 392 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 78} | 395 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 78} |
| 393 | } | 396 | } | ||
| 394 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | 397 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) | ||
| 395 | if templ_7745c5c3_Err != nil { | 398 | if templ_7745c5c3_Err != nil { | ||
| 396 | return templ_7745c5c3_Err | 399 | return templ_7745c5c3_Err | ||
| 397 | } | 400 | } | ||
| 398 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" type=\"color\" value=\"") | 401 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" type=\"text\" value=\"") |
| 399 | if templ_7745c5c3_Err != nil { | 402 | if templ_7745c5c3_Err != nil { | ||
| 400 | return templ_7745c5c3_Err | 403 | return templ_7745c5c3_Err | ||
| 401 | } | 404 | } | ||
| 402 | var templ_7745c5c3_Var20 string | 405 | var templ_7745c5c3_Var20 string | ||
| 403 | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(palette.SurfaceVariant) | 406 | templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(palette.SurfaceVariant) | ||
| 404 | if templ_7745c5c3_Err != nil { | 407 | if templ_7745c5c3_Err != nil { | ||
| 405 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 52, Col: 124} | 408 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 123} |
| 406 | } | 409 | } | ||
| 407 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | 410 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) | ||
| 408 | if templ_7745c5c3_Err != nil { | 411 | if templ_7745c5c3_Err != nil { | ||
| @@ -415,20 +418,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 415 | var templ_7745c5c3_Var21 string | 418 | var templ_7745c5c3_Var21 string | ||
| 416 | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_text") | 419 | templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_text") | ||
| 417 | if templ_7745c5c3_Err != nil { | 420 | if templ_7745c5c3_Err != nil { | ||
| 418 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 56} | 421 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 56} |
| 419 | } | 422 | } | ||
| 420 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) | 423 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) | ||
| 421 | if templ_7745c5c3_Err != nil { | 424 | if templ_7745c5c3_Err != nil { | ||
| 422 | return templ_7745c5c3_Err | 425 | return templ_7745c5c3_Err | ||
| 423 | } | 426 | } | ||
| 424 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" type=\"color\" value=\"") | 427 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" type=\"text\" value=\"") |
| 425 | if templ_7745c5c3_Err != nil { | 428 | if templ_7745c5c3_Err != nil { | ||
| 426 | return templ_7745c5c3_Err | 429 | return templ_7745c5c3_Err | ||
| 427 | } | 430 | } | ||
| 428 | var templ_7745c5c3_Var22 string | 431 | var templ_7745c5c3_Var22 string | ||
| 429 | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Text) | 432 | templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Text) | ||
| 430 | if templ_7745c5c3_Err != nil { | 433 | if templ_7745c5c3_Err != nil { | ||
| 431 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 53, Col: 92} | 434 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 91} |
| 432 | } | 435 | } | ||
| 433 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | 436 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) | ||
| 434 | if templ_7745c5c3_Err != nil { | 437 | if templ_7745c5c3_Err != nil { | ||
| @@ -441,20 +444,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 441 | var templ_7745c5c3_Var23 string | 444 | var templ_7745c5c3_Var23 string | ||
| 442 | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_muted") | 445 | templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_muted") | ||
| 443 | if templ_7745c5c3_Err != nil { | 446 | if templ_7745c5c3_Err != nil { | ||
| 444 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 58} | 447 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 57, Col: 58} |
| 445 | } | 448 | } | ||
| 446 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | 449 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) | ||
| 447 | if templ_7745c5c3_Err != nil { | 450 | if templ_7745c5c3_Err != nil { | ||
| 448 | return templ_7745c5c3_Err | 451 | return templ_7745c5c3_Err | ||
| 449 | } | 452 | } | ||
| 450 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" type=\"color\" value=\"") | 453 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" type=\"text\" value=\"") |
| 451 | if templ_7745c5c3_Err != nil { | 454 | if templ_7745c5c3_Err != nil { | ||
| 452 | return templ_7745c5c3_Err | 455 | return templ_7745c5c3_Err | ||
| 453 | } | 456 | } | ||
| 454 | var templ_7745c5c3_Var24 string | 457 | var templ_7745c5c3_Var24 string | ||
| 455 | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Muted) | 458 | templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Muted) | ||
| 456 | if templ_7745c5c3_Err != nil { | 459 | if templ_7745c5c3_Err != nil { | ||
| 457 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 54, Col: 95} | 460 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 57, Col: 94} |
| 458 | } | 461 | } | ||
| 459 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) | 462 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) | ||
| 460 | if templ_7745c5c3_Err != nil { | 463 | if templ_7745c5c3_Err != nil { | ||
| @@ -467,20 +470,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 467 | var templ_7745c5c3_Var25 string | 470 | var templ_7745c5c3_Var25 string | ||
| 468 | templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_accent") | 471 | templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_accent") | ||
| 469 | if templ_7745c5c3_Err != nil { | 472 | if templ_7745c5c3_Err != nil { | ||
| 470 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 60} | 473 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 58, Col: 60} |
| 471 | } | 474 | } | ||
| 472 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | 475 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) | ||
| 473 | if templ_7745c5c3_Err != nil { | 476 | if templ_7745c5c3_Err != nil { | ||
| 474 | return templ_7745c5c3_Err | 477 | return templ_7745c5c3_Err | ||
| 475 | } | 478 | } | ||
| 476 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" type=\"color\" value=\"") | 479 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" type=\"text\" value=\"") |
| 477 | if templ_7745c5c3_Err != nil { | 480 | if templ_7745c5c3_Err != nil { | ||
| 478 | return templ_7745c5c3_Err | 481 | return templ_7745c5c3_Err | ||
| 479 | } | 482 | } | ||
| 480 | var templ_7745c5c3_Var26 string | 483 | var templ_7745c5c3_Var26 string | ||
| 481 | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Accent) | 484 | templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Accent) | ||
| 482 | if templ_7745c5c3_Err != nil { | 485 | if templ_7745c5c3_Err != nil { | ||
| 483 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 55, Col: 98} | 486 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 58, Col: 97} |
| 484 | } | 487 | } | ||
| 485 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) | 488 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) | ||
| 486 | if templ_7745c5c3_Err != nil { | 489 | if templ_7745c5c3_Err != nil { | ||
| @@ -493,20 +496,20 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 493 | var templ_7745c5c3_Var27 string | 496 | var templ_7745c5c3_Var27 string | ||
| 494 | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_border") | 497 | templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(prefix + "_border") | ||
| 495 | if templ_7745c5c3_Err != nil { | 498 | if templ_7745c5c3_Err != nil { | ||
| 496 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 60} | 499 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 60} |
| 497 | } | 500 | } | ||
| 498 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | 501 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) | ||
| 499 | if templ_7745c5c3_Err != nil { | 502 | if templ_7745c5c3_Err != nil { | ||
| 500 | return templ_7745c5c3_Err | 503 | return templ_7745c5c3_Err | ||
| 501 | } | 504 | } | ||
| 502 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" type=\"color\" value=\"") | 505 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" type=\"text\" value=\"") |
| 503 | if templ_7745c5c3_Err != nil { | 506 | if templ_7745c5c3_Err != nil { | ||
| 504 | return templ_7745c5c3_Err | 507 | return templ_7745c5c3_Err | ||
| 505 | } | 508 | } | ||
| 506 | var templ_7745c5c3_Var28 string | 509 | var templ_7745c5c3_Var28 string | ||
| 507 | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Border) | 510 | templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(palette.Border) | ||
| 508 | if templ_7745c5c3_Err != nil { | 511 | if templ_7745c5c3_Err != nil { | ||
| 509 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 56, Col: 98} | 512 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 59, Col: 97} |
| 510 | } | 513 | } | ||
| 511 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | 514 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) | ||
| 512 | if templ_7745c5c3_Err != nil { | 515 | if templ_7745c5c3_Err != nil { | ||
| @@ -520,7 +523,7 @@ func PaletteFields(title string, prefix string, palette port.ColorPalette) templ | |||||
| 520 | }) | 523 | }) | ||
| 521 | } | 524 | } | ||
| 522 | 525 | ||||
| 523 | - | func SearchSettings(config port.SystemviewConfigPayload) templ.Component { | 526 | + | func SearchSettings(config dto.SystemviewConfigPayload) templ.Component { |
| 524 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 527 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | ||
| 525 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | 528 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||
| 526 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 529 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | ||
| @@ -548,7 +551,7 @@ func SearchSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 548 | var templ_7745c5c3_Var30 string | 551 | var templ_7745c5c3_Var30 string | ||
| 549 | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(config.DefaultSearchEngine) | 552 | templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(config.DefaultSearchEngine) | ||
| 550 | if templ_7745c5c3_Err != nil { | 553 | if templ_7745c5c3_Err != nil { | ||
| 551 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 62, Col: 125} | 554 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 65, Col: 125} |
| 552 | } | 555 | } | ||
| 553 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | 556 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) | ||
| 554 | if templ_7745c5c3_Err != nil { | 557 | if templ_7745c5c3_Err != nil { | ||
| @@ -616,7 +619,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 616 | var templ_7745c5c3_Var32 string | 619 | var templ_7745c5c3_Var32 string | ||
| 617 | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | 620 | templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | ||
| 618 | if templ_7745c5c3_Err != nil { | 621 | if templ_7745c5c3_Err != nil { | ||
| 619 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 90, Col: 55} | 622 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 93, Col: 55} |
| 620 | } | 623 | } | ||
| 621 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | 624 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) | ||
| 622 | if templ_7745c5c3_Err != nil { | 625 | if templ_7745c5c3_Err != nil { | ||
| @@ -629,7 +632,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 629 | var templ_7745c5c3_Var33 string | 632 | var templ_7745c5c3_Var33 string | ||
| 630 | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | 633 | templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | ||
| 631 | if templ_7745c5c3_Err != nil { | 634 | if templ_7745c5c3_Err != nil { | ||
| 632 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 91, Col: 80} | 635 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 94, Col: 80} |
| 633 | } | 636 | } | ||
| 634 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | 637 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) | ||
| 635 | if templ_7745c5c3_Err != nil { | 638 | if templ_7745c5c3_Err != nil { | ||
| @@ -642,7 +645,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 642 | var templ_7745c5c3_Var34 string | 645 | var templ_7745c5c3_Var34 string | ||
| 643 | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.URL) | 646 | templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.URL) | ||
| 644 | if templ_7745c5c3_Err != nil { | 647 | if templ_7745c5c3_Err != nil { | ||
| 645 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 92, Col: 76} | 648 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 95, Col: 76} |
| 646 | } | 649 | } | ||
| 647 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) | 650 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) | ||
| 648 | if templ_7745c5c3_Err != nil { | 651 | if templ_7745c5c3_Err != nil { | ||
| @@ -655,7 +658,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 655 | var templ_7745c5c3_Var35 string | 658 | var templ_7745c5c3_Var35 string | ||
| 656 | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Description) | 659 | templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Description) | ||
| 657 | if templ_7745c5c3_Err != nil { | 660 | if templ_7745c5c3_Err != nil { | ||
| 658 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 93, Col: 100} | 661 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 96, Col: 100} |
| 659 | } | 662 | } | ||
| 660 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | 663 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) | ||
| 661 | if templ_7745c5c3_Err != nil { | 664 | if templ_7745c5c3_Err != nil { | ||
| @@ -668,7 +671,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 668 | var templ_7745c5c3_Var36 string | 671 | var templ_7745c5c3_Var36 string | ||
| 669 | templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | 672 | templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(shortcut.Key) | ||
| 670 | if templ_7745c5c3_Err != nil { | 673 | if templ_7745c5c3_Err != nil { | ||
| 671 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 95, Col: 149} | 674 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 98, Col: 149} |
| 672 | } | 675 | } | ||
| 673 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) | 676 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36)) | ||
| 674 | if templ_7745c5c3_Err != nil { | 677 | if templ_7745c5c3_Err != nil { | ||
| @@ -682,7 +685,7 @@ func SearchShortcutItem(shortcut searchShortcutRow) templ.Component { | |||||
| 682 | }) | 685 | }) | ||
| 683 | } | 686 | } | ||
| 684 | 687 | ||||
| 685 | - | func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | 688 | + | func PerformanceSettings(config dto.SystemviewConfigPayload) templ.Component { |
| 686 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | 689 | return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { | ||
| 687 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | 690 | templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context | ||
| 688 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | 691 | if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { | ||
| @@ -751,7 +754,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 751 | var templ_7745c5c3_Var38 string | 754 | var templ_7745c5c3_Var38 string | ||
| 752 | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaCPUThreads)) | 755 | templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaCPUThreads)) | ||
| 753 | if templ_7745c5c3_Err != nil { | 756 | if templ_7745c5c3_Err != nil { | ||
| 754 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 110, Col: 164} | 757 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 113, Col: 164} |
| 755 | } | 758 | } | ||
| 756 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) | 759 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38)) | ||
| 757 | if templ_7745c5c3_Err != nil { | 760 | if templ_7745c5c3_Err != nil { | ||
| @@ -774,7 +777,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 774 | var templ_7745c5c3_Var39 string | 777 | var templ_7745c5c3_Var39 string | ||
| 775 | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaGPUThreads)) | 778 | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.SkiaGPUThreads)) | ||
| 776 | if templ_7745c5c3_Err != nil { | 779 | if templ_7745c5c3_Err != nil { | ||
| 777 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 111, Col: 165} | 780 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 114, Col: 165} |
| 778 | } | 781 | } | ||
| 779 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | 782 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | ||
| 780 | if templ_7745c5c3_Err != nil { | 783 | if templ_7745c5c3_Err != nil { | ||
| @@ -797,7 +800,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 797 | var templ_7745c5c3_Var40 string | 800 | var templ_7745c5c3_Var40 string | ||
| 798 | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebProcessMemoryMB)) | 801 | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebProcessMemoryMB)) | ||
| 799 | if templ_7745c5c3_Err != nil { | 802 | if templ_7745c5c3_Err != nil { | ||
| 800 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 112, Col: 182} | 803 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 115, Col: 182} |
| 801 | } | 804 | } | ||
| 802 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | 805 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | ||
| 803 | if templ_7745c5c3_Err != nil { | 806 | if templ_7745c5c3_Err != nil { | ||
| @@ -820,7 +823,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 820 | var templ_7745c5c3_Var41 string | 823 | var templ_7745c5c3_Var41 string | ||
| 821 | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.NetworkProcessMemoryMB)) | 824 | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.NetworkProcessMemoryMB)) | ||
| 822 | if templ_7745c5c3_Err != nil { | 825 | if templ_7745c5c3_Err != nil { | ||
| 823 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 113, Col: 193} | 826 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 116, Col: 193} |
| 824 | } | 827 | } | ||
| 825 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | 828 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | ||
| 826 | if templ_7745c5c3_Err != nil { | 829 | if templ_7745c5c3_Err != nil { | ||
| @@ -843,7 +846,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 843 | var templ_7745c5c3_Var42 string | 846 | var templ_7745c5c3_Var42 string | ||
| 844 | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebViewPoolPrewarm)) | 847 | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Custom.WebViewPoolPrewarm)) | ||
| 845 | if templ_7745c5c3_Err != nil { | 848 | if templ_7745c5c3_Err != nil { | ||
| 846 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 114, Col: 177} | 849 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 117, Col: 177} |
| 847 | } | 850 | } | ||
| 848 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | 851 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | ||
| 849 | if templ_7745c5c3_Err != nil { | 852 | if templ_7745c5c3_Err != nil { | ||
| @@ -866,7 +869,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 866 | var templ_7745c5c3_Var43 string | 869 | var templ_7745c5c3_Var43 string | ||
| 867 | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.CPUThreads)) | 870 | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.CPUThreads)) | ||
| 868 | if templ_7745c5c3_Err != nil { | 871 | if templ_7745c5c3_Err != nil { | ||
| 869 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 118, Col: 113} | 872 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 121, Col: 113} |
| 870 | } | 873 | } | ||
| 871 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | 874 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | ||
| 872 | if templ_7745c5c3_Err != nil { | 875 | if templ_7745c5c3_Err != nil { | ||
| @@ -879,7 +882,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 879 | var templ_7745c5c3_Var44 string | 882 | var templ_7745c5c3_Var44 string | ||
| 880 | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.TotalRAMMB)) | 883 | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(formatConfigInt(config.Performance.Hardware.TotalRAMMB)) | ||
| 881 | if templ_7745c5c3_Err != nil { | 884 | if templ_7745c5c3_Err != nil { | ||
| 882 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 119, Col: 113} | 885 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 122, Col: 113} |
| 883 | } | 886 | } | ||
| 884 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | 887 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | ||
| 885 | if templ_7745c5c3_Err != nil { | 888 | if templ_7745c5c3_Err != nil { | ||
| @@ -892,7 +895,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 892 | var templ_7745c5c3_Var45 string | 895 | var templ_7745c5c3_Var45 string | ||
| 893 | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(config.Performance.Hardware.GPUName) | 896 | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(config.Performance.Hardware.GPUName) | ||
| 894 | if templ_7745c5c3_Err != nil { | 897 | if templ_7745c5c3_Err != nil { | ||
| 895 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 120, Col: 93} | 898 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 123, Col: 93} |
| 896 | } | 899 | } | ||
| 897 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | 900 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | ||
| 898 | if templ_7745c5c3_Err != nil { | 901 | if templ_7745c5c3_Err != nil { | ||
| @@ -905,7 +908,7 @@ func PerformanceSettings(config port.SystemviewConfigPayload) templ.Component { | |||||
| 905 | var templ_7745c5c3_Var46 string | 908 | var templ_7745c5c3_Var46 string | ||
| 906 | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(config.EngineType) | 909 | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(config.EngineType) | ||
| 907 | if templ_7745c5c3_Err != nil { | 910 | if templ_7745c5c3_Err != nil { | ||
| 908 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 121, Col: 75} | 911 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 124, Col: 75} |
| 909 | } | 912 | } | ||
| 910 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | 913 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | ||
| 911 | if templ_7745c5c3_Err != nil { | 914 | if templ_7745c5c3_Err != nil { | ||
| @@ -1006,7 +1009,7 @@ func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component { | |||||
| 1006 | var templ_7745c5c3_Var49 string | 1009 | var templ_7745c5c3_Var49 string | ||
| 1007 | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupTitle(group)) | 1010 | templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupTitle(group)) | ||
| 1008 | if templ_7745c5c3_Err != nil { | 1011 | if templ_7745c5c3_Err != nil { | ||
| 1009 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 150, Col: 37} | 1012 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 153, Col: 37} |
| 1010 | } | 1013 | } | ||
| 1011 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | 1014 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) | ||
| 1012 | if templ_7745c5c3_Err != nil { | 1015 | if templ_7745c5c3_Err != nil { | ||
| @@ -1024,7 +1027,7 @@ func ConfigKeybindingGroup(group renderedKeybindingGroup) templ.Component { | |||||
| 1024 | var templ_7745c5c3_Var50 string | 1027 | var templ_7745c5c3_Var50 string | ||
| 1025 | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupMeta(group)) | 1028 | templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingGroupMeta(group)) | ||
| 1026 | if templ_7745c5c3_Err != nil { | 1029 | if templ_7745c5c3_Err != nil { | ||
| 1027 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 152, Col: 52} | 1030 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 155, Col: 52} |
| 1028 | } | 1031 | } | ||
| 1029 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) | 1032 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) | ||
| 1030 | if templ_7745c5c3_Err != nil { | 1033 | if templ_7745c5c3_Err != nil { | ||
| @@ -1096,7 +1099,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1096 | var templ_7745c5c3_Var52 string | 1099 | var templ_7745c5c3_Var52 string | ||
| 1097 | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingDescription(binding)) | 1100 | templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingDescription(binding)) | ||
| 1098 | if templ_7745c5c3_Err != nil { | 1101 | if templ_7745c5c3_Err != nil { | ||
| 1099 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 174, Col: 44} | 1102 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 177, Col: 44} |
| 1100 | } | 1103 | } | ||
| 1101 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) | 1104 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) | ||
| 1102 | if templ_7745c5c3_Err != nil { | 1105 | if templ_7745c5c3_Err != nil { | ||
| @@ -1109,7 +1112,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1109 | var templ_7745c5c3_Var53 string | 1112 | var templ_7745c5c3_Var53 string | ||
| 1110 | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingAction(binding)) | 1113 | templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingAction(binding)) | ||
| 1111 | if templ_7745c5c3_Err != nil { | 1114 | if templ_7745c5c3_Err != nil { | ||
| 1112 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 175, Col: 50} | 1115 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 178, Col: 50} |
| 1113 | } | 1116 | } | ||
| 1114 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) | 1117 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) | ||
| 1115 | if templ_7745c5c3_Err != nil { | 1118 | if templ_7745c5c3_Err != nil { | ||
| @@ -1130,7 +1133,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1130 | var templ_7745c5c3_Var54 string | 1133 | var templ_7745c5c3_Var54 string | ||
| 1131 | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingStatus(binding)) | 1134 | templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingStatus(binding)) | ||
| 1132 | if templ_7745c5c3_Err != nil { | 1135 | if templ_7745c5c3_Err != nil { | ||
| 1133 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 178, Col: 55} | 1136 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 181, Col: 55} |
| 1134 | } | 1137 | } | ||
| 1135 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54)) | 1138 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54)) | ||
| 1136 | if templ_7745c5c3_Err != nil { | 1139 | if templ_7745c5c3_Err != nil { | ||
| @@ -1157,7 +1160,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1157 | var templ_7745c5c3_Var55 string | 1160 | var templ_7745c5c3_Var55 string | ||
| 1158 | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | 1161 | templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | ||
| 1159 | if templ_7745c5c3_Err != nil { | 1162 | if templ_7745c5c3_Err != nil { | ||
| 1160 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 186, Col: 55} | 1163 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 189, Col: 55} |
| 1161 | } | 1164 | } | ||
| 1162 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55)) | 1165 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55)) | ||
| 1163 | if templ_7745c5c3_Err != nil { | 1166 | if templ_7745c5c3_Err != nil { | ||
| @@ -1170,7 +1173,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1170 | var templ_7745c5c3_Var56 string | 1173 | var templ_7745c5c3_Var56 string | ||
| 1171 | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | 1174 | templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | ||
| 1172 | if templ_7745c5c3_Err != nil { | 1175 | if templ_7745c5c3_Err != nil { | ||
| 1173 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 187, Col: 61} | 1176 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 61} |
| 1174 | } | 1177 | } | ||
| 1175 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | 1178 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) | ||
| 1176 | if templ_7745c5c3_Err != nil { | 1179 | if templ_7745c5c3_Err != nil { | ||
| @@ -1183,7 +1186,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1183 | var templ_7745c5c3_Var57 string | 1186 | var templ_7745c5c3_Var57 string | ||
| 1184 | templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingKeysValue(binding.Keys)) | 1187 | templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(keybindingKeysValue(binding.Keys)) | ||
| 1185 | if templ_7745c5c3_Err != nil { | 1188 | if templ_7745c5c3_Err != nil { | ||
| 1186 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 188, Col: 100} | 1189 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 191, Col: 100} |
| 1187 | } | 1190 | } | ||
| 1188 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) | 1191 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) | ||
| 1189 | if templ_7745c5c3_Err != nil { | 1192 | if templ_7745c5c3_Err != nil { | ||
| @@ -1196,7 +1199,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1196 | var templ_7745c5c3_Var58 string | 1199 | var templ_7745c5c3_Var58 string | ||
| 1197 | templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | 1200 | templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(group.Mode) | ||
| 1198 | if templ_7745c5c3_Err != nil { | 1201 | if templ_7745c5c3_Err != nil { | ||
| 1199 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 127} | 1202 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 193, Col: 127} |
| 1200 | } | 1203 | } | ||
| 1201 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) | 1204 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) | ||
| 1202 | if templ_7745c5c3_Err != nil { | 1205 | if templ_7745c5c3_Err != nil { | ||
| @@ -1209,7 +1212,7 @@ func ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) | |||||
| 1209 | var templ_7745c5c3_Var59 string | 1212 | var templ_7745c5c3_Var59 string | ||
| 1210 | templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | 1213 | templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(binding.Action) | ||
| 1211 | if templ_7745c5c3_Err != nil { | 1214 | if templ_7745c5c3_Err != nil { | ||
| 1212 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 190, Col: 158} | 1215 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `config.templ`, Line: 193, Col: 158} |
| 1213 | } | 1216 | } | ||
| 1214 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59)) | 1217 | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59)) | ||
| 1215 | if templ_7745c5c3_Err != nil { | 1218 | if templ_7745c5c3_Err != nil { | ||
M internal/ui/systemviews/config_view.go +3 -2
| @@ -6,11 +6,12 @@ import ( | |||
| 6 | 6 | "sort" | |
| 7 | 7 | "strings" | |
| 8 | 8 | ||
| 9 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 9 | 10 | "github.com/bnema/dumber/internal/application/port" | |
| 10 | 11 | ) | |
| 11 | 12 | ||
| 12 | 13 | type configRenderData struct { | |
| 13 | - | Config port.SystemviewConfigPayload | |
| 14 | + | Config dto.SystemviewConfigPayload | |
| 14 | 15 | Keybindings port.KeybindingsConfig | |
| 15 | 16 | Notice string | |
| 16 | 17 | Error string | |
| @@ -100,7 +101,7 @@ type searchShortcutRow struct { | |||
| 100 | 101 | Description string | |
| 101 | 102 | } | |
| 102 | 103 | ||
| 103 | - | func searchShortcutRows(shortcuts map[string]port.SearchShortcut) []searchShortcutRow { | |
| 104 | + | func searchShortcutRows(shortcuts map[string]dto.SearchShortcut) []searchShortcutRow { | |
| 104 | 105 | if len(shortcuts) == 0 { | |
| 105 | 106 | return nil | |
| 106 | 107 | } | |
| @@ -6,11 +6,12 @@ import ( | |||||
| 6 | "sort" | 6 | "sort" | ||
| 7 | "strings" | 7 | "strings" | ||
| 8 | 8 | ||||
| 9 | + | "github.com/bnema/dumber/internal/application/dto" | |||
| 9 | "github.com/bnema/dumber/internal/application/port" | 10 | "github.com/bnema/dumber/internal/application/port" | ||
| 10 | ) | 11 | ) | ||
| 11 | 12 | ||||
| 12 | type configRenderData struct { | 13 | type configRenderData struct { | ||
| 13 | - | Config port.SystemviewConfigPayload | 14 | + | Config dto.SystemviewConfigPayload |
| 14 | Keybindings port.KeybindingsConfig | 15 | Keybindings port.KeybindingsConfig | ||
| 15 | Notice string | 16 | Notice string | ||
| 16 | Error string | 17 | Error string | ||
| @@ -100,7 +101,7 @@ type searchShortcutRow struct { | |||||
| 100 | Description string | 101 | Description string | ||
| 101 | } | 102 | } | ||
| 102 | 103 | ||||
| 103 | - | func searchShortcutRows(shortcuts map[string]port.SearchShortcut) []searchShortcutRow { | 104 | + | func searchShortcutRows(shortcuts map[string]dto.SearchShortcut) []searchShortcutRow { |
| 104 | if len(shortcuts) == 0 { | 105 | if len(shortcuts) == 0 { | ||
| 105 | return nil | 106 | return nil | ||
| 106 | } | 107 | } | ||
M internal/ui/systemviews/dom_js.go +5 -6
| @@ -402,7 +402,10 @@ func isEditableTarget(target js.Value) bool { | |||
| 402 | 402 | return false | |
| 403 | 403 | } | |
| 404 | 404 | tag := strings.ToLower(target.Get("tagName").String()) | |
| 405 | - | return tag == "input" || tag == "textarea" || tag == "select" || target.Get("isContentEditable").Bool() | |
| 405 | + | role := strings.ToLower(target.Get("role").String()) | |
| 406 | + | return tag == "input" || tag == "textarea" || tag == "select" || tag == "button" || | |
| 407 | + | (tag == "a" && target.Get("href").String() != "") || role == "button" || role == "link" || | |
| 408 | + | target.Get("isContentEditable").Bool() | |
| 406 | 409 | } | |
| 407 | 410 | ||
| 408 | 411 | func (d *browserDOM) focusHistoryRow(delta int) { | |
| @@ -456,11 +459,7 @@ func (d *browserDOM) activeRow(selector, datasetKey string) js.Value { | |||
| 456 | 459 | } | |
| 457 | 460 | idx := d.activeIndex(datasetKey) | |
| 458 | 461 | if idx < 0 || idx >= length { | |
| 459 | - | idx = 0 | |
| 460 | - | row := rows.Index(idx) | |
| 461 | - | d.target.Get("dataset").Set(datasetKey, "0") | |
| 462 | - | row.Get("classList").Call("add", "sv-focused") | |
| 463 | - | return row | |
| 462 | + | return js.Value{} | |
| 464 | 463 | } | |
| 465 | 464 | return rows.Index(idx) | |
| 466 | 465 | } | |
| @@ -402,7 +402,10 @@ func isEditableTarget(target js.Value) bool { | |||||
| 402 | return false | 402 | return false | ||
| 403 | } | 403 | } | ||
| 404 | tag := strings.ToLower(target.Get("tagName").String()) | 404 | tag := strings.ToLower(target.Get("tagName").String()) | ||
| 405 | - | return tag == "input" || tag == "textarea" || tag == "select" || target.Get("isContentEditable").Bool() | 405 | + | role := strings.ToLower(target.Get("role").String()) |
| 406 | + | return tag == "input" || tag == "textarea" || tag == "select" || tag == "button" || | |||
| 407 | + | (tag == "a" && target.Get("href").String() != "") || role == "button" || role == "link" || | |||
| 408 | + | target.Get("isContentEditable").Bool() | |||
| 406 | } | 409 | } | ||
| 407 | 410 | ||||
| 408 | func (d *browserDOM) focusHistoryRow(delta int) { | 411 | func (d *browserDOM) focusHistoryRow(delta int) { | ||
| @@ -456,11 +459,7 @@ func (d *browserDOM) activeRow(selector, datasetKey string) js.Value { | |||||
| 456 | } | 459 | } | ||
| 457 | idx := d.activeIndex(datasetKey) | 460 | idx := d.activeIndex(datasetKey) | ||
| 458 | if idx < 0 || idx >= length { | 461 | if idx < 0 || idx >= length { | ||
| 459 | - | idx = 0 | 462 | + | return js.Value{} |
| 460 | - | row := rows.Index(idx) | |||
| 461 | - | d.target.Get("dataset").Set(datasetKey, "0") | |||
| 462 | - | row.Get("classList").Call("add", "sv-focused") | |||
| 463 | - | return row | |||
| 464 | } | 463 | } | ||
| 465 | return rows.Index(idx) | 464 | return rows.Index(idx) | ||
| 466 | } | 465 | } | ||
M internal/ui/systemviews/history_view_test.go +6 -3
| @@ -13,10 +13,13 @@ func TestHistoryDateKeyAndLabelUsesProvidedNow(t *testing.T) { | |||
| 13 | 13 | loc := time.FixedZone("test", 2*60*60) | |
| 14 | 14 | now := time.Date(2026, 4, 24, 12, 0, 0, 0, loc) | |
| 15 | 15 | ||
| 16 | - | _, todayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 24, 8, 30, 0, 0, loc), now) | |
| 17 | - | _, yesterdayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 23, 22, 30, 0, 0, loc), now) | |
| 18 | - | _, olderLabel := historyDateKeyAndLabel(time.Date(2026, 4, 20, 9, 0, 0, 0, loc), now) | |
| 16 | + | todayKey, todayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 24, 8, 30, 0, 0, loc), now) | |
| 17 | + | yesterdayKey, yesterdayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 23, 22, 30, 0, 0, loc), now) | |
| 18 | + | olderKey, olderLabel := historyDateKeyAndLabel(time.Date(2026, 4, 20, 9, 0, 0, 0, loc), now) | |
| 19 | 19 | ||
| 20 | + | assert.Equal(t, "2026-04-24", todayKey) | |
| 21 | + | assert.Equal(t, "2026-04-23", yesterdayKey) | |
| 22 | + | assert.Equal(t, "2026-04-20", olderKey) | |
| 20 | 23 | assert.Equal(t, "Today", todayLabel) | |
| 21 | 24 | assert.Equal(t, "Yesterday", yesterdayLabel) | |
| 22 | 25 | assert.Equal(t, "Mon Apr 20", olderLabel) | |
| @@ -13,10 +13,13 @@ func TestHistoryDateKeyAndLabelUsesProvidedNow(t *testing.T) { | |||||
| 13 | loc := time.FixedZone("test", 2*60*60) | 13 | loc := time.FixedZone("test", 2*60*60) | ||
| 14 | now := time.Date(2026, 4, 24, 12, 0, 0, 0, loc) | 14 | now := time.Date(2026, 4, 24, 12, 0, 0, 0, loc) | ||
| 15 | 15 | ||||
| 16 | - | _, todayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 24, 8, 30, 0, 0, loc), now) | 16 | + | todayKey, todayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 24, 8, 30, 0, 0, loc), now) |
| 17 | - | _, yesterdayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 23, 22, 30, 0, 0, loc), now) | 17 | + | yesterdayKey, yesterdayLabel := historyDateKeyAndLabel(time.Date(2026, 4, 23, 22, 30, 0, 0, loc), now) |
| 18 | - | _, olderLabel := historyDateKeyAndLabel(time.Date(2026, 4, 20, 9, 0, 0, 0, loc), now) | 18 | + | olderKey, olderLabel := historyDateKeyAndLabel(time.Date(2026, 4, 20, 9, 0, 0, 0, loc), now) |
| 19 | 19 | ||||
| 20 | + | assert.Equal(t, "2026-04-24", todayKey) | |||
| 21 | + | assert.Equal(t, "2026-04-23", yesterdayKey) | |||
| 22 | + | assert.Equal(t, "2026-04-20", olderKey) | |||
| 20 | assert.Equal(t, "Today", todayLabel) | 23 | assert.Equal(t, "Today", todayLabel) | ||
| 21 | assert.Equal(t, "Yesterday", yesterdayLabel) | 24 | assert.Equal(t, "Yesterday", yesterdayLabel) | ||
| 22 | assert.Equal(t, "Mon Apr 20", olderLabel) | 25 | assert.Equal(t, "Mon Apr 20", olderLabel) | ||
M internal/ui/systemviews/href.go +1 -0
| @@ -29,6 +29,7 @@ func sanitizeHref(raw string) string { | |||
| 29 | 29 | return "#" | |
| 30 | 30 | } | |
| 31 | 31 | parsed.Scheme = strings.ToLower(parsed.Scheme) | |
| 32 | + | parsed.User = nil | |
| 32 | 33 | return parsed.String() | |
| 33 | 34 | case "dumb": | |
| 34 | 35 | if parsed.Host == "" && parsed.Opaque == "" { | |
| @@ -29,6 +29,7 @@ func sanitizeHref(raw string) string { | |||||
| 29 | return "#" | 29 | return "#" | ||
| 30 | } | 30 | } | ||
| 31 | parsed.Scheme = strings.ToLower(parsed.Scheme) | 31 | parsed.Scheme = strings.ToLower(parsed.Scheme) | ||
| 32 | + | parsed.User = nil | |||
| 32 | return parsed.String() | 33 | return parsed.String() | ||
| 33 | case "dumb": | 34 | case "dumb": | ||
| 34 | if parsed.Host == "" && parsed.Opaque == "" { | 35 | if parsed.Host == "" && parsed.Opaque == "" { | ||
M internal/ui/systemviews/href_test.go +1 -0
| @@ -42,6 +42,7 @@ func TestSanitizeHrefRejectsNetworkPathAndMalformedHTTP(t *testing.T) { | |||
| 42 | 42 | assert.Equal(t, "#", sanitizeHref("https:///evil.com")) | |
| 43 | 43 | assert.Equal(t, "#", sanitizeHref("http:evil.com")) | |
| 44 | 44 | assert.Equal(t, "https://example.com", sanitizeHref("https://example.com")) | |
| 45 | + | assert.Equal(t, "https://example.com/path", sanitizeHref("https://user:pass@example.com/path")) | |
| 45 | 46 | } | |
| 46 | 47 | ||
| 47 | 48 | func TestSanitizeHrefAllowsInternalDumbRoutes(t *testing.T) { | |
| @@ -42,6 +42,7 @@ func TestSanitizeHrefRejectsNetworkPathAndMalformedHTTP(t *testing.T) { | |||||
| 42 | assert.Equal(t, "#", sanitizeHref("https:///evil.com")) | 42 | assert.Equal(t, "#", sanitizeHref("https:///evil.com")) | ||
| 43 | assert.Equal(t, "#", sanitizeHref("http:evil.com")) | 43 | assert.Equal(t, "#", sanitizeHref("http:evil.com")) | ||
| 44 | assert.Equal(t, "https://example.com", sanitizeHref("https://example.com")) | 44 | assert.Equal(t, "https://example.com", sanitizeHref("https://example.com")) | ||
| 45 | + | assert.Equal(t, "https://example.com/path", sanitizeHref("https://user:pass@example.com/path")) | |||
| 45 | } | 46 | } | ||
| 46 | 47 | ||||
| 47 | func TestSanitizeHrefAllowsInternalDumbRoutes(t *testing.T) { | 48 | func TestSanitizeHrefAllowsInternalDumbRoutes(t *testing.T) { | ||
M internal/ui/systemviews/theme.go +7 -7
| @@ -5,7 +5,7 @@ import ( | |||
| 5 | 5 | "strings" | |
| 6 | 6 | "unicode" | |
| 7 | 7 | ||
| 8 | - | "github.com/bnema/dumber/internal/application/port" | |
| 8 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | type shellTheme struct { | |
| @@ -31,12 +31,12 @@ func currentPrefersDark() bool { | |||
| 31 | 31 | return currentPrefersDarkImpl() | |
| 32 | 32 | } | |
| 33 | 33 | ||
| 34 | - | func resolveShellTheme(appearance port.WebUIAppearanceConfig) shellTheme { | |
| 34 | + | func resolveShellTheme(appearance dto.WebUIAppearanceConfig) shellTheme { | |
| 35 | 35 | if isZeroAppearance(appearance) { | |
| 36 | 36 | return shellTheme{} | |
| 37 | 37 | } | |
| 38 | 38 | ||
| 39 | - | var palette port.ColorPalette | |
| 39 | + | var palette dto.ColorPalette | |
| 40 | 40 | var rootClass string | |
| 41 | 41 | ||
| 42 | 42 | switch strings.ToLower(strings.TrimSpace(appearance.ColorScheme)) { | |
| @@ -62,7 +62,7 @@ func resolveShellTheme(appearance port.WebUIAppearanceConfig) shellTheme { | |||
| 62 | 62 | } | |
| 63 | 63 | } | |
| 64 | 64 | ||
| 65 | - | func buildInlineVars(appearance port.WebUIAppearanceConfig, palette port.ColorPalette) string { | |
| 65 | + | func buildInlineVars(appearance dto.WebUIAppearanceConfig, palette dto.ColorPalette) string { | |
| 66 | 66 | parts := make([]string, 0, 11) | |
| 67 | 67 | appendInlineVar := func(name, value string, sanitize func(string) (string, bool)) { | |
| 68 | 68 | value, ok := sanitize(value) | |
| @@ -178,12 +178,12 @@ func isNamedColor(value string) bool { | |||
| 178 | 178 | return ok | |
| 179 | 179 | } | |
| 180 | 180 | ||
| 181 | - | func isZeroAppearance(appearance port.WebUIAppearanceConfig) bool { | |
| 181 | + | func isZeroAppearance(appearance dto.WebUIAppearanceConfig) bool { | |
| 182 | 182 | return appearance.ColorScheme == "" && | |
| 183 | 183 | appearance.SansFont == "" && | |
| 184 | 184 | appearance.SerifFont == "" && | |
| 185 | 185 | appearance.MonospaceFont == "" && | |
| 186 | 186 | appearance.DefaultFontSize == 0 && | |
| 187 | - | appearance.LightPalette == (port.ColorPalette{}) && | |
| 188 | - | appearance.DarkPalette == (port.ColorPalette{}) | |
| 187 | + | appearance.LightPalette == (dto.ColorPalette{}) && | |
| 188 | + | appearance.DarkPalette == (dto.ColorPalette{}) | |
| 189 | 189 | } | |
| @@ -5,7 +5,7 @@ import ( | |||||
| 5 | "strings" | 5 | "strings" | ||
| 6 | "unicode" | 6 | "unicode" | ||
| 7 | 7 | ||||
| 8 | - | "github.com/bnema/dumber/internal/application/port" | 8 | + | "github.com/bnema/dumber/internal/application/dto" |
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | type shellTheme struct { | 11 | type shellTheme struct { | ||
| @@ -31,12 +31,12 @@ func currentPrefersDark() bool { | |||||
| 31 | return currentPrefersDarkImpl() | 31 | return currentPrefersDarkImpl() | ||
| 32 | } | 32 | } | ||
| 33 | 33 | ||||
| 34 | - | func resolveShellTheme(appearance port.WebUIAppearanceConfig) shellTheme { | 34 | + | func resolveShellTheme(appearance dto.WebUIAppearanceConfig) shellTheme { |
| 35 | if isZeroAppearance(appearance) { | 35 | if isZeroAppearance(appearance) { | ||
| 36 | return shellTheme{} | 36 | return shellTheme{} | ||
| 37 | } | 37 | } | ||
| 38 | 38 | ||||
| 39 | - | var palette port.ColorPalette | 39 | + | var palette dto.ColorPalette |
| 40 | var rootClass string | 40 | var rootClass string | ||
| 41 | 41 | ||||
| 42 | switch strings.ToLower(strings.TrimSpace(appearance.ColorScheme)) { | 42 | switch strings.ToLower(strings.TrimSpace(appearance.ColorScheme)) { | ||
| @@ -62,7 +62,7 @@ func resolveShellTheme(appearance port.WebUIAppearanceConfig) shellTheme { | |||||
| 62 | } | 62 | } | ||
| 63 | } | 63 | } | ||
| 64 | 64 | ||||
| 65 | - | func buildInlineVars(appearance port.WebUIAppearanceConfig, palette port.ColorPalette) string { | 65 | + | func buildInlineVars(appearance dto.WebUIAppearanceConfig, palette dto.ColorPalette) string { |
| 66 | parts := make([]string, 0, 11) | 66 | parts := make([]string, 0, 11) | ||
| 67 | appendInlineVar := func(name, value string, sanitize func(string) (string, bool)) { | 67 | appendInlineVar := func(name, value string, sanitize func(string) (string, bool)) { | ||
| 68 | value, ok := sanitize(value) | 68 | value, ok := sanitize(value) | ||
| @@ -178,12 +178,12 @@ func isNamedColor(value string) bool { | |||||
| 178 | return ok | 178 | return ok | ||
| 179 | } | 179 | } | ||
| 180 | 180 | ||||
| 181 | - | func isZeroAppearance(appearance port.WebUIAppearanceConfig) bool { | 181 | + | func isZeroAppearance(appearance dto.WebUIAppearanceConfig) bool { |
| 182 | return appearance.ColorScheme == "" && | 182 | return appearance.ColorScheme == "" && | ||
| 183 | appearance.SansFont == "" && | 183 | appearance.SansFont == "" && | ||
| 184 | appearance.SerifFont == "" && | 184 | appearance.SerifFont == "" && | ||
| 185 | appearance.MonospaceFont == "" && | 185 | appearance.MonospaceFont == "" && | ||
| 186 | appearance.DefaultFontSize == 0 && | 186 | appearance.DefaultFontSize == 0 && | ||
| 187 | - | appearance.LightPalette == (port.ColorPalette{}) && | 187 | + | appearance.LightPalette == (dto.ColorPalette{}) && |
| 188 | - | appearance.DarkPalette == (port.ColorPalette{}) | 188 | + | appearance.DarkPalette == (dto.ColorPalette{}) |
| 189 | } | 189 | } | ||
M internal/ui/systemviews/theme_test.go +7 -7
| @@ -3,25 +3,25 @@ package systemviews | |||
| 3 | 3 | import ( | |
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | - | "github.com/bnema/dumber/internal/application/port" | |
| 6 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | 7 | "github.com/stretchr/testify/assert" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func TestBuildInlineVarsRejectsUnsafeCSSValues(t *testing.T) { | |
| 11 | 11 | t.Parallel() | |
| 12 | 12 | ||
| 13 | - | vars := buildInlineVars(port.WebUIAppearanceConfig{ | |
| 13 | + | vars := buildInlineVars(dto.WebUIAppearanceConfig{ | |
| 14 | 14 | SansFont: "Inter; color:red", | |
| 15 | 15 | SerifFont: "Georgia", | |
| 16 | 16 | MonospaceFont: "JetBrains Mono", | |
| 17 | - | LightPalette: port.ColorPalette{ | |
| 17 | + | LightPalette: dto.ColorPalette{ | |
| 18 | 18 | Background: "#ffffff; color:red", | |
| 19 | 19 | Surface: "rgb(1 2 3 / 50%)", | |
| 20 | 20 | Text: "transparent", | |
| 21 | 21 | Accent: "definitelynotacolor", | |
| 22 | 22 | Border: "#ABCDEF80", | |
| 23 | 23 | }, | |
| 24 | - | }, port.ColorPalette{ | |
| 24 | + | }, dto.ColorPalette{ | |
| 25 | 25 | Background: "#ffffff; color:red", | |
| 26 | 26 | Surface: "rgb(1 2 3 / 50%)", | |
| 27 | 27 | Text: "transparent", | |
| @@ -73,13 +73,13 @@ func TestResolveShellTheme(t *testing.T) { | |||
| 73 | 73 | currentPrefersDarkImpl = orig | |
| 74 | 74 | }) | |
| 75 | 75 | ||
| 76 | - | appearance := port.WebUIAppearanceConfig{ | |
| 76 | + | appearance := dto.WebUIAppearanceConfig{ | |
| 77 | 77 | SansFont: "Inter", | |
| 78 | 78 | SerifFont: "Georgia", | |
| 79 | 79 | MonospaceFont: "JetBrains Mono", | |
| 80 | 80 | DefaultFontSize: 16, | |
| 81 | 81 | ColorScheme: "default", | |
| 82 | - | LightPalette: port.ColorPalette{ | |
| 82 | + | LightPalette: dto.ColorPalette{ | |
| 83 | 83 | Background: "#ffffff", | |
| 84 | 84 | Surface: "#f8f8f8", | |
| 85 | 85 | SurfaceVariant: "#eeeeee", | |
| @@ -88,7 +88,7 @@ func TestResolveShellTheme(t *testing.T) { | |||
| 88 | 88 | Accent: "#0055ff", | |
| 89 | 89 | Border: "#dddddd", | |
| 90 | 90 | }, | |
| 91 | - | DarkPalette: port.ColorPalette{ | |
| 91 | + | DarkPalette: dto.ColorPalette{ | |
| 92 | 92 | Background: "#111111", | |
| 93 | 93 | Surface: "#1a1a1a", | |
| 94 | 94 | SurfaceVariant: "#2a2a2a", | |
| @@ -3,25 +3,25 @@ package systemviews | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "testing" | 4 | "testing" | ||
| 5 | 5 | ||||
| 6 | - | "github.com/bnema/dumber/internal/application/port" | 6 | + | "github.com/bnema/dumber/internal/application/dto" |
| 7 | "github.com/stretchr/testify/assert" | 7 | "github.com/stretchr/testify/assert" | ||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | func TestBuildInlineVarsRejectsUnsafeCSSValues(t *testing.T) { | 10 | func TestBuildInlineVarsRejectsUnsafeCSSValues(t *testing.T) { | ||
| 11 | t.Parallel() | 11 | t.Parallel() | ||
| 12 | 12 | ||||
| 13 | - | vars := buildInlineVars(port.WebUIAppearanceConfig{ | 13 | + | vars := buildInlineVars(dto.WebUIAppearanceConfig{ |
| 14 | SansFont: "Inter; color:red", | 14 | SansFont: "Inter; color:red", | ||
| 15 | SerifFont: "Georgia", | 15 | SerifFont: "Georgia", | ||
| 16 | MonospaceFont: "JetBrains Mono", | 16 | MonospaceFont: "JetBrains Mono", | ||
| 17 | - | LightPalette: port.ColorPalette{ | 17 | + | LightPalette: dto.ColorPalette{ |
| 18 | Background: "#ffffff; color:red", | 18 | Background: "#ffffff; color:red", | ||
| 19 | Surface: "rgb(1 2 3 / 50%)", | 19 | Surface: "rgb(1 2 3 / 50%)", | ||
| 20 | Text: "transparent", | 20 | Text: "transparent", | ||
| 21 | Accent: "definitelynotacolor", | 21 | Accent: "definitelynotacolor", | ||
| 22 | Border: "#ABCDEF80", | 22 | Border: "#ABCDEF80", | ||
| 23 | }, | 23 | }, | ||
| 24 | - | }, port.ColorPalette{ | 24 | + | }, dto.ColorPalette{ |
| 25 | Background: "#ffffff; color:red", | 25 | Background: "#ffffff; color:red", | ||
| 26 | Surface: "rgb(1 2 3 / 50%)", | 26 | Surface: "rgb(1 2 3 / 50%)", | ||
| 27 | Text: "transparent", | 27 | Text: "transparent", | ||
| @@ -73,13 +73,13 @@ func TestResolveShellTheme(t *testing.T) { | |||||
| 73 | currentPrefersDarkImpl = orig | 73 | currentPrefersDarkImpl = orig | ||
| 74 | }) | 74 | }) | ||
| 75 | 75 | ||||
| 76 | - | appearance := port.WebUIAppearanceConfig{ | 76 | + | appearance := dto.WebUIAppearanceConfig{ |
| 77 | SansFont: "Inter", | 77 | SansFont: "Inter", | ||
| 78 | SerifFont: "Georgia", | 78 | SerifFont: "Georgia", | ||
| 79 | MonospaceFont: "JetBrains Mono", | 79 | MonospaceFont: "JetBrains Mono", | ||
| 80 | DefaultFontSize: 16, | 80 | DefaultFontSize: 16, | ||
| 81 | ColorScheme: "default", | 81 | ColorScheme: "default", | ||
| 82 | - | LightPalette: port.ColorPalette{ | 82 | + | LightPalette: dto.ColorPalette{ |
| 83 | Background: "#ffffff", | 83 | Background: "#ffffff", | ||
| 84 | Surface: "#f8f8f8", | 84 | Surface: "#f8f8f8", | ||
| 85 | SurfaceVariant: "#eeeeee", | 85 | SurfaceVariant: "#eeeeee", | ||
| @@ -88,7 +88,7 @@ func TestResolveShellTheme(t *testing.T) { | |||||
| 88 | Accent: "#0055ff", | 88 | Accent: "#0055ff", | ||
| 89 | Border: "#dddddd", | 89 | Border: "#dddddd", | ||
| 90 | }, | 90 | }, | ||
| 91 | - | DarkPalette: port.ColorPalette{ | 91 | + | DarkPalette: dto.ColorPalette{ |
| 92 | Background: "#111111", | 92 | Background: "#111111", | ||
| 93 | Surface: "#1a1a1a", | 93 | Surface: "#1a1a1a", | ||
| 94 | SurfaceVariant: "#2a2a2a", | 94 | SurfaceVariant: "#2a2a2a", | ||