Skip to content

Commit 558f5e7

Browse files
committed
fix(client): deterministic theme cycle order; document in THEMES/README/ARCH
1 parent d5fa1f5 commit 558f5e7

5 files changed

Lines changed: 46 additions & 3 deletions

File tree

ARCHITECTURE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ marchat produces two main executables:
524524
- **Color Schemes**: Comprehensive color palette support
525525
- **Component Styling**: Granular control over UI elements
526526
- **Dynamic Loading**: Runtime theme switching
527+
- **List / cycle order**: Built-in themes in a fixed sequence; custom themes sorted lexicographically by JSON key for `:themes` and Ctrl+T (see **THEMES.md**)
527528

528529
### Alternative Frontends
529530

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ A lightweight terminal chat with real-time messaging over WebSockets, optional E
5050
#### Toolchain and security
5151
- **Go 1.25.9** in **`go.mod`**, GitHub Actions, and the **Docker** builder image clears **govulncheck**-listed standard-library issues from **Go 1.25.8** (**crypto/tls**, **crypto/x509**, **archive/tar**, **html/template**, etc.). See **SECURITY.md** for scanner notes: container/SBOM scans often flag dependency **presence** in the image/binary; **`govulncheck ./...`** checks **reachability**. Package-level **pgx** findings (for example **CVE-2026-33815** / **CVE-2026-33816**) may persist while advisory **fixed-version** metadata lags; marchat ships **pgx** v5.9.0+ with related upstream protocol fixes, and default **`govulncheck ./...`** reports no reachable vulnerable call paths.
5252

53+
### Unreleased (main; not in v0.11.0-beta.3 yet)
54+
55+
- **Client themes**: **`:themes`** and **Ctrl+T** use a deterministic order—built-ins `system``patriot``retro``modern`, then custom themes sorted alphabetically by JSON key in `themes.json` ([THEMES.md](THEMES.md)).
56+
5357
### v0.11.0-beta.2
5458
- **Go 1.25.8** across CI, Docker, and docs; **SECURITY.md** updates (supported versions, edwards25519 note)
5559
- **UX**: Terminal-native chrome (reaction/message emoji unchanged); **Alt+M** / **`:msginfo`** toggle message metadata; colorized server banner and client pre-TUI (**`NO_COLOR`** respected)

THEMES.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ Marchat comes with 4 built-in themes:
1919
- **retro** - Retro terminal theme with orange, green, and yellow
2020
- **modern** - Modern dark theme with blue-gray tones
2121

22+
## Theme list and cycle order
23+
24+
`:themes` and **Ctrl+T** use a **stable** order every run:
25+
26+
1. Built-in themes, always in this sequence: **system****patriot****retro****modern**.
27+
2. Custom themes from `themes.json`, sorted **alphabetically by JSON key** (the quoted identifier for each theme object, e.g. `"dracula"`). Key order in the file does not affect this list; rename the key if you want a different position in the cycle.
28+
2229
## Custom Theme Format
2330

2431
```json
@@ -86,15 +93,15 @@ Marchat comes with 4 built-in themes:
8693
:themes
8794
```
8895

89-
This will show all available themes (built-in + custom) with descriptions.
96+
This will show all available themes (built-in + custom) with descriptions, in the [order above](#theme-list-and-cycle-order).
9097

9198
### Switch to a Theme
9299
```
93100
:theme dracula
94101
```
95102

96103
### Cycle Through Themes
97-
Press `Ctrl+T` to cycle through all available themes (built-in and custom).
104+
Press `Ctrl+T` to cycle through all available themes (built-in and custom) in the [same order](#theme-list-and-cycle-order).
98105

99106
### Set Default Theme
100107
Use the interactive configuration or edit your config file:

client/theme_loader.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8+
"sort"
89
"strings"
910

1011
"github.com/charmbracelet/lipgloss"
@@ -80,12 +81,14 @@ func LoadCustomThemes() error {
8081
return nil
8182
}
8283

83-
// GetCustomThemeNames returns a list of all custom theme names
84+
// GetCustomThemeNames returns a list of all custom theme names (sorted by key).
85+
// Map iteration order is undefined in Go, so we sort for stable Ctrl+T / :themes order.
8486
func GetCustomThemeNames() []string {
8587
names := make([]string, 0, len(customThemes))
8688
for name := range customThemes {
8789
names = append(names, name)
8890
}
91+
sort.Strings(names)
8992
return names
9093
}
9194

client/theme_loader_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestGetCustomThemeNamesSorted(t *testing.T) {
9+
saved := customThemes
10+
t.Cleanup(func() { customThemes = saved })
11+
12+
customThemes = ThemeFile{
13+
"zebra": {Name: "Z"},
14+
"alpha": {Name: "A"},
15+
"moon": {Name: "M"},
16+
}
17+
got := GetCustomThemeNames()
18+
want := []string{"alpha", "moon", "zebra"}
19+
if !reflect.DeepEqual(got, want) {
20+
t.Fatalf("GetCustomThemeNames() = %v, want %v", got, want)
21+
}
22+
23+
all := ListAllThemes()
24+
wantAll := []string{"system", "patriot", "retro", "modern", "alpha", "moon", "zebra"}
25+
if !reflect.DeepEqual(all, wantAll) {
26+
t.Fatalf("ListAllThemes() = %v, want %v", all, wantAll)
27+
}
28+
}

0 commit comments

Comments
 (0)