Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Add Catppuccin, Nord, and Solarized as built-in themes

0584bb700c0b144752ddc29764d697551234f862 · Keenan Nemetz · 1d ago · parent c980fe53

Add Catppuccin, Nord, and Solarized as built-in themes

Ship four more Appearance choices: Catppuccin Mocha, Catppuccin Latte, Nord, and Solarized Light. Each is a token block in static/palettes.css, scoped to its data-theme value and loaded after Everforest, before instance theme packs and profile/custom.css.

The ids join theme.js's allowlist and the account picker, and the instance repository's reserved-name check now covers nord and the catppuccin-/solarized- prefixes so a theme pack cannot shadow a built-in. NOTICE and three MIT license files record the palettes.

server/ui_test.go +27−3

@@ -135,9 +135,10 @@ func TestStaticAssetsAreServedFromTheBinary(t *testing.T) {
135 135 }
136 136 }
137 137
138 // Everforest is built in: the shell starts on dark/medium, the account page owns
139 // the picker, and every variant is scoped to the attribute theme.js sets. If the
140 // shell stops naming either file the picker does nothing. The instance
138 // Everforest and the other built-in palettes are scoped to the attribute
139 // theme.js sets. The shell starts on Everforest dark/medium, the account page
140 // owns the picker, and every variant is scoped to the attribute theme.js sets.
141 // If the shell stops naming a sheet the picker does nothing. The instance
141 142 // repository may add its own themes; instance_repo_test.go covers that path.
142 143 func TestEverforestThemesAndAccountPicker(t *testing.T) {
143 144 t.Parallel()
@@ -150,6 +151,7 @@ func TestEverforestThemesAndAccountPicker(t *testing.T) {
150 151 for _, want := range []string{
151 152 `src="/static/theme.js"`,
152 153 `href="/static/everforest.css"`,
154 `href="/static/palettes.css"`,
153 155 `data-theme="everforest-dark-medium"`,
154 156 `data-font-scale="medium"`,
155 157 } {
@@ -187,6 +189,10 @@ func TestEverforestThemesAndAccountPicker(t *testing.T) {
187 189 "everforest-light-hard",
188 190 "everforest-light-medium",
189 191 "everforest-light-soft",
192 "catppuccin-mocha",
193 "catppuccin-latte",
194 "nord",
195 "solarized-light",
190 196 } {
191 197 if !strings.Contains(account, `value="`+variant+`"`) {
192 198 t.Errorf("the theme picker does not offer %s", variant)
@@ -216,6 +222,24 @@ func TestEverforestThemesAndAccountPicker(t *testing.T) {
216 222 t.Fatal("the Everforest sheet has an unscoped body rule and would restyle the fallback")
217 223 }
218 224
225 code, palettes := f.get("/static/palettes.css")
226 if code != 200 {
227 t.Fatalf("/static/palettes.css = %d", code)
228 }
229 for _, variant := range []string{
230 "catppuccin-mocha",
231 "catppuccin-latte",
232 "nord",
233 "solarized-light",
234 } {
235 if !strings.Contains(palettes, `data-theme="`+variant+`"`) {
236 t.Errorf("the palette sheet does not define %s", variant)
237 }
238 }
239 if strings.Contains(palettes, "\nbody {") {
240 t.Fatal("the palette sheet has an unscoped body rule and would restyle the fallback")
241 }
242
219 243 code, base := f.get("/static/vole.css")
220 244 if code != 200 {
221 245 t.Fatalf("/static/vole.css = %d", code)