feat: support folder index pages

This commit is contained in:
2026-04-29 09:36:52 -04:00
parent 8c454de809
commit d371c3c602
7 changed files with 223 additions and 21 deletions

View File

@@ -60,6 +60,8 @@ type browserItem struct {
Path string
URL string
IsFolder bool
IsIndex bool
HasIndex bool
Active bool
}
@@ -82,12 +84,16 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
})
}
func (s *Server) handleDocsIndexRedirect(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusFound)
func (s *Server) handleDocsIndex(w http.ResponseWriter, r *http.Request) {
s.renderDocumentPage(w, r, "")
}
func (s *Server) handleDocument(w http.ResponseWriter, r *http.Request) {
pagePath := chi.URLParam(r, "*")
s.renderDocumentPage(w, r, pagePath)
}
func (s *Server) renderDocumentPage(w http.ResponseWriter, r *http.Request, pagePath string) {
page, err := s.documents.LoadPage(r.Context(), pagePath)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
@@ -271,9 +277,13 @@ func writeJSONWithStatus(w http.ResponseWriter, status int, payload any) {
func buildBrowser(records []docs.DocumentRecord, activePath string) browserData {
paths := make([]string, 0, len(records))
titleByPath := make(map[string]string, len(records))
indexByFolder := make(map[string]string)
for _, record := range records {
paths = append(paths, record.Path)
titleByPath[record.Path] = record.Title
if folder, ok := indexFolder(record.Path); ok {
indexByFolder[folder] = record.Path
}
}
prefixes := []string{""}
@@ -292,7 +302,7 @@ func buildBrowser(records []docs.DocumentRecord, activePath string) browserData
for _, prefix := range prefixes {
column := browserColumn{
Title: columnTitle(prefix),
Items: buildBrowserItems(paths, titleByPath, prefix, activePath),
Items: buildBrowserItems(paths, titleByPath, indexByFolder, prefix, activePath),
}
if len(column.Items) > 0 {
columns = append(columns, column)
@@ -302,7 +312,7 @@ func buildBrowser(records []docs.DocumentRecord, activePath string) browserData
return browserData{Columns: columns}
}
func buildBrowserItems(paths []string, titleByPath map[string]string, prefix string, activePath string) []browserItem {
func buildBrowserItems(paths []string, titleByPath map[string]string, indexByFolder map[string]string, prefix string, activePath string) []browserItem {
seenFolders := make(map[string]browserItem)
files := make([]browserItem, 0)
prefixWithSlash := ""
@@ -311,6 +321,10 @@ func buildBrowserItems(paths []string, titleByPath map[string]string, prefix str
}
for _, path := range paths {
if shouldHideIndexFile(path, prefix) {
continue
}
if prefix != "" && !strings.HasPrefix(path, prefixWithSlash) {
continue
}
@@ -326,21 +340,28 @@ func buildBrowserItems(paths []string, titleByPath map[string]string, prefix str
if prefix != "" {
folderPath = prefix + "/" + name
}
indexPath, hasIndex := indexByFolder[folderPath]
url := "/?folder=" + folderPath
if hasIndex {
url = "/docs/" + strings.TrimSuffix(indexPath, "/index.md")
}
seenFolders[folderPath] = browserItem{
Name: name,
Name: displayFolderName(name, indexPath, titleByPath),
Path: folderPath,
URL: "/?folder=" + folderPath,
URL: url,
IsFolder: true,
HasIndex: hasIndex,
Active: activePath == folderPath || strings.HasPrefix(activePath, folderPath+"/"),
}
continue
}
files = append(files, browserItem{
Name: displayDocumentName(path, titleByPath[path]),
Path: path,
URL: "/docs/" + strings.TrimSuffix(path, ".md"),
Active: activePath == path,
Name: displayDocumentName(path, titleByPath[path]),
Path: path,
URL: "/docs/" + strings.TrimSuffix(path, ".md"),
IsIndex: path == "index.md" || strings.HasSuffix(path, "/index.md"),
Active: activePath == path,
})
}
@@ -372,3 +393,34 @@ func displayDocumentName(path string, title string) string {
}
return strings.TrimSuffix(filepath.Base(path), ".md")
}
func displayFolderName(name string, indexPath string, titleByPath map[string]string) string {
if indexPath == "" {
return name
}
if title := titleByPath[indexPath]; title != "" && title != "Untitled" {
return title
}
return name
}
func indexFolder(path string) (string, bool) {
if path == "index.md" {
return "", true
}
if !strings.HasSuffix(path, "/index.md") {
return "", false
}
return strings.TrimSuffix(path, "/index.md"), true
}
func shouldHideIndexFile(path string, prefix string) bool {
if path == "index.md" {
return false
}
if !strings.HasSuffix(path, "/index.md") {
return false
}
folder := strings.TrimSuffix(path, "/index.md")
return folder == prefix
}