v0.27.2 5960dd8

resize.go RawHistoryFolder

1// Package favicon provides favicon fetching and caching infrastructure.
2package favicon
3
4import (
5	"fmt"
6	"image"
7	"image/png"
8	"os"
9
10	"golang.org/x/image/draw"
11)
12
13const (
14	// NormalizedIconSize is the standard size for dmenu/fuzzel icons.
15	NormalizedIconSize = 32
16)
17
18// ResizePNG loads a PNG from srcPath, resizes it to a square of the given size,
19// and saves the result to dstPath. Uses golang.org/x/image/draw for high-quality
20// resizing with CatmullRom interpolation.
21func ResizePNG(srcPath, dstPath string, size int) error {
22	// Open source file
23	srcFile, err := os.Open(srcPath)
24	if err != nil {
25		return fmt.Errorf("open source: %w", err)
26	}
27	defer func() { _ = srcFile.Close() }()
28
29	// Decode PNG
30	srcImg, err := png.Decode(srcFile)
31	if err != nil {
32		return fmt.Errorf("decode png: %w", err)
33	}
34
35	// Calculate crop region to maintain aspect ratio (center crop to square)
36	srcBounds := srcImg.Bounds()
37	srcW := srcBounds.Dx()
38	srcH := srcBounds.Dy()
39
40	var cropRect image.Rectangle
41	if srcW > srcH {
42		// Wider than tall - crop sides
43		offset := (srcW - srcH) / 2
44		cropRect = image.Rect(srcBounds.Min.X+offset, srcBounds.Min.Y, srcBounds.Min.X+offset+srcH, srcBounds.Max.Y)
45	} else if srcH > srcW {
46		// Taller than wide - crop top/bottom
47		offset := (srcH - srcW) / 2
48		cropRect = image.Rect(srcBounds.Min.X, srcBounds.Min.Y+offset, srcBounds.Max.X, srcBounds.Min.Y+offset+srcW)
49	} else {
50		// Already square
51		cropRect = srcBounds
52	}
53
54	// Create cropped subimage
55	croppedImg := cropImage(srcImg, cropRect)
56
57	// Create destination image
58	dstImg := image.NewRGBA(image.Rect(0, 0, size, size))
59
60	// Scale using CatmullRom for high-quality interpolation
61	draw.CatmullRom.Scale(dstImg, dstImg.Bounds(), croppedImg, croppedImg.Bounds(), draw.Over, nil)
62
63	// Create destination file
64	dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, diskCacheFilePerm)
65	if err != nil {
66		return fmt.Errorf("create destination: %w", err)
67	}
68	defer func() { _ = dstFile.Close() }()
69
70	// Encode as PNG
71	if err := png.Encode(dstFile, dstImg); err != nil {
72		return fmt.Errorf("encode png: %w", err)
73	}
74
75	return nil
76}
77
78// cropImage returns a cropped portion of the source image.
79func cropImage(src image.Image, rect image.Rectangle) image.Image {
80	// If the source supports SubImage, use it for efficiency
81	if subImager, ok := src.(interface {
82		SubImage(r image.Rectangle) image.Image
83	}); ok {
84		return subImager.SubImage(rect)
85	}
86
87	// Otherwise, copy pixels manually
88	dst := image.NewRGBA(image.Rect(0, 0, rect.Dx(), rect.Dy()))
89	for y := 0; y < rect.Dy(); y++ {
90		for x := 0; x < rect.Dx(); x++ {
91			dst.Set(x, y, src.At(rect.Min.X+x, rect.Min.Y+y))
92		}
93	}
94	return dst
95}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close