From 16d1ab69b4c6f62dc7dc8e2b41854e861034dcf8 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Wed, 5 Nov 2025 06:24:24 +0000 Subject: [PATCH] appview/ogcard: introduce highlevel helpers to draw icons and assets Change-Id: yvtruzmyvxpswxxunvoxllvoxspqytnp - DrawLucideIcon & DrawDollySilhouette to simplify the svg drawing logic - DrawDollySilhouette now depends on the html template itself, instead of a bespoke svg this changes lets us remove dolly.svg from the fragments. Signed-off-by: oppiliappan --- appview/issues/opengraph.go | 10 +++--- appview/ogcard/card.go | 69 +++++++++++++++++++++++++++++++------ appview/pulls/opengraph.go | 14 ++++---- appview/repo/opengraph.go | 8 ++--- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/appview/issues/opengraph.go b/appview/issues/opengraph.go index d09d547a..315cad2c 100644 --- a/appview/issues/opengraph.go +++ b/appview/issues/opengraph.go @@ -143,11 +143,11 @@ func (rp *Issues) drawIssueSummaryCard(issue *models.Issue, repo *models.Repo, c var statusBgColor color.RGBA if issue.Open { - statusIcon = "static/icons/circle-dot.svg" + statusIcon = "circle-dot" statusText = "open" statusBgColor = color.RGBA{34, 139, 34, 255} // green } else { - statusIcon = "static/icons/circle-dot.svg" + statusIcon = "ban" statusText = "closed" statusBgColor = color.RGBA{52, 58, 64, 255} // dark gray } @@ -155,7 +155,7 @@ func (rp *Issues) drawIssueSummaryCard(issue *models.Issue, repo *models.Repo, c badgeIconSize := 36 // Draw icon with status color (no background) - err = statusCommentsArea.DrawSVGIcon(statusIcon, statsX, statsY+iconBaselineOffset-badgeIconSize/2+5, badgeIconSize, statusBgColor) + err = statusCommentsArea.DrawLucideIcon(statusIcon, statsX, statsY+iconBaselineOffset-badgeIconSize/2+5, badgeIconSize, statusBgColor) if err != nil { log.Printf("failed to draw status icon: %v", err) } @@ -172,7 +172,7 @@ func (rp *Issues) drawIssueSummaryCard(issue *models.Issue, repo *models.Repo, c currentX := statsX + badgeIconSize + 12 + statusTextWidth + 50 // Draw comment count - err = statusCommentsArea.DrawSVGIcon("static/icons/message-square.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statusCommentsArea.DrawLucideIcon("message-square", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw comment icon: %v", err) } @@ -193,7 +193,7 @@ func (rp *Issues) drawIssueSummaryCard(issue *models.Issue, repo *models.Repo, c dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2) dollyY := statsY + iconBaselineOffset - dollySize/2 + 25 dollyColor := color.RGBA{180, 180, 180, 255} // light gray - err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor) + err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor) if err != nil { log.Printf("dolly silhouette not available (this is ok): %v", err) } diff --git a/appview/ogcard/card.go b/appview/ogcard/card.go index 6fd1692e..b8fe1e8f 100644 --- a/appview/ogcard/card.go +++ b/appview/ogcard/card.go @@ -7,6 +7,7 @@ package ogcard import ( "bytes" "fmt" + "html/template" "image" "image/color" "io" @@ -279,13 +280,7 @@ func (c *Card) DrawBoldText(text string, x, y int, textColor color.Color, sizePt return width, nil } -// DrawSVGIcon draws an SVG icon from the embedded files at the specified position -func (c *Card) DrawSVGIcon(svgPath string, x, y, size int, iconColor color.Color) error { - svgData, err := pages.Files.ReadFile(svgPath) - if err != nil { - return fmt.Errorf("failed to read SVG file %s: %w", svgPath, err) - } - +func BuildSVGIconFromData(svgData []byte, iconColor color.Color) (*oksvg.SvgIcon, error) { // Convert color to hex string for SVG rgba, isRGBA := iconColor.(color.RGBA) if !isRGBA { @@ -304,9 +299,65 @@ func (c *Card) DrawSVGIcon(svgPath string, x, y, size int, iconColor color.Color // Parse SVG icon, err := oksvg.ReadIconStream(strings.NewReader(svgString)) if err != nil { - return fmt.Errorf("failed to parse SVG %s: %w", svgPath, err) + return nil, fmt.Errorf("failed to parse SVG: %w", err) + } + + return icon, nil +} + +func BuildSVGIconFromPath(svgPath string, iconColor color.Color) (*oksvg.SvgIcon, error) { + svgData, err := pages.Files.ReadFile(svgPath) + if err != nil { + return nil, fmt.Errorf("failed to read SVG file %s: %w", svgPath, err) + } + + icon, err := BuildSVGIconFromData(svgData, iconColor) + if err != nil { + return nil, fmt.Errorf("failed to build SVG icon %s: %w", svgPath, err) + } + + return icon, nil +} + +func BuildLucideIcon(name string, iconColor color.Color) (*oksvg.SvgIcon, error) { + return BuildSVGIconFromPath(fmt.Sprintf("static/icons/%s.svg", name), iconColor) +} + +func (c *Card) DrawLucideIcon(name string, x, y, size int, iconColor color.Color) error { + icon, err := BuildSVGIconFromPath(fmt.Sprintf("static/icons/%s.svg", name), iconColor) + if err != nil { + return err + } + + c.DrawSVGIcon(icon, x, y, size) + + return nil +} + +func (c *Card) DrawDollySilhouette(x, y, size int, iconColor color.Color) error { + tpl, err := template.New("dolly"). + ParseFS(pages.Files, "templates/fragments/dolly/silhouette.html") + if err != nil { + return fmt.Errorf("failed to read dolly silhouette template: %w", err) + } + + var svgData bytes.Buffer + if err = tpl.ExecuteTemplate(&svgData, "fragments/dolly/silhouette", nil); err != nil { + return fmt.Errorf("failed to execute dolly silhouette template: %w", err) + } + + icon, err := BuildSVGIconFromData(svgData.Bytes(), iconColor) + if err != nil { + return err } + c.DrawSVGIcon(icon, x, y, size) + + return nil +} + +// DrawSVGIcon draws an SVG icon from the embedded files at the specified position +func (c *Card) DrawSVGIcon(icon *oksvg.SvgIcon, x, y, size int) { // Set the icon size w, h := float64(size), float64(size) icon.SetTarget(0, 0, w, h) @@ -334,8 +385,6 @@ func (c *Card) DrawSVGIcon(svgPath string, x, y, size int, iconColor color.Color } draw.Draw(c.Img, destRect, iconImg, image.Point{}, draw.Over) - - return nil } // DrawImage fills the card with an image, scaled to maintain the original aspect ratio and centered with respect to the non-filled dimension diff --git a/appview/pulls/opengraph.go b/appview/pulls/opengraph.go index c273b3ff..c05619ae 100644 --- a/appview/pulls/opengraph.go +++ b/appview/pulls/opengraph.go @@ -146,15 +146,15 @@ func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commen var statusColor color.RGBA if pull.State.IsOpen() { - statusIcon = "static/icons/git-pull-request.svg" + statusIcon = "git-pull-request" statusText = "open" statusColor = color.RGBA{34, 139, 34, 255} // green } else if pull.State.IsMerged() { - statusIcon = "static/icons/git-merge.svg" + statusIcon = "git-merge" statusText = "merged" statusColor = color.RGBA{138, 43, 226, 255} // purple } else { - statusIcon = "static/icons/git-pull-request-closed.svg" + statusIcon = "git-pull-request-closed" statusText = "closed" statusColor = color.RGBA{128, 128, 128, 255} // gray } @@ -162,7 +162,7 @@ func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commen statusIconSize := 36 // Draw icon with status color - err = statusStatsArea.DrawSVGIcon(statusIcon, statsX, statsY+iconBaselineOffset-statusIconSize/2+5, statusIconSize, statusColor) + err = statusStatsArea.DrawLucideIcon(statusIcon, statsX, statsY+iconBaselineOffset-statusIconSize/2+5, statusIconSize, statusColor) if err != nil { log.Printf("failed to draw status icon: %v", err) } @@ -179,7 +179,7 @@ func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commen currentX := statsX + statusIconSize + 12 + statusTextWidth + 40 // Draw comment count - err = statusStatsArea.DrawSVGIcon("static/icons/message-square.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statusStatsArea.DrawLucideIcon("message-square", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw comment icon: %v", err) } @@ -198,7 +198,7 @@ func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commen currentX += commentTextWidth + 40 // Draw files changed - err = statusStatsArea.DrawSVGIcon("static/icons/file-diff.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statusStatsArea.DrawLucideIcon("static/icons/file-diff", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw file diff icon: %v", err) } @@ -241,7 +241,7 @@ func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commen dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2) dollyY := statsY + iconBaselineOffset - dollySize/2 + 25 dollyColor := color.RGBA{180, 180, 180, 255} // light gray - err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor) + err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor) if err != nil { log.Printf("dolly silhouette not available (this is ok): %v", err) } diff --git a/appview/repo/opengraph.go b/appview/repo/opengraph.go index e5cc2fc0..6c6122e4 100644 --- a/appview/repo/opengraph.go +++ b/appview/repo/opengraph.go @@ -158,7 +158,7 @@ func (rp *Repo) drawRepoSummaryCard(repo *models.Repo, languageStats []types.Rep // Draw star icon, count, and label // Align icon baseline with text baseline iconBaselineOffset := int(textSize) / 2 - err = statsArea.DrawSVGIcon("static/icons/star.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statsArea.DrawLucideIcon("star", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw star icon: %v", err) } @@ -185,7 +185,7 @@ func (rp *Repo) drawRepoSummaryCard(repo *models.Repo, languageStats []types.Rep // Draw issues icon, count, and label issueStartX := currentX - err = statsArea.DrawSVGIcon("static/icons/circle-dot.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statsArea.DrawLucideIcon("circle-dot", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw circle-dot icon: %v", err) } @@ -210,7 +210,7 @@ func (rp *Repo) drawRepoSummaryCard(repo *models.Repo, languageStats []types.Rep // Draw pull request icon, count, and label prStartX := currentX - err = statsArea.DrawSVGIcon("static/icons/git-pull-request.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) + err = statsArea.DrawLucideIcon("git-pull-request", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor) if err != nil { log.Printf("failed to draw git-pull-request icon: %v", err) } @@ -236,7 +236,7 @@ func (rp *Repo) drawRepoSummaryCard(repo *models.Repo, languageStats []types.Rep dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2) dollyY := statsY + iconBaselineOffset - dollySize/2 + 25 dollyColor := color.RGBA{180, 180, 180, 255} // light gray - err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor) + err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor) if err != nil { log.Printf("dolly silhouette not available (this is ok): %v", err) } -- 2.43.0