This commit is contained in:
Kerwin Bryant 2025-04-13 06:43:44 -03:00 committed by GitHub
commit 65bdbc6ef9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 183 additions and 90 deletions

View File

@ -10,17 +10,51 @@ import (
"code.gitea.io/gitea/modules/svg"
)
func BasicThemeIcon(entry *git.TreeEntry) template.HTML {
type FileEntry struct {
Name string
EntryMode git.EntryMode
FollowEntryMode git.EntryMode
}
func GetFileEntryByTreeEntry(entry *git.TreeEntry) *FileEntry {
if entry.IsLink() {
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
return &FileEntry{
Name: entry.Name(),
EntryMode: entry.Mode(),
FollowEntryMode: te.Mode(),
}
}
}
return &FileEntry{
Name: entry.Name(),
EntryMode: entry.Mode(),
}
}
func BasicThemeFolderIconName(isOpen bool) string {
if isOpen {
return "octicon-file-directory-open-fill"
}
return "octicon-file-directory-fill"
}
func BasicThemeFolderIconWithOpenStatus(isOpen bool) template.HTML {
return svg.RenderHTML(BasicThemeFolderIconName(isOpen))
}
func BasicThemeIconWithOpenStatus(entry *FileEntry, isOpen bool) template.HTML {
// TODO: add "open icon" support
svgName := "octicon-file"
switch {
case entry.IsLink():
case entry.EntryMode.IsLink():
svgName = "octicon-file-symlink-file"
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.FollowEntryMode.IsDir() {
svgName = "octicon-file-directory-symlink"
}
case entry.IsDir():
svgName = "octicon-file-directory-fill"
case entry.IsSubModule():
case entry.EntryMode.IsDir():
svgName = BasicThemeFolderIconName(isOpen)
case entry.EntryMode.IsSubModule():
svgName = "octicon-file-submodule"
}
return svg.RenderHTML(svgName)

View File

@ -9,7 +9,6 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
@ -69,41 +68,51 @@ func (m *MaterialIconProvider) renderFileIconSVG(p *RenderedIconPool, name, svg,
}
svgID := "svg-mfi-" + name
svgCommonAttrs := `class="svg git-entry-icon ` + extraClass + `" width="16" height="16" aria-hidden="true"`
svgHTML := template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
if p == nil {
return svgHTML
}
if p.IconSVGs[svgID] == "" {
p.IconSVGs[svgID] = template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
p.IconSVGs[svgID] = svgHTML
}
return template.HTML(`<svg ` + svgCommonAttrs + `><use xlink:href="#` + svgID + `"></use></svg>`)
}
func (m *MaterialIconProvider) FileIcon(p *RenderedIconPool, entry *git.TreeEntry) template.HTML {
func (m *MaterialIconProvider) FolderIconWithOpenStatus(p *RenderedIconPool, isOpen bool) template.HTML {
name := m.FindIconName("folder", true, isOpen)
return m.renderFileIconSVG(p, name, m.svgs[name], BasicThemeFolderIconName(isOpen))
}
func (m *MaterialIconProvider) FileIconWithOpenStatus(p *RenderedIconPool, entry *FileEntry, isOpen bool) template.HTML {
if m.rules == nil {
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}
if entry.IsLink() {
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.EntryMode.IsLink() {
if entry.FollowEntryMode.IsDir() {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
return svg.RenderHTML("material-folder-symlink", 16, "octicon-file-directory-symlink")
}
return svg.RenderHTML("octicon-file-symlink-file") // TODO: find some better icons for them
}
name := m.findIconNameByGit(entry)
// TODO: add "open icon" support
name := m.findIconNameByGit(entry, isOpen)
// the material icon pack's "folder" icon doesn't look good, so use our built-in one
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
if iconSVG, ok := m.svgs[name]; ok && name != "folder" && iconSVG != "" {
if iconSVG, ok := m.svgs[name]; ok && iconSVG != "" {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
extraClass := "octicon-file"
switch {
case entry.IsDir():
extraClass = "octicon-file-directory-fill"
case entry.IsSubModule():
case entry.EntryMode.IsDir():
extraClass = BasicThemeFolderIconName(isOpen)
case entry.EntryMode.IsSubModule():
extraClass = "octicon-file-submodule"
}
return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
// TODO: use an interface or wrapper for git.Entry to make the code testable.
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}
func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
@ -118,12 +127,15 @@ func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
return ""
}
func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
func (m *MaterialIconProvider) FindIconName(name string, isDir, isOpen bool) string {
fileNameLower := strings.ToLower(path.Base(name))
if isDir {
if s, ok := m.rules.FolderNames[fileNameLower]; ok {
return s
}
if isOpen {
return "folder-open"
}
return "folder"
}
@ -147,9 +159,9 @@ func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
return "file"
}
func (m *MaterialIconProvider) findIconNameByGit(entry *git.TreeEntry) string {
if entry.IsSubModule() {
func (m *MaterialIconProvider) findIconNameByGit(entry *FileEntry, isOpen bool) string {
if entry.EntryMode.IsSubModule() {
return "folder-git"
}
return m.FindIconName(entry.Name(), entry.IsDir())
return m.FindIconName(entry.Name, entry.EntryMode.IsDir(), isOpen)
}

View File

@ -19,8 +19,8 @@ func TestMain(m *testing.M) {
func TestFindIconName(t *testing.T) {
unittest.PrepareTestEnv(t)
p := fileicon.DefaultMaterialIconProvider()
assert.Equal(t, "php", p.FindIconName("foo.php", false))
assert.Equal(t, "php", p.FindIconName("foo.PHP", false))
assert.Equal(t, "javascript", p.FindIconName("foo.js", false))
assert.Equal(t, "visualstudio", p.FindIconName("foo.vba", false))
assert.Equal(t, "php", p.FindIconName("foo.php", false, false))
assert.Equal(t, "php", p.FindIconName("foo.PHP", false, false))
assert.Equal(t, "javascript", p.FindIconName("foo.js", false, false))
assert.Equal(t, "visualstudio", p.FindIconName("foo.vba", false, false))
}

View File

@ -7,7 +7,6 @@ import (
"html/template"
"strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
@ -34,19 +33,9 @@ func (p *RenderedIconPool) RenderToHTML() template.HTML {
return template.HTML(sb.String())
}
// TODO: use an interface or struct to replace "*git.TreeEntry", to decouple the fileicon module from git module
func RenderEntryIcon(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
func RenderEntryIconWithOpenStatus(renderedIconPool *RenderedIconPool, entry *FileEntry, isOpen bool) template.HTML {
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
return DefaultMaterialIconProvider().FileIconWithOpenStatus(renderedIconPool, entry, isOpen)
}
return BasicThemeIcon(entry)
}
func RenderEntryIconOpen(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
// TODO: add "open icon" support
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
}
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}

View File

@ -30,6 +30,31 @@ func (e EntryMode) String() string {
return strconv.FormatInt(int64(e), 8)
}
// IsSubModule if the entry is a sub module
func (e EntryMode) IsSubModule() bool {
return e == EntryModeCommit
}
// IsDir if the entry is a sub dir
func (e EntryMode) IsDir() bool {
return e == EntryModeTree
}
// IsLink if the entry is a symlink
func (e EntryMode) IsLink() bool {
return e == EntryModeSymlink
}
// IsRegular if the entry is a regular file
func (e EntryMode) IsRegular() bool {
return e == EntryModeBlob
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (e EntryMode) IsExecutable() bool {
return e == EntryModeExec
}
func ParseEntryMode(mode string) (EntryMode, error) {
switch mode {
case "000000":

View File

@ -59,27 +59,27 @@ func (te *TreeEntry) Size() int64 {
// IsSubModule if the entry is a sub module
func (te *TreeEntry) IsSubModule() bool {
return te.entryMode == EntryModeCommit
return te.entryMode.IsSubModule()
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.entryMode == EntryModeTree
return te.entryMode.IsDir()
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.entryMode == EntryModeSymlink
return te.entryMode.IsLink()
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.entryMode == EntryModeBlob
return te.entryMode.IsRegular()
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.entryMode == EntryModeExec
return te.entryMode.IsExecutable()
}
// Blob returns the blob object the entry

View File

@ -59,11 +59,12 @@ func NewFuncMap() template.FuncMap {
// -----------------------------------------------------------------
// svg / avatar / icon / color
"svg": svg.RenderHTML,
"MigrationIcon": migrationIcon,
"ActionIcon": actionIcon,
"SortArrow": sortArrow,
"ContrastColor": util.ContrastColor,
"svg": svg.RenderHTML,
"folderIconHTMLByOpenStatus": FolderIconHTMLByOpenStatus,
"MigrationIcon": migrationIcon,
"ActionIcon": actionIcon,
"SortArrow": sortArrow,
"ContrastColor": util.ContrastColor,
// -----------------------------------------------------------------
// time / number / format

View File

@ -14,11 +14,13 @@ import (
activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
"github.com/editorconfig/editorconfig-core-go/v2"
@ -192,3 +194,10 @@ func tabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
}
return "tab-size-4"
}
func FolderIconHTMLByOpenStatus(isOpen bool) template.HTML {
if setting.UI.FileIconTheme == "material" {
return fileicon.DefaultMaterialIconProvider().FolderIconWithOpenStatus(nil, isOpen)
}
return fileicon.BasicThemeFolderIconWithOpenStatus(isOpen)
}

View File

@ -21,6 +21,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
@ -370,7 +371,11 @@ func Diff(ctx *context.Context) {
return
}
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, nil)
renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(renderedIconPool, diffTree, nil)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)

View File

@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
csv_module "code.gitea.io/gitea/modules/csv"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
@ -639,7 +640,11 @@ func PrepareCompareDiff(
return false
}
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, nil)
renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(renderedIconPool, diffTree, nil)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}
headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID)

View File

@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
issue_template "code.gitea.io/gitea/modules/issue/template"
@ -834,7 +835,11 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}
}
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(diffTree, filesViewedState)
renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFiles"] = transformDiffTreeForUI(renderedIconPool, diffTree, filesViewedState)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}
ctx.Data["Diff"] = diff

View File

@ -4,6 +4,7 @@
package repo
import (
"html/template"
"net/http"
pull_model "code.gitea.io/gitea/models/pull"
@ -63,11 +64,12 @@ type FileDiffFile struct {
IsSubmodule bool
IsViewed bool
Status string
FileIcon template.HTML
}
// transformDiffTreeForUI transforms a DiffTree into a slice of FileDiffFile for UI rendering
// it also takes a map of file names to their viewed state, which is used to mark files as viewed
func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) []FileDiffFile {
func transformDiffTreeForUI(renderedIconPool *fileicon.RenderedIconPool, diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) []FileDiffFile {
files := make([]FileDiffFile, 0, len(diffTree.Files))
for _, file := range diffTree.Files {
@ -81,6 +83,10 @@ func transformDiffTreeForUI(diffTree *gitdiff.DiffTree, filesViewedState map[str
IsSubmodule: isSubmodule,
IsViewed: isViewed,
Status: file.Status,
FileIcon: fileicon.RenderEntryIconWithOpenStatus(renderedIconPool, &fileicon.FileEntry{
Name: file.HeadPath,
EntryMode: file.HeadMode,
}, false),
})
}

View File

@ -257,7 +257,7 @@ func prepareDirectoryFileIcons(ctx *context.Context, files []git.CommitInfo) {
renderedIconPool := fileicon.NewRenderedIconPool()
fileIcons := map[string]template.HTML{}
for _, f := range files {
fileIcons[f.Entry.Name()] = fileicon.RenderEntryIcon(renderedIconPool, f.Entry)
fileIcons[f.Entry.Name()] = fileicon.RenderEntryIconWithOpenStatus(renderedIconPool, fileicon.GetFileEntryByTreeEntry(f.Entry), false)
}
ctx.Data["FileIcons"] = fileIcons
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()

View File

@ -175,9 +175,8 @@ func newTreeViewNodeFromEntry(ctx context.Context, renderedIconPool *fileicon.Re
}
if node.EntryIcon == "" {
node.EntryIcon = fileicon.RenderEntryIcon(renderedIconPool, entry)
// TODO: no open icon support yet
// node.EntryIconOpen = fileicon.RenderEntryIconOpen(renderedIconPool, entry)
node.EntryIcon = fileicon.RenderEntryIconWithOpenStatus(renderedIconPool, fileicon.GetFileEntryByTreeEntry(entry), false)
node.EntryIconOpen = fileicon.RenderEntryIconWithOpenStatus(renderedIconPool, fileicon.GetFileEntryByTreeEntry(entry), true)
}
if node.EntryMode == "commit" {

View File

@ -71,14 +71,18 @@ func TestGetTreeViewNodes(t *testing.T) {
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 xlink:href="#` + id + `"></use></svg>`)
}
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 xlink:href="#` + id + `"></use></svg>`)
}
treeNodes, err := GetTreeViewNodes(ctx, renderedIconPool, ctx.Repo.Commit, "", "")
assert.NoError(t, err)
assert.Equal(t, []*TreeViewNode{
{
EntryName: "docs",
EntryMode: "tree",
FullPath: "docs",
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
EntryName: "docs",
EntryMode: "tree",
FullPath: "docs",
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
},
}, treeNodes)
@ -86,16 +90,18 @@ func TestGetTreeViewNodes(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, []*TreeViewNode{
{
EntryName: "docs",
EntryMode: "tree",
FullPath: "docs",
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
EntryName: "docs",
EntryMode: "tree",
FullPath: "docs",
EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
Children: []*TreeViewNode{
{
EntryName: "README.md",
EntryMode: "blob",
FullPath: "docs/README.md",
EntryIcon: mockIconForFile(`svg-mfi-readme`),
EntryName: "README.md",
EntryMode: "blob",
FullPath: "docs/README.md",
EntryIcon: mockIconForFile(`svg-mfi-readme`),
EntryIconOpen: mockIconForFile(`svg-mfi-readme`),
},
},
},
@ -105,10 +111,11 @@ func TestGetTreeViewNodes(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, []*TreeViewNode{
{
EntryName: "README.md",
EntryMode: "blob",
FullPath: "docs/README.md",
EntryIcon: mockIconForFile(`svg-mfi-readme`),
EntryName: "README.md",
EntryMode: "blob",
FullPath: "docs/README.md",
EntryIcon: mockIconForFile(`svg-mfi-readme`),
EntryIconOpen: mockIconForFile(`svg-mfi-readme`),
},
}, treeNodes)
}

View File

@ -21,7 +21,7 @@
<div class="ui aligned divided list">
{{range $dirI, $dir := .Dirs}}
<div class="item tw-flex tw-items-center">
<span class="tw-flex-1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
<span class="tw-flex-1"> {{folderIconHTMLByOpenStatus false}} {{$dir}}</span>
<div>
<button class="ui button primary show-modal tw-p-2" data-modal="#adopt-unadopted-modal-{{$dirI}}">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</button>
<div class="ui g-modal-confirm modal" id="adopt-unadopted-modal-{{$dirI}}">

View File

@ -60,6 +60,7 @@
{{end}}
<div id="diff-container">
{{if $showFileTree}}
{{$.FileIconPoolHTML}}
<div id="diff-file-tree" class="tw-hidden not-mobile"></div>
<script>
if (diffTreeVisible) document.getElementById('diff-file-tree').classList.remove('tw-hidden');

View File

@ -6,7 +6,7 @@
</div>
{{if .HasParentPath}}
<a class="repo-file-line parent-link silenced" href="{{.BranchLink}}{{if .ParentPath}}{{PathEscapeSegments .ParentPath}}{{end}}">
{{svg "octicon-file-directory-fill"}} ..
{{folderIconHTMLByOpenStatus false}} ..
</a>
{{end}}
{{$.FileIconPoolHTML}}

View File

@ -30,7 +30,7 @@
<span><a href="{{$repo.BaseRepo.Link}}">{{$repo.BaseRepo.OwnerName}}/{{$repo.BaseRepo.Name}}</a></span>
{{end}}
{{else}}
<span class="icon tw-inline-block tw-pt-2">{{svg "octicon-file-directory-fill"}}</span>
<span class="icon tw-inline-block tw-pt-2">{{folderIconHTMLByOpenStatus false}}</span>
<span class="name tw-inline-block tw-pt-2">{{$.ContextUser.Name}}/{{$dir}}</span>
<div class="tw-float-right">
{{if $.allowAdopt}}

View File

@ -2,7 +2,7 @@
import {SvgIcon, type SvgName} from '../svg.ts';
import {diffTreeStore} from '../modules/stores.ts';
import {ref} from 'vue';
import type {Item, File, FileStatus} from '../utils/filetree.ts';
import type {Item, FileStatus} from '../utils/filetree.ts';
defineProps<{
item: Item,
@ -22,13 +22,6 @@ function getIconForDiffStatus(pType: FileStatus) {
};
return diffTypes[pType];
}
function fileIcon(file: File) {
if (file.IsSubmodule) {
return 'octicon-file-submodule';
}
return 'octicon-file';
}
</script>
<template>
@ -39,7 +32,8 @@ function fileIcon(file: File) {
:title="item.name" :href="'#diff-' + item.file.NameHash"
>
<!-- file -->
<SvgIcon :name="fileIcon(item.file)"/>
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="tw-contents" v-html="item.file.FileIcon"/>
<span class="gt-ellipsis tw-flex-1">{{ item.name }}</span>
<SvgIcon
:name="getIconForDiffStatus(item.file.Status).name"
@ -51,10 +45,8 @@ function fileIcon(file: File) {
<div class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
<!-- directory -->
<SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/>
<SvgIcon
class="text primary"
:name="collapsed ? 'octicon-file-directory-fill' : 'octicon-file-directory-open-fill'"
/>
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="tw-contents" v-html="collapsed ? store.folderIcon : store.folderOpenIcon"/>
<span class="gt-ellipsis">{{ item.name }}</span>
</div>

View File

@ -8,6 +8,8 @@ export function diffTreeStore() {
if (!diffTreeStoreReactive) {
diffTreeStoreReactive = reactive({
files: pageData.DiffFiles,
folderIcon: pageData.FolderIcon,
folderOpenIcon: pageData.FolderOpenIcon,
fileTreeIsVisible: false,
selectedItem: '',
});

View File

@ -8,6 +8,7 @@ export type File = {
Status: FileStatus;
IsViewed: boolean;
IsSubmodule: boolean;
FileIcon: string;
}
type DirItem = {