Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"solo/internal/configuration"
"solo/internal/environment"
"solo/internal/exporter"
"solo/internal/fonts"
"solo/internal/git"
"solo/internal/host"
"solo/internal/importer"
Expand Down Expand Up @@ -305,6 +306,32 @@ func (a *App) GetThemeByName(themeName string) (*theme.Theme, error) {
return a.configManager.GetThemeByName(themeName)
}

// Font Management Methods

// SetBaseFontSizePx sets the base font size and persists the change.
func (a *App) SetBaseFontSizePx(fontSize int) error {
if a.configManager == nil {
return fmt.Errorf("configuration manager not initialized")
}
return a.configManager.SetBaseFontSizePx(fontSize)
}

// SetDefaultFontFamily sets the default font family and persists the change.
func (a *App) SetDefaultFontFamily(fontFamily string) error {
if a.configManager == nil {
return fmt.Errorf("configuration manager not initialized")
}
return a.configManager.SetDefaultFontFamily(fontFamily)
}

// SetMonoFontFamily sets the monospace font family and persists the change.
func (a *App) SetMonoFontFamily(fontFamily string) error {
if a.configManager == nil {
return fmt.Errorf("configuration manager not initialized")
}
return a.configManager.SetMonoFontFamily(fontFamily)
}

// Host Management Methods

// GetAllHosts returns a list of all configured hosts.
Expand Down Expand Up @@ -744,6 +771,11 @@ func (a *App) GetDefaultConfiguration() (configuration.Configuration, error) {
return a.configManager.GetDefaultConfiguration(), nil
}

// ListSystemFonts returns installed system font families with monospace metadata.
func (a *App) ListSystemFonts(refresh bool) ([]fonts.SystemFont, error) {
return fonts.ListFamilies(refresh)
}

// Request Management Methods

