Skip to content

Commit 9300146

Browse files
committed
add support for custom emoji
1 parent e36716a commit 9300146

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+869
-194
lines changed

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,26 @@ k9s:
11731173
11741174
---
11751175
1176+
## Custom Emoji
1177+
1178+
K9s supports customizable emoji palettes. You can create your own emoji sets to customize the look and feel of various UI elements.
1179+
1180+
To use custom emoji:
1181+
1182+
1. Create an emoji YAML file in the `$XDG_DATA_HOME/k9s/skins/emoji/` directory (e.g., `example.yaml`)
1183+
2. In your skin configuration, specify the emoji palette to use:
1184+
1185+
```yaml
1186+
k9s:
1187+
# Other skin settings...
1188+
emoji:
1189+
palette: example # Use the example.yaml emoji palette
1190+
```
1191+
1192+
See the `skins/emoji/example.yaml` file in this repository for a complete example of an emoji configuration.
1193+
1194+
---
1195+
11761196
## Contributors
11771197
11781198
Without the contributions from these fine folks, this project would be a total dud!

internal/config/files.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ var (
4646
//go:embed templates/stock-skin.yaml
4747
// stockSkinTpl tracks stock skin template
4848
stockSkinTpl []byte
49+
50+
//go:embed templates/stock-emoji.yaml
51+
// emojiTpl tracks emoji template
52+
emojiTpl []byte
4953
)
5054

5155
var (
@@ -55,6 +59,9 @@ var (
5559
// AppSkinsDir tracks skins data directory.
5660
AppSkinsDir string
5761

62+
// AppEmojiDir tracks emoji data directory.
63+
AppEmojiDir string
64+
5865
// AppBenchmarksDir tracks benchmarks results directory.
5966
AppBenchmarksDir string
6067

@@ -146,6 +153,13 @@ func initK9sEnvLocs() error {
146153
slogs.Error, err,
147154
)
148155
}
156+
AppEmojiDir = filepath.Join(AppSkinsDir, "emoji")
157+
if err := data.EnsureFullPath(AppEmojiDir, data.DefaultDirMod); err != nil {
158+
slog.Warn("Unable to create emoji dir",
159+
slogs.Dir, AppEmojiDir,
160+
slogs.Error, err,
161+
)
162+
}
149163
AppContextsDir = filepath.Join(AppConfigDir, "clusters")
150164
if err := data.EnsureFullPath(AppContextsDir, data.DefaultDirMod); err != nil {
151165
slog.Warn("Unable to create clusters dir",
@@ -186,6 +200,11 @@ func initXDGLocs() error {
186200
slog.Warn("No skins dir detected", slogs.Error, err)
187201
}
188202

203+
AppEmojiDir = filepath.Join(AppSkinsDir, "emoji")
204+
if err := data.EnsureFullPath(AppEmojiDir, data.DefaultDirMod); err != nil {
205+
slog.Warn("No emoji dir detected", slogs.Error, err)
206+
}
207+
189208
AppDumpsDir, err = xdg.StateFile(filepath.Join(AppName, "screen-dumps"))
190209
if err != nil {
191210
return err
@@ -292,11 +311,20 @@ func EnsureHotkeysCfgFile() (string, error) {
292311
return f, nil
293312
}
294313

295-
// SkinFileFromName generate skin file path from spec.
314+
// SkinFileFromName generates skin file path from spec.
296315
func SkinFileFromName(n string) string {
297316
if n == "" {
298317
n = "stock"
299318
}
300319

301320
return filepath.Join(AppSkinsDir, n+".yaml")
302321
}
322+
323+
// EmojiPaletteFileFromName generates emoji file path from spec.
324+
func EmojiPaletteFileFromName(n string) string {
325+
if n == "" {
326+
return n
327+
}
328+
329+
return filepath.Join(AppEmojiDir, n+".yaml")
330+
}

internal/config/files_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,32 @@ func TestSkinFileFromName(t *testing.T) {
121121
})
122122
}
123123
}
124+
125+
func TestEmojiPaletteFileFromName(t *testing.T) {
126+
config.AppEmojiDir = "/tmp/k9s-test/skins/emoji"
127+
128+
uu := map[string]struct {
129+
n string
130+
e string
131+
}{
132+
"empty": {
133+
n: "",
134+
e: "",
135+
},
136+
"happy": {
137+
n: "example",
138+
e: "/tmp/k9s-test/skins/emoji/example.yaml",
139+
},
140+
"with-special-chars": {
141+
n: "test-emoji_123",
142+
e: "/tmp/k9s-test/skins/emoji/test-emoji_123.yaml",
143+
},
144+
}
145+
146+
for k := range uu {
147+
u := uu[k]
148+
t.Run(k, func(t *testing.T) {
149+
assert.Equal(t, u.e, config.EmojiPaletteFileFromName(u.n))
150+
})
151+
}
152+
}
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "K9s emoji schema",
4+
"type": "object",
5+
"additionalProperties": true,
6+
"properties": {
7+
"k9s": {
8+
"type": "object",
9+
"additionalProperties": false,
10+
"properties": {
11+
"system": {
12+
"type": "object",
13+
"properties": {
14+
"log_stream_cancelled": {"type": "string"},
15+
"new_version": {"type": "string"},
16+
"default": {"type": "string"},
17+
"locked": {"type": "string"},
18+
"unlocked": {"type": "string"}
19+
}
20+
},
21+
"prompt": {
22+
"type": "object",
23+
"properties": {
24+
"query": {"type": "string"},
25+
"filter": {"type": "string"}
26+
}
27+
},
28+
"status": {
29+
"type": "object",
30+
"properties": {
31+
"info": {"type": "string"},
32+
"warn": {"type": "string"},
33+
"error": {"type": "string"}
34+
}
35+
},
36+
"xray": {
37+
"type": "object",
38+
"properties": {
39+
"namespaces": {"type": "string"},
40+
"default_gvr": {"type": "string"},
41+
"nodes": {"type": "string"},
42+
"pods": {"type": "string"},
43+
"services": {"type": "string"},
44+
"service_accounts": {"type": "string"},
45+
"persistent_volumes": {"type": "string"},
46+
"persistent_volume_claims": {"type": "string"},
47+
"secrets": {"type": "string"},
48+
"horizontal_pod_autoscalers": {"type": "string"},
49+
"config_maps": {"type": "string"},
50+
"deployments": {"type": "string"},
51+
"stateful_sets": {"type": "string"},
52+
"daemon_sets": {"type": "string"},
53+
"replica_sets": {"type": "string"},
54+
"cluster_roles": {"type": "string"},
55+
"roles": {"type": "string"},
56+
"network_policies": {"type": "string"},
57+
"pod_disruption_budgets": {"type": "string"},
58+
"pod_security_policies": {"type": "string"},
59+
"containers": {"type": "string"},
60+
"report": {"type": "string"},
61+
"issue_0": {"type": "string"},
62+
"issue_1": {"type": "string"},
63+
"issue_2": {"type": "string"},
64+
"issue_3": {"type": "string"}
65+
}
66+
}
67+
}
68+
}
69+
}
70+
}

