- Semua SVG diraster ke PNG lebih dulu (install, sync, thumbnail). SVG dari internet sebagian memakai fitur yang tidak didukung renderer sehingga ikon tidak muncul atau proses menggambar berhenti. - fill="currentColor" diganti warna brand (penyebab tersering ikon Material/ Remix gagal diraster), rasterisasi dibungkus recover - Thumbnail pencarian: placeholder + cache unduhan + batas 3 unduhan bersamaan - Kandidat ikon yang gagal dilewati, install tetap lanjut ke ikon default - Race pada data ikon pilihan diperbaiki (mutex); tes -race hijau - Baris status modal pencarian dipindah ke barisnya sendiri (tidak bertabrakan dengan tombol Back/Search di layar berskala besar)
103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
// Package icon menyimpan ikon shortcut ke direktori ikon XDG.
|
|
package icon
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"lazyrun/assets"
|
|
"lazyrun/internal/xdg"
|
|
)
|
|
|
|
var exts = []string{".png", ".svg", ".jpg", ".jpeg", ".xpm"}
|
|
var nearbyNames = []string{"icon", "logo"}
|
|
|
|
// Save memasang ikon untuk shortcut dan mengembalikan path absolutnya.
|
|
// Urutan pilihan: ikon dari user, ikon di folder binary, lalu ikon default LazyRun.
|
|
func Save(userIcon, nearbyDir, nearbyBase, slug string) (string, error) {
|
|
if userIcon != "" {
|
|
return fromFile(userIcon, slug)
|
|
}
|
|
for _, name := range append([]string{nearbyBase}, nearbyNames...) {
|
|
if name == "" {
|
|
continue
|
|
}
|
|
if p := findNearby(nearbyDir, name); p != "" {
|
|
return fromFile(p, slug)
|
|
}
|
|
}
|
|
return Default(slug)
|
|
}
|
|
|
|
// Default memasang ikon bawaan LazyRun.
|
|
func Default(slug string) (string, error) {
|
|
return SaveBytes(assets.Icon, ".png", slug)
|
|
}
|
|
|
|
// Nearby mencari file ikon bernama base.* di folder dir (dipakai untuk auto-deteksi).
|
|
func Nearby(dir, base string) string {
|
|
if dir == "" || base == "" {
|
|
return ""
|
|
}
|
|
return findNearby(dir, base)
|
|
}
|
|
|
|
func findNearby(dir, base string) string {
|
|
base = strings.ToLower(base)
|
|
for _, ext := range exts {
|
|
p := filepath.Join(dir, base+ext)
|
|
if st, err := os.Stat(p); err == nil && !st.IsDir() {
|
|
return p
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func fromFile(src, slug string) (string, error) {
|
|
data, err := os.ReadFile(src)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ext := strings.ToLower(filepath.Ext(src))
|
|
// ICO tidak dipakai desktop theme; pakai PNG di dalamnya kalau ada.
|
|
if ext == ".ico" {
|
|
png := largestPNGFromICO(data)
|
|
if png == nil {
|
|
return "", errors.New("this .ico has no PNG inside it, pick another icon")
|
|
}
|
|
return SaveBytes(png, ".png", slug)
|
|
}
|
|
return SaveBytes(data, ext, slug)
|
|
}
|
|
|
|
// SaveBytes menulis data ikon ke direktori ikon XDG.
|
|
// SVG diraster dulu ke PNG: sebagian ikon internet memakai fitur SVG yang membuat
|
|
// proses menggambar berhenti (panic) atau tidak tampil di UI/desktop.
|
|
func SaveBytes(data []byte, ext, slug string) (string, error) {
|
|
if ext == "" {
|
|
ext = ".png"
|
|
}
|
|
if ext == ".svg" {
|
|
raster, err := Rasterize(data, 256)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
data, ext = raster, ".png"
|
|
}
|
|
if err := os.MkdirAll(xdg.IconsDir(), 0o755); err != nil {
|
|
return "", err
|
|
}
|
|
// shortcut: ikon disimpan apa adanya di hicolor/512x512 tanpa resize;
|
|
// desktop environment men-skalanya sendiri. Tambah resize kalau ikon kecil terlihat pecah.
|
|
dest := filepath.Join(xdg.IconsDir(), slug+ext)
|
|
return dest, os.WriteFile(dest, data, 0o644)
|
|
}
|
|
|
|
// Ext menebak ekstensi file dari isi data (dipakai untuk ikon yang sudah diunduh UI).
|
|
func Ext(data []byte) string { return sniffExt(data) }
|
|
|
|
// IconBytes adalah ikon default LazyRun.
|
|
func IconBytes() []byte { return assets.Icon }
|