// GetRequests returns all requests within a specific collection.
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (

require (
github.com/google/go-github/v76 v76.0.0
golang.org/x/image v0.39.0
gopkg.in/yaml.v3 v3.0.1
)

Expand Down Expand Up @@ -52,5 +53,5 @@ require (
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/text v0.36.0 // indirect
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
Expand All @@ -97,8 +99,8 @@ golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
3 changes: 3 additions & 0 deletions internal/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type GeneralSettings struct {
IncludePrereleaseUpdates bool `json:"includePrereleaseUpdates"`
DebugMode bool `json:"debugMode"`
SelectedEnvironment string `json:"selectedEnvironment,omitempty"`
BaseFontSizePx int `json:"baseFontSizePx"`
DefaultFontFamily string `json:"defaultFontFamily"`
MonoFontFamily string `json:"monoFontFamily"`
}

type RequestSettings struct {
Expand Down
40 changes: 40 additions & 0 deletions internal/configuration/configuration_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"log/slog"
"os"
"solo/internal/fonts"
"solo/internal/theme"
"solo/internal/tools"
"sync"
Expand Down Expand Up @@ -60,6 +61,9 @@ func (cm *ConfigurationManager) createDefault() Configuration {
CheckForUpdates: tools.DEFAULT_CHECK_UPDATES,
IncludePrereleaseUpdates: tools.DEFAULT_INCLUDE_PRERELEASE_UPDATES,
DebugMode: false,
BaseFontSizePx: tools.DEFAULT_BASE_FONT_SIZE_PX,
DefaultFontFamily: "",
Comment thread
matstech marked this conversation as resolved.
MonoFontFamily: "",
},
Request: RequestSettings{
TimeoutSeconds: tools.DEFAULT_TIMEOUT_SECONDS,
Expand Down Expand Up @@ -223,3 +227,39 @@ func (cm *ConfigurationManager) DeleteCustomTheme(themeName string) error {

return cm.Save(configToSave)
}

func (cm *ConfigurationManager) SetBaseFontSizePx(fontSize int) error {
cm.mu.Lock()
cm.config.General.BaseFontSizePx = fonts.ClampBaseFontSizePx(fontSize)
configToSave := *cm.config
cm.mu.Unlock()
return cm.Save(configToSave) // Save immediately to persist
}

func (cm *ConfigurationManager) SetDefaultFontFamily(fontFamily string) error {
if fonts.IsValidFontFamily(fontFamily) {
cm.mu.Lock()
cm.config.General.DefaultFontFamily = fontFamily
configToSave := *cm.config
cm.mu.Unlock()
return cm.Save(configToSave) // Save immediately to persist
}
return fmt.Errorf("invalid font family: %s", fontFamily)
}

func (cm *ConfigurationManager) GetMonoFontFamily() string {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.config.General.MonoFontFamily
}

func (cm *ConfigurationManager) SetMonoFontFamily(fontFamily string) error {
if fonts.IsValidMonoFontFamily(fontFamily) {
cm.mu.Lock()
cm.config.General.MonoFontFamily = fontFamily
configToSave := *cm.config
cm.mu.Unlock()
return cm.Save(configToSave) // Save immediately to persist
}
return fmt.Errorf("invalid font family: %s", fontFamily)
}
82 changes: 82 additions & 0 deletions internal/configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package configuration

import (
"os"
"path/filepath"
"solo/internal/testutil"
"solo/internal/theme"
"solo/internal/tools"
Expand Down Expand Up @@ -32,6 +34,15 @@ func TestConfigurationManager_Defaults(t *testing.T) {
cfg.General.IncludePrereleaseUpdates,
)
}
if cfg.General.BaseFontSizePx != tools.DEFAULT_BASE_FONT_SIZE_PX {
t.Errorf("Expected default base font size %d, got %d", tools.DEFAULT_BASE_FONT_SIZE_PX, cfg.General.BaseFontSizePx)
}
if cfg.General.DefaultFontFamily != "" {
t.Errorf("Expected default sans font family to be empty, got %q", cfg.General.DefaultFontFamily)
}
if cfg.General.MonoFontFamily != "" {
t.Errorf("Expected default mono font family to be empty, got %q", cfg.General.MonoFontFamily)
}

newTheme := "nord"
cfg.General.ActiveTheme = newTheme
Expand All @@ -53,6 +64,77 @@ func TestConfigurationManager_Defaults(t *testing.T) {
}
}

func TestConfigurationManager_LegacyTypographyFieldsRemainUnsetOnLoad(t *testing.T) {
testutil.IsolateUserConfigDir(t)

configDir, err := tools.GetOrCreateConfigDir()
if err != nil {
t.Fatalf("GetOrCreateConfigDir failed: %v", err)
}

legacyConfig := []byte(`{
"general": {
"activeTheme": "ocean",
"themeMode": "system",
"dayTheme": "ocean",
"nightTheme": "nord",
"checkForUpdates": true,
"includePrereleaseUpdates": false,
"debugMode": false
},
"request": {
"timeoutSeconds": 30,
"followRedirects": true,
"maxRedirects": 10,
"validateSSL": true,
"defaultUserAgent": "Solo/1.0"
},
"customThemes": []
}`)
if err := os.WriteFile(filepath.Join(configDir, tools.CONFIG_JSON_FILENAME), legacyConfig, 0o600); err != nil {
t.Fatalf("WriteFile failed: %v", err)
}

cm, err := NewConfigurationManager()
if err != nil {
t.Fatalf("NewConfigurationManager failed: %v", err)
}

cfg := cm.Get()
if cfg.General.BaseFontSizePx != 0 {
t.Errorf("Expected legacy base font size to remain unset (0), got %d", cfg.General.BaseFontSizePx)
}
if cfg.General.DefaultFontFamily != "" {
t.Errorf("Expected legacy sans font family to remain empty, got %q", cfg.General.DefaultFontFamily)
}
if cfg.General.MonoFontFamily != "" {
t.Errorf("Expected legacy mono font family to remain empty, got %q", cfg.General.MonoFontFamily)
}
}

func TestConfigurationManager_ClampsInvalidBaseFontSize(t *testing.T) {
testutil.IsolateUserConfigDir(t)

cm, err := NewConfigurationManager()
if err != nil {
t.Fatalf("NewConfigurationManager failed: %v", err)
}

if err := cm.SetBaseFontSizePx(99); err != nil {
t.Fatalf("Save failed: %v", err)
}

cm2, err := NewConfigurationManager()
if err != nil {
t.Fatalf("Second NewConfigurationManager failed: %v", err)
}

cfg2 := cm2.Get()
if cfg2.General.BaseFontSizePx != tools.DEFAULT_BASE_FONT_SIZE_PX {
t.Errorf("Expected clamped base font size %d, got %d", tools.DEFAULT_BASE_FONT_SIZE_PX, cfg2.General.BaseFontSizePx)
}
}

func TestConfigurationManager_ThemeManagement(t *testing.T) {
testutil.IsolateUserConfigDir(t)

Expand Down
20 changes: 20 additions & 0 deletions internal/fonts/dirs_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2026-present raml-dev
// SPDX-License-Identifier: AGPL-3.0-only

//go:build darwin

package fonts

import (
"os"
"path/filepath"
)

func fontDirs() []string {
home, _ := os.UserHomeDir()
return []string{
filepath.Join(home, "Library", "Fonts"),
"/Library/Fonts",
"/System/Library/Fonts",
}
}
21 changes: 21 additions & 0 deletions internal/fonts/dirs_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2026-present raml-dev
// SPDX-License-Identifier: AGPL-3.0-only

//go:build linux

package fonts

import (
"os"
"path/filepath"
)

func fontDirs() []string {
home, _ := os.UserHomeDir()
return []string{
filepath.Join(home, ".fonts"),
filepath.Join(home, ".local", "share", "fonts"),
"/usr/share/fonts",
"/usr/local/share/fonts",
}
}
20 changes: 20 additions & 0 deletions internal/fonts/dirs_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2026-present raml-dev
// SPDX-License-Identifier: AGPL-3.0-only

//go:build windows

package fonts

import (
"os"
"path/filepath"
)

func fontDirs() []string {
windir := os.Getenv("WINDIR")
home, _ := os.UserHomeDir()
return []string{
filepath.Join(windir, "Fonts"),
filepath.Join(home, "AppData", "Local", "Microsoft", "Windows", "Fonts"),
}
}
Loading
Loading