feat(ui): tabbed view for root markdown files alongside README (#500) #501

Merged
jmiller merged 1 commits from feat/500-root-file-tabs into dev 2026-06-06 11:50:21 +00:00
4 changed files with 123 additions and 0 deletions
+6
View File
@@ -1004,6 +1004,12 @@
"repo.object_format": "Object Format",
"repo.object_format_helper": "Object format of the repository. Cannot be changed later. SHA1 is most compatible.",
"repo.readme": "README",
"repo.well_known_file.readme": "Readme",
"repo.well_known_file.license": "License",
"repo.well_known_file.contributing": "Contributing",
"repo.well_known_file.code_of_conduct": "Code of Conduct",
"repo.well_known_file.security": "Security",
"repo.well_known_file.changelog": "Changelog",
"repo.readme_helper": "Select a README file template.",
"repo.readme_helper_desc": "This is the place where you can write a complete description for your project.",
"repo.auto_init": "Initialize Repository (Adds .gitignore, License and README)",
+45
View File
@@ -151,6 +151,51 @@ func prepareToRenderDirectory(ctx *context.Context) {
return
}
// Well-known file tabs — only at root
activeTab := ctx.FormString("tab")
if ctx.Repo.TreePath == "" {
wellKnownTabs := findWellKnownFiles(entries)
// Determine which tab is active
hasReadme := readmeFile != nil
if hasReadme || len(wellKnownTabs) > 0 {
// Only show tabs if there are at least 2 items (README + at least one well-known file)
if hasReadme && len(wellKnownTabs) > 0 {
readmeTab := WellKnownFileTab{
TabKey: "readme",
Label: "repo.well_known_file.readme",
FileName: "",
Active: activeTab == "" || activeTab == "readme",
}
if readmeFile != nil {
readmeTab.FileName = readmeFile.Name()
}
// Set active state for well-known tabs
for i := range wellKnownTabs {
wellKnownTabs[i].Active = wellKnownTabs[i].TabKey == activeTab
}
allTabs := append([]WellKnownFileTab{readmeTab}, wellKnownTabs...)
ctx.Data["WellKnownFileTabs"] = allTabs
}
}
// If a non-readme tab is selected, render that file instead
if activeTab != "" && activeTab != "readme" {
for _, tab := range wellKnownTabs {
if tab.TabKey == activeTab {
entry := findFileEntryByName(entries, tab.FileName)
if entry != nil {
prepareToRenderReadmeFile(ctx, "", entry)
return
}
}
}
// If the requested tab was not found, fall through to render readme
}
}
prepareToRenderReadmeFile(ctx, subfolder, readmeFile)
}
+57
View File
@@ -141,6 +141,63 @@ func localizedExtensions(ext, languageCode string) (localizedExts []string) {
return []string{lowerLangCode + ext, ext}
}
// WellKnownFileTab represents a tab for a well-known root file (LICENSE, CONTRIBUTING, etc.)
type WellKnownFileTab struct {
TabKey string // query parameter value, e.g. "license"
Label string // locale key suffix, e.g. "repo.well_known_file.license"
FileName string // actual file name found in repo, e.g. "LICENSE.md"
Active bool // whether this tab is currently selected
}
// wellKnownFilePatterns maps tab keys to the base file names to search for (case-insensitive).
// Order defines the tab display order.
var wellKnownFilePatterns = []struct {
TabKey string
BaseName string // matched case-insensitively against file names (without extension)
}{
{"license", "LICENSE"},
{"contributing", "CONTRIBUTING"},
{"code_of_conduct", "CODE_OF_CONDUCT"},
{"security", "SECURITY"},
{"changelog", "CHANGELOG"},
}
// findWellKnownFiles scans root directory entries for well-known markdown/text files.
func findWellKnownFiles(entries []*git.TreeEntry) []WellKnownFileTab {
var tabs []WellKnownFileTab
for _, pattern := range wellKnownFilePatterns {
for _, entry := range entries {
if entry.IsDir() || entry.IsSubModule() {
continue
}
name := entry.Name()
baseName := name
if idx := strings.LastIndex(name, "."); idx >= 0 {
baseName = name[:idx]
}
if strings.EqualFold(baseName, pattern.BaseName) {
tabs = append(tabs, WellKnownFileTab{
TabKey: pattern.TabKey,
Label: "repo.well_known_file." + pattern.TabKey,
FileName: name,
})
break // take the first match for this pattern
}
}
}
return tabs
}
// findFileEntryByName finds a tree entry by exact file name.
func findFileEntryByName(entries []*git.TreeEntry, fileName string) *git.TreeEntry {
for _, entry := range entries {
if entry.Name() == fileName {
return entry
}
}
return nil
}
func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) {
if readmeFile == nil {
return
+15
View File
@@ -130,6 +130,21 @@
{{template "repo/code/upstream_diverging_info" .}}
{{end}}
{{template "repo/view_list" .}}
{{if .WellKnownFileTabs}}
<div class="ui top attached tabular menu well-known-file-tabs">
{{range .WellKnownFileTabs}}
<a class="{{if .Active}}active {{end}}item" href="?tab={{.TabKey}}">
{{if eq .TabKey "readme"}}{{svg "octicon-book" 16 "tw-mr-1"}}{{end -}}
{{if eq .TabKey "license"}}{{svg "octicon-law" 16 "tw-mr-1"}}{{end -}}
{{if eq .TabKey "contributing"}}{{svg "octicon-heart" 16 "tw-mr-1"}}{{end -}}
{{if eq .TabKey "code_of_conduct"}}{{svg "octicon-people" 16 "tw-mr-1"}}{{end -}}
{{if eq .TabKey "security"}}{{svg "octicon-shield" 16 "tw-mr-1"}}{{end -}}
{{if eq .TabKey "changelog"}}{{svg "octicon-history" 16 "tw-mr-1"}}{{end -}}
{{ctx.Locale.Tr .Label}}
</a>
{{end}}
</div>
{{end}}
{{if and .ReadmeExist (or .RenderAsMarkup .IsPlainText)}}
{{template "repo/view_file" .}}
{{end}}