feat(ui): tabbed view for root markdown files alongside README (#500) #501
@@ -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)",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}}
|
||||
|
||||
Reference in New Issue
Block a user