diff --git a/pkg/modules/avatar/avatar.go b/pkg/modules/avatar/avatar.go
index aefb9d50f..321fb8553 100644
--- a/pkg/modules/avatar/avatar.go
+++ b/pkg/modules/avatar/avatar.go
@@ -18,6 +18,7 @@ package avatar
import (
"code.vikunja.io/api/pkg/log"
+ "code.vikunja.io/api/pkg/modules/avatar/botmarble"
"code.vikunja.io/api/pkg/modules/avatar/empty"
"code.vikunja.io/api/pkg/modules/avatar/gravatar"
"code.vikunja.io/api/pkg/modules/avatar/initials"
@@ -47,6 +48,7 @@ func FlushAllCaches(u *user.User) {
&ldap.Provider{},
&openid.Provider{},
&marble.Provider{},
+ &botmarble.Provider{},
&empty.Provider{},
}
for _, p := range providers {
diff --git a/pkg/modules/avatar/botmarble/botmarble.go b/pkg/modules/avatar/botmarble/botmarble.go
new file mode 100644
index 000000000..03e025ded
--- /dev/null
+++ b/pkg/modules/avatar/botmarble/botmarble.go
@@ -0,0 +1,44 @@
+// Vikunja is a to-do list application to facilitate your life.
+// Copyright 2018-present Vikunja and contributors. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package botmarble
+
+import (
+ "code.vikunja.io/api/pkg/modules/avatar/marble"
+ "code.vikunja.io/api/pkg/user"
+)
+
+// botColors is a cool-toned palette distinct from the marble default so bot avatars are visually recognizable as bots at a glance.
+var botColors = []string{
+ "#3B82F6",
+ "#06B6D4",
+ "#8B5CF6",
+ "#14B8A6",
+ "#6366F1",
+}
+
+// Provider renders marble-style avatars using the bot-specific palette.
+type Provider struct{}
+
+func (p *Provider) GetAvatar(u *user.User, size int64) ([]byte, string, error) {
+ return marble.GenerateSVG(u, size, botColors)
+}
+
+func (p *Provider) AsDataURI(u *user.User, size int64) (string, error) {
+ return marble.GenerateDataURI(u, size, botColors)
+}
+
+func (p *Provider) FlushCache(_ *user.User) error { return nil }
diff --git a/pkg/modules/avatar/marble/marble.go b/pkg/modules/avatar/marble/marble.go
index 2fcad6be6..9f7493253 100644
--- a/pkg/modules/avatar/marble/marble.go
+++ b/pkg/modules/avatar/marble/marble.go
@@ -34,7 +34,7 @@ func (p *Provider) FlushCache(_ *user.User) error { return nil }
const avatarSize = 80
-var colors = []string{
+var defaultColors = []string{
"#A3A948",
"#EDB92E",
"#F85931",
@@ -62,12 +62,12 @@ func getUnit(number int, rang, index int) int {
return value
}
-func getPropsForUser(u *user.User) []*props {
+func getPropsForUser(u *user.User, palette []string) []*props {
ps := []*props{}
- for i := 0; i < 3; i++ {
+ for i := range 3 {
f := float64(getUnit(int(u.ID)*(i+1), avatarSize/10, 0))
ps = append(ps, &props{
- Color: colors[(int(u.ID)+i)%(len(colors)-1)],
+ Color: palette[(int(u.ID)+i)%len(palette)],
TranslateX: getUnit(int(u.ID)*(i+1), avatarSize/10, 1),
TranslateY: getUnit(int(u.ID)*(i+1), avatarSize/10, 2),
Scale: 1.2 + f/10,
@@ -78,13 +78,14 @@ func getPropsForUser(u *user.User) []*props {
return ps
}
-func (p *Provider) GetAvatar(u *user.User, size int64) (avatar []byte, mimeType string, err error) {
+// GenerateSVG renders a marble-style SVG avatar for the given user using the provided palette.
+func GenerateSVG(u *user.User, size int64, palette []string) (avatar []byte, mimeType string, err error) {
s := strconv.FormatInt(size, 10)
avatarSizeStr := strconv.Itoa(avatarSize)
avatarSizeHalf := strconv.Itoa(avatarSize / 2)
- ps := getPropsForUser(u)
+ ps := getPropsForUser(u, palette)
return []byte(`