package main import ( "encoding/json" "net/http" "os" "sort" "strings" ) type treeEntry struct { Name string `json:"name"` Kind string `json:"kind"` } type treeResponse struct { Path string `json:"path"` Entries []treeEntry `json:"entries"` } // handleTree responds with a JSON listing of the immediate children of the // folder at fsPath. Hidden entries and `index.md` are filtered. Files are not // descended — the client lazy-loads children on expand. func (h *handler) handleTree(w http.ResponseWriter, r *http.Request, urlPath, fsPath string) { info, err := os.Stat(fsPath) if err != nil { if os.IsNotExist(err) { http.NotFound(w, r) return } http.Error(w, "stat failed", http.StatusInternalServerError) return } if !info.IsDir() { http.Error(w, "not a folder", http.StatusBadRequest) return } entries, err := listTreeEntries(fsPath) if err != nil { http.Error(w, "read failed", http.StatusInternalServerError) return } resp := treeResponse{Path: canonicalTreePath(urlPath), Entries: entries} w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(resp) } // canonicalTreePath returns the URL path in the form used by the picker: // "/" for root, otherwise stripped of any trailing slash. func canonicalTreePath(urlPath string) string { if urlPath == "" || urlPath == "/" { return "/" } return "/" + strings.Trim(urlPath, "/") } // listTreeEntries returns the immediate children of fsPath, filtering hidden // entries and index.md. Folders are listed before files; both groups are // sorted alphabetically. func listTreeEntries(fsPath string) ([]treeEntry, error) { raw, err := os.ReadDir(fsPath) if err != nil { return nil, err } var folders, files []treeEntry for _, e := range raw { name := e.Name() if strings.HasPrefix(name, ".") { continue } if e.IsDir() { folders = append(folders, treeEntry{Name: name, Kind: "folder"}) } else { if name == "index.md" { continue } files = append(files, treeEntry{Name: name, Kind: "file"}) } } sort.Slice(folders, func(i, j int) bool { return folders[i].Name < folders[j].Name }) sort.Slice(files, func(i, j int) bool { return files[i].Name < files[j].Name }) return append(folders, files...), nil }