All checks were successful
Tests / unit-tests (push) Successful in 51s
- Login sa session cookie autentifikacijom - Lista projekata iz filesystem-a - Chat sa Claude CLI preko WebSocket-a - Streaming NDJSON parsiranje iz CLI stdout-a - Sesija zivi nezavisno od browsera (reconnect replay) - Sidebar sa .md fajlovima i markdown renderovanjem - Dark tema, htmx + Go templates - 47 unit testova Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65 lines
1.3 KiB
Go
65 lines
1.3 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type Project struct {
|
|
Name string
|
|
Path string
|
|
Description string
|
|
HasReadme bool
|
|
}
|
|
|
|
// ListProjects returns a sorted list of projects from the given directory.
|
|
// Only directories are considered projects. Hidden directories (starting with .)
|
|
// are excluded.
|
|
func ListProjects(projectsPath string) ([]Project, error) {
|
|
entries, err := os.ReadDir(projectsPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var projects []Project
|
|
for _, e := range entries {
|
|
if !e.IsDir() {
|
|
continue
|
|
}
|
|
name := e.Name()
|
|
if strings.HasPrefix(name, ".") {
|
|
continue
|
|
}
|
|
|
|
p := Project{
|
|
Name: name,
|
|
Path: filepath.Join(projectsPath, name),
|
|
}
|
|
|
|
// Try to read description from README.md first line
|
|
readmePath := filepath.Join(p.Path, "README.md")
|
|
if data, err := os.ReadFile(readmePath); err == nil {
|
|
p.HasReadme = true
|
|
lines := strings.SplitN(string(data), "\n", 3)
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
line = strings.TrimLeft(line, "# ")
|
|
if line != "" {
|
|
p.Description = line
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
projects = append(projects, p)
|
|
}
|
|
|
|
sort.Slice(projects, func(i, j int) bool {
|
|
return projects[i].Name < projects[j].Name
|
|
})
|
|
|
|
return projects, nil
|
|
}
|