mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-31 11:49:52 +01:00
Add FOLDER_ICON_THEME configuration option (#36496)
Fixes: https://github.com/go-gitea/gitea/issues/35182 Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: silverwind <115237+silverwind@users.noreply.github.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
7
AGENTS.md
Normal file
7
AGENTS.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Instructions for agents
|
||||
|
||||
- Use `make help` to find available development targets
|
||||
- Before committing go code changes, run `make fmt`
|
||||
- Before committing `go.mod` changes, run `make tidy`
|
||||
- Before committing new `.go` files, add the current year into the copyright header
|
||||
- Before committing files, removed any trailing whitespace
|
||||
@@ -1329,9 +1329,12 @@ LEVEL = Info
|
||||
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
|
||||
;THEMES =
|
||||
;;
|
||||
;; The icons for file list (basic/material), this is a temporary option which will be replaced by a user setting in the future.
|
||||
;; The icon theme for files (basic/material)
|
||||
;FILE_ICON_THEME = material
|
||||
;;
|
||||
;; The icon theme for folders (basic/material)
|
||||
;FOLDER_ICON_THEME = basic
|
||||
;;
|
||||
;; All available reactions users can choose on issues/prs and comments.
|
||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||
|
||||
@@ -34,7 +34,13 @@ func (p *RenderedIconPool) RenderToHTML() template.HTML {
|
||||
}
|
||||
|
||||
func RenderEntryIconHTML(renderedIconPool *RenderedIconPool, entry *EntryInfo) template.HTML {
|
||||
if setting.UI.FileIconTheme == "material" {
|
||||
// Use folder theme for directories and symlinks to directories
|
||||
theme := setting.UI.FileIconTheme
|
||||
if entry.EntryMode.IsDir() || (entry.EntryMode.IsLink() && entry.SymlinkToMode.IsDir()) {
|
||||
theme = setting.UI.FolderIconTheme
|
||||
}
|
||||
|
||||
if theme == "material" {
|
||||
return DefaultMaterialIconProvider().EntryIconHTML(renderedIconPool, entry)
|
||||
}
|
||||
return BasicEntryIconHTML(entry)
|
||||
|
||||
75
modules/fileicon/render_test.go
Normal file
75
modules/fileicon/render_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package fileicon_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/fileicon"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRenderEntryIconHTML_WithDifferentThemes(t *testing.T) {
|
||||
// Test that folder icons use the folder theme
|
||||
t.Run("FolderUsesBasicTheme", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.UI.FileIconTheme, "material")()
|
||||
defer test.MockVariableValue(&setting.UI.FolderIconTheme, "basic")()
|
||||
|
||||
folderEntry := &fileicon.EntryInfo{
|
||||
BaseName: "testfolder",
|
||||
EntryMode: git.EntryModeTree,
|
||||
}
|
||||
|
||||
html := fileicon.RenderEntryIconHTML(nil, folderEntry)
|
||||
// Basic theme renders octicon classes
|
||||
assert.Contains(t, string(html), "octicon-file-directory-fill")
|
||||
})
|
||||
|
||||
t.Run("FileUsesMaterialTheme", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.UI.FileIconTheme, "material")()
|
||||
defer test.MockVariableValue(&setting.UI.FolderIconTheme, "basic")()
|
||||
|
||||
fileEntry := &fileicon.EntryInfo{
|
||||
BaseName: "test.js",
|
||||
EntryMode: git.EntryModeBlob,
|
||||
}
|
||||
|
||||
html := fileicon.RenderEntryIconHTML(nil, fileEntry)
|
||||
// Material theme for files renders material icons
|
||||
assert.Contains(t, string(html), "svg-mfi-")
|
||||
})
|
||||
|
||||
t.Run("SymlinkToFolderUsesBasicTheme", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.UI.FileIconTheme, "material")()
|
||||
defer test.MockVariableValue(&setting.UI.FolderIconTheme, "basic")()
|
||||
|
||||
symlinkEntry := &fileicon.EntryInfo{
|
||||
BaseName: "link",
|
||||
EntryMode: git.EntryModeSymlink,
|
||||
SymlinkToMode: git.EntryModeTree,
|
||||
}
|
||||
|
||||
html := fileicon.RenderEntryIconHTML(nil, symlinkEntry)
|
||||
// Symlinks to folders should use folder theme
|
||||
assert.Contains(t, string(html), "octicon-file-directory-symlink")
|
||||
})
|
||||
|
||||
t.Run("BothMaterialTheme", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.UI.FileIconTheme, "material")()
|
||||
defer test.MockVariableValue(&setting.UI.FolderIconTheme, "material")()
|
||||
|
||||
folderEntry := &fileicon.EntryInfo{
|
||||
BaseName: "testfolder",
|
||||
EntryMode: git.EntryModeTree,
|
||||
}
|
||||
|
||||
html := fileicon.RenderEntryIconHTML(nil, folderEntry)
|
||||
// Material theme for folders renders material folder icons
|
||||
assert.Contains(t, string(html), "svg-mfi-")
|
||||
})
|
||||
}
|
||||
@@ -29,6 +29,7 @@ var UI = struct {
|
||||
DefaultTheme string
|
||||
Themes []string
|
||||
FileIconTheme string
|
||||
FolderIconTheme string
|
||||
Reactions []string
|
||||
ReactionsLookup container.Set[string] `ini:"-"`
|
||||
CustomEmojis []string
|
||||
@@ -88,6 +89,7 @@ var UI = struct {
|
||||
MaxDisplayFileSize: 8388608,
|
||||
DefaultTheme: `gitea-auto`,
|
||||
FileIconTheme: `material`,
|
||||
FolderIconTheme: `basic`,
|
||||
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
||||
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
||||
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
||||
|
||||
@@ -56,6 +56,7 @@ func TestGetTreeBySHA(t *testing.T) {
|
||||
|
||||
func TestGetTreeViewNodes(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
|
||||
ctx, _ := contexttest.MockContext(t, "user2/repo1")
|
||||
ctx.Repo.RefFullName = git.RefNameFromBranch("sub-home-md-img-check")
|
||||
contexttest.LoadRepo(t, ctx, 1)
|
||||
@@ -69,11 +70,13 @@ func TestGetTreeViewNodes(t *testing.T) {
|
||||
mockIconForFile := func(id string) template.HTML {
|
||||
return template.HTML(`<svg class="svg git-entry-icon octicon-file" width="16" height="16" aria-hidden="true"><use href="#` + id + `"></use></svg>`)
|
||||
}
|
||||
mockIconForFolder := func(id string) template.HTML {
|
||||
return template.HTML(`<svg class="svg git-entry-icon octicon-file-directory-fill" width="16" height="16" aria-hidden="true"><use href="#` + id + `"></use></svg>`)
|
||||
mockIconForFolder := func() template.HTML {
|
||||
// With basic theme (default for folders), we get octicon icons without IDs
|
||||
return template.HTML(`<span>octicon-file-directory-fill(16/)</span>`)
|
||||
}
|
||||
mockOpenIconForFolder := func(id string) template.HTML {
|
||||
return template.HTML(`<svg class="svg git-entry-icon octicon-file-directory-open-fill" width="16" height="16" aria-hidden="true"><use href="#` + id + `"></use></svg>`)
|
||||
mockOpenIconForFolder := func() template.HTML {
|
||||
// With basic theme (default for folders), we get octicon icons without IDs
|
||||
return template.HTML(`<span>octicon-file-directory-open-fill(16/)</span>`)
|
||||
}
|
||||
treeNodes, err := GetTreeViewNodes(ctx, curRepoLink, renderedIconPool, ctx.Repo.Commit, "", "")
|
||||
assert.NoError(t, err)
|
||||
@@ -82,8 +85,8 @@ func TestGetTreeViewNodes(t *testing.T) {
|
||||
EntryName: "docs",
|
||||
EntryMode: "tree",
|
||||
FullPath: "docs",
|
||||
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
|
||||
EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
|
||||
EntryIcon: mockIconForFolder(),
|
||||
EntryIconOpen: mockOpenIconForFolder(),
|
||||
},
|
||||
}, treeNodes)
|
||||
|
||||
@@ -94,8 +97,8 @@ func TestGetTreeViewNodes(t *testing.T) {
|
||||
EntryName: "docs",
|
||||
EntryMode: "tree",
|
||||
FullPath: "docs",
|
||||
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
|
||||
EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
|
||||
EntryIcon: mockIconForFolder(),
|
||||
EntryIconOpen: mockOpenIconForFolder(),
|
||||
Children: []*TreeViewNode{
|
||||
{
|
||||
EntryName: "README.md",
|
||||
|
||||
Reference in New Issue
Block a user