mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-22 08:18:29 +02:00
refactor(homepage): improve icon search functionality and add case-insensitive string matching
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,19 +24,20 @@ type (
|
|||||||
IconMap map[IconKey]*IconMeta
|
IconMap map[IconKey]*IconMeta
|
||||||
IconList []string
|
IconList []string
|
||||||
IconMeta struct {
|
IconMeta struct {
|
||||||
SVG, PNG, WebP bool
|
SVG bool `json:"SVG"`
|
||||||
Light, Dark bool
|
PNG bool `json:"PNG"`
|
||||||
DisplayName string
|
WebP bool `json:"WebP"`
|
||||||
Tag string
|
Light bool `json:"Light"`
|
||||||
|
Dark bool `json:"Dark"`
|
||||||
|
DisplayName string `json:"-"`
|
||||||
|
Tag string `json:"-"`
|
||||||
}
|
}
|
||||||
IconMetaSearch struct {
|
IconMetaSearch struct {
|
||||||
Source IconSource `json:"Source"`
|
Source IconSource `json:"Source"`
|
||||||
Ref string `json:"Ref"`
|
Ref string `json:"Ref"`
|
||||||
SVG bool `json:"SVG"`
|
*IconMeta
|
||||||
PNG bool `json:"PNG"`
|
|
||||||
WebP bool `json:"WebP"`
|
rank int
|
||||||
Light bool `json:"Light"`
|
|
||||||
Dark bool `json:"Dark"`
|
|
||||||
}
|
}
|
||||||
Cache struct {
|
Cache struct {
|
||||||
Icons IconMap
|
Icons IconMap
|
||||||
@@ -150,31 +152,54 @@ func ListAvailableIcons() (*Cache, error) {
|
|||||||
return iconsCache, nil
|
return iconsCache, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchIcons(keyword string, limit int) []IconMetaSearch {
|
func SearchIcons(keyword string, limit int) []*IconMetaSearch {
|
||||||
if keyword == "" {
|
if keyword == "" {
|
||||||
return make([]IconMetaSearch, 0)
|
return []*IconMetaSearch{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if limit == 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
iconsCache.RLock()
|
iconsCache.RLock()
|
||||||
defer iconsCache.RUnlock()
|
defer iconsCache.RUnlock()
|
||||||
result := make([]IconMetaSearch, 0)
|
|
||||||
|
searchLimit := min(limit*5, 50)
|
||||||
|
|
||||||
|
results := make([]*IconMetaSearch, 0, searchLimit)
|
||||||
|
|
||||||
|
sortByRank := func(a, b *IconMetaSearch) int {
|
||||||
|
return a.rank - b.rank
|
||||||
|
}
|
||||||
|
|
||||||
|
var rank int
|
||||||
for k, icon := range iconsCache.Icons {
|
for k, icon := range iconsCache.Icons {
|
||||||
if fuzzy.MatchFold(keyword, string(k)) {
|
if strutils.ContainsFold(string(k), keyword) || strutils.ContainsFold(icon.DisplayName, keyword) {
|
||||||
source, ref := k.SourceRef()
|
rank = 0
|
||||||
result = append(result, IconMetaSearch{
|
} else {
|
||||||
Source: source,
|
rank = fuzzy.RankMatchFold(keyword, string(k))
|
||||||
Ref: ref,
|
if rank == -1 || rank > 3 {
|
||||||
SVG: icon.SVG,
|
continue
|
||||||
PNG: icon.PNG,
|
}
|
||||||
WebP: icon.WebP,
|
|
||||||
Light: icon.Light,
|
|
||||||
Dark: icon.Dark,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if len(result) >= limit {
|
|
||||||
|
source, ref := k.SourceRef()
|
||||||
|
ranked := &IconMetaSearch{
|
||||||
|
Source: source,
|
||||||
|
Ref: ref,
|
||||||
|
IconMeta: icon,
|
||||||
|
rank: rank,
|
||||||
|
}
|
||||||
|
// Sorted insert based on rank (lower rank = better match)
|
||||||
|
insertPos, _ := slices.BinarySearchFunc(results, ranked, sortByRank)
|
||||||
|
results = slices.Insert(results, insertPos, ranked)
|
||||||
|
if len(results) == searchLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
|
// Extract results and limit to the requested count
|
||||||
|
return results[:min(len(results), limit)]
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasIcon(icon *IconURL) bool {
|
func HasIcon(icon *IconURL) bool {
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ func Title(s string) string {
|
|||||||
return cases.Title(language.AmericanEnglish).String(s)
|
return cases.Title(language.AmericanEnglish).String(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ContainsFold(s, substr string) bool {
|
||||||
|
return IndexFold(s, substr) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexFold(s, substr string) int {
|
||||||
|
return strings.Index(strings.ToLower(s), strings.ToLower(substr))
|
||||||
|
}
|
||||||
|
|
||||||
func ToLowerNoSnake(s string) string {
|
func ToLowerNoSnake(s string) string {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
for _, r := range s {
|
for _, r := range s {
|
||||||
|
|||||||
Reference in New Issue
Block a user