internal/config/json/schemas/skin.json

+6
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@
178178
}
179179
}
180180
}
181+
},
182+
"emoji": {
183+
"type": "object",
184+
"properties": {
185+
"palette": {"type": "string"}
186+
}
181187
}
182188
}
183189
}

internal/config/json/validator.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import (
88
_ "embed"
99
"errors"
1010
"fmt"
11-
"log/slog"
12-
"slices"
13-
1411
"github.com/derailed/k9s/internal/slogs"
1512
"github.com/xeipuuv/gojsonschema"
1613
"gopkg.in/yaml.v3"
14+
"log/slog"
15+
"slices"
1716
)
1817

1918
const (
@@ -43,6 +42,9 @@ const (
4342

4443
// SkinSchema describes skin config schema.
4544
SkinSchema = "skin.json"
45+
46+
// EmojiSchema describes emoji config schema.
47+
EmojiSchema = "emoji.json"
4648
)
4749

4850
var (
@@ -72,6 +74,9 @@ var (
7274

7375
//go:embed schemas/skin.json
7476
skinSchema string
77+
78+
//go:embed schemas/emoji.json
79+
emojiSchema string
7580
)
7681

7782
// Validator tracks schemas validation.
@@ -93,6 +98,7 @@ func NewValidator() *Validator {
9398
PluginMultiSchema: gojsonschema.NewStringLoader(pluginMultiSchema),
9499
HotkeysSchema: gojsonschema.NewStringLoader(hotkeysSchema),
95100
SkinSchema: gojsonschema.NewStringLoader(skinSchema),
101+
EmojiSchema: gojsonschema.NewStringLoader(emojiSchema),
96102
},
97103
}
98104
v.register()

0 commit comments

Comments
 (0)