From ad18b17927c76f052d64313466dc7c117ff7719d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 9 Jul 2025 00:04:21 +0300 Subject: feat: add SVG image support and experimental project detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix image extraction regex to handle unquoted HTML img src attributes - Add detection of version tags and experimental status for projects - Display "Experimental (no releases yet)" for projects without tags - Successfully extracts SVG images from projects like ior ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- internal/showcase/images.go | 11 ++++++----- internal/showcase/metadata.go | 34 ++++++++++++++++++++++++++++++++++ internal/showcase/showcase.go | 5 +++++ 3 files changed, 45 insertions(+), 5 deletions(-) (limited to 'internal/showcase') diff --git a/internal/showcase/images.go b/internal/showcase/images.go index 0fa37f7..66372cb 100644 --- a/internal/showcase/images.go +++ b/internal/showcase/images.go @@ -125,7 +125,8 @@ func extractImageReferences(content string) []string { // Regex patterns for markdown images patterns := []string{ `!\[([^\]]*)\]\(([^)]+)\)`, // ![alt](url) - `]+src=["']([^"']+)["'][^>]*>`, // + `]+src=["']([^"']+)["'][^>]*>`, // with quotes + `]+src=([^\s>]+)[^>]*>`, // without quotes `!\[([^\]]*)\]\[([^\]]+)\]`, // ![alt][ref] `\[([^\]]+)\]:\s*(.+?)(?:\s+"[^"]+")?\s*$`, // [ref]: url "title" } @@ -133,7 +134,7 @@ func extractImageReferences(content string) []string { fmt.Printf("DEBUG: Content length: %d bytes\n", len(content)) // Extract from markdown image syntax - for i, pattern := range patterns[:2] { // First two patterns have URLs in different positions + for i, pattern := range patterns[:3] { // First three patterns have URLs in different positions re := regexp.MustCompile(pattern) matches := re.FindAllStringSubmatch(content, -1) fmt.Printf("DEBUG: Pattern %d (%s) found %d matches\n", i, pattern, len(matches)) @@ -143,7 +144,7 @@ func extractImageReferences(content string) []string { if pattern == patterns[0] { url = match[2] // For ![alt](url) } else { - url = match[1] // For + url = match[1] // For (both with and without quotes) } // Clean and validate URL @@ -182,7 +183,7 @@ func extractImageReferences(content string) []string { } // Handle reference-style images - refPattern := regexp.MustCompile(patterns[3]) + refPattern := regexp.MustCompile(patterns[4]) refMatches := refPattern.FindAllStringSubmatch(content, -1) refs := make(map[string]string) for _, match := range refMatches { @@ -190,7 +191,7 @@ func extractImageReferences(content string) []string { } // Find reference-style image uses - refUsePattern := regexp.MustCompile(patterns[2]) + refUsePattern := regexp.MustCompile(patterns[3]) refUseMatches := refUsePattern.FindAllStringSubmatch(content, -1) for _, match := range refUseMatches { ref := match[2] diff --git a/internal/showcase/metadata.go b/internal/showcase/metadata.go index 13caf9f..9627910 100644 --- a/internal/showcase/metadata.go +++ b/internal/showcase/metadata.go @@ -28,6 +28,8 @@ type RepoMetadata struct { LastCommitDate string License string AvgCommitAge float64 // Average age of last 42 commits in days + LatestTag string // Latest version tag (empty if no tags) + HasReleases bool // Whether the project has any releases/tags } // extractRepoMetadata extracts metadata from a repository @@ -86,6 +88,14 @@ func extractRepoMetadata(repoPath string) (*RepoMetadata, error) { } metadata.AvgCommitAge = avgAge + // Get latest tag and check for releases + latestTag, hasReleases, err := getLatestTag(repoPath) + if err != nil { + fmt.Printf("Warning: Failed to get latest tag: %v\n", err) + } + metadata.LatestTag = latestTag + metadata.HasReleases = hasReleases + return metadata, nil } @@ -268,4 +278,28 @@ func getAverageCommitAge(repoPath string, commitCount int) (float64, error) { } return totalAge / float64(validCommits), nil +} + +// getLatestTag returns the latest git tag and whether the repo has any releases +func getLatestTag(repoPath string) (string, bool, error) { + // First try to get tags sorted by version + cmd := exec.Command("git", "-C", repoPath, "tag", "-l", "--sort=-version:refname") + output, err := cmd.Output() + if err != nil { + // Fallback to describe + cmd = exec.Command("git", "-C", repoPath, "describe", "--tags", "--abbrev=0") + output, err = cmd.Output() + if err != nil { + // No tags at all + return "", false, nil + } + } + + tags := strings.Split(strings.TrimSpace(string(output)), "\n") + if len(tags) == 0 || tags[0] == "" { + return "", false, nil + } + + // Return the latest tag + return tags[0], true, nil } \ No newline at end of file diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go index 0bce7ee..8f8592d 100644 --- a/internal/showcase/showcase.go +++ b/internal/showcase/showcase.go @@ -435,6 +435,11 @@ func (g *Generator) formatGemtext(summaries []ProjectSummary) string { builder.WriteString(fmt.Sprintf("* ๐Ÿ”ฅ Recent Activity: %.1f days (avg. age of last 42 commits)\n", summary.Metadata.AvgCommitAge)) builder.WriteString(fmt.Sprintf("* โš–๏ธ License: %s\n", summary.Metadata.License)) + // Add experimental status if no releases + if !summary.Metadata.HasReleases { + builder.WriteString("* ๐Ÿงช Status: Experimental (no releases yet)\n") + } + // Add AI-Assisted notice if detected if summary.AIAssisted { builder.WriteString("* ๐Ÿค– AI-Assisted: This project was partially created with the help of generative AI\n") -- cgit v1.2.3