Skip to content

Commit 458de74

Browse files
committed
Add icon cache, get icon theme from config
1 parent 0f928b0 commit 458de74

File tree

4 files changed

+50
-21
lines changed

4 files changed

+50
-21
lines changed

custom/conf/app.example.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,9 @@ LEVEL = Info
12621262
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
12631263
;THEMES =
12641264
;;
1265+
;; Icon theme used by the file tree. Icons have to be place in data/icons/<iconpack>/. Default is "".
1266+
;FILE_ICON_THEME =
1267+
;;
12651268
;; All available reactions users can choose on issues/prs and comments.
12661269
;; Values can be emoji alias (:smile:) or a unicode emoji.
12671270
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png

modules/fileicon/icon.go renamed to modules/fileicon/fileicon.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,19 @@ import (
1111
"code.gitea.io/gitea/modules/log"
1212
"code.gitea.io/gitea/modules/setting"
1313
"code.gitea.io/gitea/modules/svg"
14+
lru "github.com/hashicorp/golang-lru/v2"
1415
)
1516

17+
var fileIconCache *lru.Cache[string, string]
18+
19+
func init() {
20+
var err error
21+
fileIconCache, err = lru.New[string, string](1000)
22+
if err != nil {
23+
log.Fatal("Failed to create file icon cache: %v", err)
24+
}
25+
}
26+
1627
func getBasicFileIconName(entry *git.TreeEntry) string {
1728
switch {
1829
case entry.IsLink():
@@ -35,49 +46,53 @@ func getBasicFileIconName(entry *git.TreeEntry) string {
3546
}
3647

3748
func getFileIconNames(entry *git.TreeEntry) []string {
49+
fileName := strings.ToLower(path.Base(entry.Name()))
50+
3851
if entry.IsDir() {
39-
fileName := strings.ToLower(path.Base(entry.Name()))
40-
return []string{fileName, "directory"}
52+
return []string{"folder_" + fileName, "folder"}
4153
}
4254

4355
if entry.IsRegular() {
44-
fileName := strings.ToLower(path.Base(entry.Name()))
4556
ext := strings.ToLower(strings.TrimPrefix(path.Ext(fileName), "."))
46-
return []string{fileName, ext, "file"}
57+
return []string{"file_" + fileName, "file_" + ext, "file"}
4758
}
4859

4960
return nil
5061
}
5162

5263
func loadCustomIcon(iconPath string) (string, error) {
64+
log.Info("Loading custom icon from %s", iconPath)
65+
66+
if icon, ok := fileIconCache.Get(iconPath); ok {
67+
return icon, nil
68+
}
69+
5370
// Try to load the icon from the filesystem
5471
if _, err := os.Stat(iconPath); err != nil {
5572
return "", err
5673
}
5774

58-
// Read the SVG file
5975
iconData, err := os.ReadFile(iconPath)
6076
if err != nil {
6177
return "", err
6278
}
6379

80+
fileIconCache.Add(iconPath, string(iconData))
81+
6482
return string(iconData), nil
6583
}
6684

6785
// FileIcon returns a custom icon from a folder or the default octicon for displaying files/directories
6886
func FileIcon(ctx context.Context, entry *git.TreeEntry) template.HTML {
69-
iconPack, ok := ctx.Value("icon-pack").(string) // TODO: allow user to select an icon pack from a list
70-
iconPack = "demo"
71-
ok = true
72-
73-
if ok && iconPack != "" {
87+
iconTheme := setting.UI.FileIconTheme
88+
if iconTheme != "" {
7489
iconNames := getFileIconNames(entry)
7590

7691
// Try to load the custom icon
7792
for _, iconName := range iconNames {
78-
iconPath := path.Join(setting.AppDataPath, "icons", iconPack, iconName+".svg")
93+
iconPath := path.Join(setting.AppDataPath, "icons", iconTheme, iconName+".svg")
7994
if icon, err := loadCustomIcon(iconPath); err == nil {
80-
return template.HTML(icon)
95+
return svg.RenderHTMLFromString(icon)
8196
}
8297
}
8398
}

modules/setting/ui.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var UI = struct {
2828
DefaultShowFullName bool
2929
DefaultTheme string
3030
Themes []string
31+
FileIconTheme string
3132
Reactions []string
3233
ReactionsLookup container.Set[string] `ini:"-"`
3334
CustomEmojis []string

modules/svg/svg.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,26 @@ func MockIcon(icon string) func() {
6060
func RenderHTML(icon string, others ...any) template.HTML {
6161
size, class := gitea_html.ParseSizeAndClass(defaultSize, "", others...)
6262
if svgStr, ok := svgIcons[icon]; ok {
63-
// the code is somewhat hacky, but it just works, because the SVG contents are all normalized
64-
if size != defaultSize {
65-
svgStr = strings.Replace(svgStr, fmt.Sprintf(`width="%d"`, defaultSize), fmt.Sprintf(`width="%d"`, size), 1)
66-
svgStr = strings.Replace(svgStr, fmt.Sprintf(`height="%d"`, defaultSize), fmt.Sprintf(`height="%d"`, size), 1)
67-
}
68-
if class != "" {
69-
svgStr = strings.Replace(svgStr, `class="`, fmt.Sprintf(`class="%s `, class), 1)
70-
}
71-
return template.HTML(svgStr)
63+
return RenderHTMLFromString(svgStr, size, class)
7264
}
65+
7366
// during test (or something wrong happens), there is no SVG loaded, so use a dummy span to tell that the icon is missing
7467
return template.HTML(fmt.Sprintf("<span>%s(%d/%s)</span>", template.HTMLEscapeString(icon), size, template.HTMLEscapeString(class)))
7568
}
69+
70+
// RenderHTMLFromString renders icons from a string - arguments SVG string (string), size (int), class (string)
71+
func RenderHTMLFromString(svgStr string, others ...any) template.HTML {
72+
svgStr = string(Normalize([]byte(svgStr), defaultSize))
73+
74+
size, class := gitea_html.ParseSizeAndClass(defaultSize, "", others...)
75+
76+
// the code is somewhat hacky, but it just works, because the SVG contents are all normalized
77+
if size != defaultSize {
78+
svgStr = strings.Replace(svgStr, fmt.Sprintf(`width="%d"`, defaultSize), fmt.Sprintf(`width="%d"`, size), 1)
79+
svgStr = strings.Replace(svgStr, fmt.Sprintf(`height="%d"`, defaultSize), fmt.Sprintf(`height="%d"`, size), 1)
80+
}
81+
if class != "" {
82+
svgStr = strings.Replace(svgStr, `class="`, fmt.Sprintf(`class="%s `, class), 1)
83+
}
84+
return template.HTML(svgStr)
85+
}

0 commit comments

Comments
 (0)