Ikon otomatis dari internet + cursor mengikuti tema host

- Cari ikon online (Iconify) dari dialog install, plus tombol sync ikon per shortcut
- Auto ikon: file terdekat -> favicon situs (termasuk ikon manifest PWA) -> ikon brand
  yang namanya cocok -> ikon default; sumber ikon disimpan di metadata
- Query dibersihkan (web.whatsapp.com -> whatsapp) dan kata <4 huruf tidak dipakai
  supaya aplikasi tanpa ikon resmi tidak dapat ikon acak
- Unduhan thumbnail dibatasi 4 bersamaan + retry sekali (rate limit Iconify)
- Cursor: XCURSOR_THEME/XCURSOR_SIZE diambil dari config KDE/GTK/~/.icons default,
  dan AppImage tidak lagi membundel libX*/libwayland*/libxkbcommon (dipakai dari host)
- README: bagian ikon + integrasi desktop
This commit is contained in:
oxpoe committed 2026-10-13 08:00:12 +07:00
1 parent 70534bf6e2
commit 7f7cb97a65
19 files changed
+1117 -40

No files matched your search

+38
View File
@@ -6,6 +6,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
"fyne.io/fyne/v2"
@@ -138,6 +139,9 @@ func (ui *app) row(sc store.Shortcut) *hoverCard {
actions := container.NewHBox(chips...)
actions.Add(iconButton(theme.MediaPlayIcon(), func() { ui.launch(sc) }))
if syncable(sc) {
actions.Add(iconButton(theme.ViewRefreshIcon(), func() { ui.syncIcon(sc) }))
}
actions.Add(iconButton(theme.FolderOpenIcon(), func() {
if err := exec.Command("xdg-open", filepath.Dir(sc.DesktopPath)).Start(); err != nil {
dialog.ShowError(err, ui.win)
@@ -192,6 +196,40 @@ func (ui *app) flashCards() {
}
}
// syncable berarti ikon shortcut punya sumber online yang bisa diunduh ulang.
func syncable(sc store.Shortcut) bool {
source := sc.IconSource
return strings.HasPrefix(source, "http") ||
strings.HasPrefix(source, "favicon:") ||
strings.HasPrefix(source, "search:")
}
// syncIcon mengunduh ulang ikon shortcut dari sumber online-nya.
func (ui *app) syncIcon(sc store.Shortcut) {
progress := dialog.NewProgressInfinite(T("sync_icon"), T("working"), ui.win)
progress.Show()
go func() {
path, err := shortcut.RefreshIcon(sc)
fyne.Do(func() {
progress.Hide()
if err != nil {
dialog.ShowError(err, ui.win)
return
}
if i, ok := ui.store.Get(sc.Slug); ok {
ui.store.Items[i].IconPath = path
if err := ui.store.Save(); err != nil {
dialog.ShowError(err, ui.win)
}
}
ui.reload()
if card, ok := ui.cards[sc.Slug]; ok {
card.flash(0)
}
})
}()
}
func (ui *app) confirmDelete(sc store.Shortcut) {
content := container.NewVBox(widget.NewLabel(fmt.Sprintf(T("confirm_delete_msg"), sc.Name)))