+5
-5
appview/issues/opengraph.go
+5
-5
appview/issues/opengraph.go
···
143
143
var statusBgColor color.RGBA
144
144
145
145
if issue.Open {
146
-
statusIcon = "static/icons/circle-dot.svg"
146
+
statusIcon = "circle-dot"
147
147
statusText = "open"
148
148
statusBgColor = color.RGBA{34, 139, 34, 255} // green
149
149
} else {
150
-
statusIcon = "static/icons/circle-dot.svg"
150
+
statusIcon = "ban"
151
151
statusText = "closed"
152
152
statusBgColor = color.RGBA{52, 58, 64, 255} // dark gray
153
153
}
···
155
155
badgeIconSize := 36
156
156
157
157
// Draw icon with status color (no background)
158
-
err = statusCommentsArea.DrawSVGIcon(statusIcon, statsX, statsY+iconBaselineOffset-badgeIconSize/2+5, badgeIconSize, statusBgColor)
158
+
err = statusCommentsArea.DrawLucideIcon(statusIcon, statsX, statsY+iconBaselineOffset-badgeIconSize/2+5, badgeIconSize, statusBgColor)
159
159
if err != nil {
160
160
log.Printf("failed to draw status icon: %v", err)
161
161
}
···
172
172
currentX := statsX + badgeIconSize + 12 + statusTextWidth + 50
173
173
174
174
// Draw comment count
175
-
err = statusCommentsArea.DrawSVGIcon("static/icons/message-square.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
175
+
err = statusCommentsArea.DrawLucideIcon("message-square", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
176
176
if err != nil {
177
177
log.Printf("failed to draw comment icon: %v", err)
178
178
}
···
193
193
dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2)
194
194
dollyY := statsY + iconBaselineOffset - dollySize/2 + 25
195
195
dollyColor := color.RGBA{180, 180, 180, 255} // light gray
196
-
err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor)
196
+
err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor)
197
197
if err != nil {
198
198
log.Printf("dolly silhouette not available (this is ok): %v", err)
199
199
}
+59
-10
appview/ogcard/card.go
+59
-10
appview/ogcard/card.go
···
7
7
import (
8
8
"bytes"
9
9
"fmt"
10
+
"html/template"
10
11
"image"
11
12
"image/color"
12
13
"io"
···
279
280
return width, nil
280
281
}
281
282
282
-
// DrawSVGIcon draws an SVG icon from the embedded files at the specified position
283
-
func (c *Card) DrawSVGIcon(svgPath string, x, y, size int, iconColor color.Color) error {
284
-
svgData, err := pages.Files.ReadFile(svgPath)
285
-
if err != nil {
286
-
return fmt.Errorf("failed to read SVG file %s: %w", svgPath, err)
287
-
}
288
-
283
+
func BuildSVGIconFromData(svgData []byte, iconColor color.Color) (*oksvg.SvgIcon, error) {
289
284
// Convert color to hex string for SVG
290
285
rgba, isRGBA := iconColor.(color.RGBA)
291
286
if !isRGBA {
···
304
299
// Parse SVG
305
300
icon, err := oksvg.ReadIconStream(strings.NewReader(svgString))
306
301
if err != nil {
307
-
return fmt.Errorf("failed to parse SVG %s: %w", svgPath, err)
302
+
return nil, fmt.Errorf("failed to parse SVG: %w", err)
308
303
}
309
304
305
+
return icon, nil
306
+
}
307
+
308
+
func BuildSVGIconFromPath(svgPath string, iconColor color.Color) (*oksvg.SvgIcon, error) {
309
+
svgData, err := pages.Files.ReadFile(svgPath)
310
+
if err != nil {
311
+
return nil, fmt.Errorf("failed to read SVG file %s: %w", svgPath, err)
312
+
}
313
+
314
+
icon, err := BuildSVGIconFromData(svgData, iconColor)
315
+
if err != nil {
316
+
return nil, fmt.Errorf("failed to build SVG icon %s: %w", svgPath, err)
317
+
}
318
+
319
+
return icon, nil
320
+
}
321
+
322
+
func BuildLucideIcon(name string, iconColor color.Color) (*oksvg.SvgIcon, error) {
323
+
return BuildSVGIconFromPath(fmt.Sprintf("static/icons/%s.svg", name), iconColor)
324
+
}
325
+
326
+
func (c *Card) DrawLucideIcon(name string, x, y, size int, iconColor color.Color) error {
327
+
icon, err := BuildSVGIconFromPath(fmt.Sprintf("static/icons/%s.svg", name), iconColor)
328
+
if err != nil {
329
+
return err
330
+
}
331
+
332
+
c.DrawSVGIcon(icon, x, y, size)
333
+
334
+
return nil
335
+
}
336
+
337
+
func (c *Card) DrawDollySilhouette(x, y, size int, iconColor color.Color) error {
338
+
tpl, err := template.New("dolly").
339
+
ParseFS(pages.Files, "templates/fragments/dolly/silhouette.html")
340
+
if err != nil {
341
+
return fmt.Errorf("failed to read dolly silhouette template: %w", err)
342
+
}
343
+
344
+
var svgData bytes.Buffer
345
+
if err = tpl.ExecuteTemplate(&svgData, "fragments/dolly/silhouette", nil); err != nil {
346
+
return fmt.Errorf("failed to execute dolly silhouette template: %w", err)
347
+
}
348
+
349
+
icon, err := BuildSVGIconFromData(svgData.Bytes(), iconColor)
350
+
if err != nil {
351
+
return err
352
+
}
353
+
354
+
c.DrawSVGIcon(icon, x, y, size)
355
+
356
+
return nil
357
+
}
358
+
359
+
// DrawSVGIcon draws an SVG icon from the embedded files at the specified position
360
+
func (c *Card) DrawSVGIcon(icon *oksvg.SvgIcon, x, y, size int) {
310
361
// Set the icon size
311
362
w, h := float64(size), float64(size)
312
363
icon.SetTarget(0, 0, w, h)
···
334
385
}
335
386
336
387
draw.Draw(c.Img, destRect, iconImg, image.Point{}, draw.Over)
337
-
338
-
return nil
339
388
}
340
389
341
390
// DrawImage fills the card with an image, scaled to maintain the original aspect ratio and centered with respect to the non-filled dimension
+7
-7
appview/pulls/opengraph.go
+7
-7
appview/pulls/opengraph.go
···
146
146
var statusColor color.RGBA
147
147
148
148
if pull.State.IsOpen() {
149
-
statusIcon = "static/icons/git-pull-request.svg"
149
+
statusIcon = "git-pull-request"
150
150
statusText = "open"
151
151
statusColor = color.RGBA{34, 139, 34, 255} // green
152
152
} else if pull.State.IsMerged() {
153
-
statusIcon = "static/icons/git-merge.svg"
153
+
statusIcon = "git-merge"
154
154
statusText = "merged"
155
155
statusColor = color.RGBA{138, 43, 226, 255} // purple
156
156
} else {
157
-
statusIcon = "static/icons/git-pull-request-closed.svg"
157
+
statusIcon = "git-pull-request-closed"
158
158
statusText = "closed"
159
159
statusColor = color.RGBA{128, 128, 128, 255} // gray
160
160
}
···
162
162
statusIconSize := 36
163
163
164
164
// Draw icon with status color
165
-
err = statusStatsArea.DrawSVGIcon(statusIcon, statsX, statsY+iconBaselineOffset-statusIconSize/2+5, statusIconSize, statusColor)
165
+
err = statusStatsArea.DrawLucideIcon(statusIcon, statsX, statsY+iconBaselineOffset-statusIconSize/2+5, statusIconSize, statusColor)
166
166
if err != nil {
167
167
log.Printf("failed to draw status icon: %v", err)
168
168
}
···
179
179
currentX := statsX + statusIconSize + 12 + statusTextWidth + 40
180
180
181
181
// Draw comment count
182
-
err = statusStatsArea.DrawSVGIcon("static/icons/message-square.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
182
+
err = statusStatsArea.DrawLucideIcon("message-square", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
183
183
if err != nil {
184
184
log.Printf("failed to draw comment icon: %v", err)
185
185
}
···
198
198
currentX += commentTextWidth + 40
199
199
200
200
// Draw files changed
201
-
err = statusStatsArea.DrawSVGIcon("static/icons/file-diff.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
201
+
err = statusStatsArea.DrawLucideIcon("static/icons/file-diff", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
202
202
if err != nil {
203
203
log.Printf("failed to draw file diff icon: %v", err)
204
204
}
···
241
241
dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2)
242
242
dollyY := statsY + iconBaselineOffset - dollySize/2 + 25
243
243
dollyColor := color.RGBA{180, 180, 180, 255} // light gray
244
-
err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor)
244
+
err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor)
245
245
if err != nil {
246
246
log.Printf("dolly silhouette not available (this is ok): %v", err)
247
247
}
+4
-4
appview/repo/opengraph.go
+4
-4
appview/repo/opengraph.go
···
158
158
// Draw star icon, count, and label
159
159
// Align icon baseline with text baseline
160
160
iconBaselineOffset := int(textSize) / 2
161
-
err = statsArea.DrawSVGIcon("static/icons/star.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
161
+
err = statsArea.DrawLucideIcon("star", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
162
162
if err != nil {
163
163
log.Printf("failed to draw star icon: %v", err)
164
164
}
···
185
185
186
186
// Draw issues icon, count, and label
187
187
issueStartX := currentX
188
-
err = statsArea.DrawSVGIcon("static/icons/circle-dot.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
188
+
err = statsArea.DrawLucideIcon("circle-dot", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
189
189
if err != nil {
190
190
log.Printf("failed to draw circle-dot icon: %v", err)
191
191
}
···
210
210
211
211
// Draw pull request icon, count, and label
212
212
prStartX := currentX
213
-
err = statsArea.DrawSVGIcon("static/icons/git-pull-request.svg", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
213
+
err = statsArea.DrawLucideIcon("git-pull-request", currentX, statsY+iconBaselineOffset-iconSize/2+5, iconSize, iconColor)
214
214
if err != nil {
215
215
log.Printf("failed to draw git-pull-request icon: %v", err)
216
216
}
···
236
236
dollyX := dollyBounds.Min.X + (dollyBounds.Dx() / 2) - (dollySize / 2)
237
237
dollyY := statsY + iconBaselineOffset - dollySize/2 + 25
238
238
dollyColor := color.RGBA{180, 180, 180, 255} // light gray
239
-
err = dollyArea.DrawSVGIcon("templates/fragments/dolly/silhouette.svg", dollyX, dollyY, dollySize, dollyColor)
239
+
err = dollyArea.DrawDollySilhouette(dollyX, dollyY, dollySize, dollyColor)
240
240
if err != nil {
241
241
log.Printf("dolly silhouette not available (this is ok): %v", err)
242
242
}