Files
code-utils/kind_icon.go

205 lines
5.3 KiB
Go
Raw Normal View History

2026-08-15 17:18:00 +08:00
package main
// kind_icon.go启动台可识别 kind 的默认图标(仅本地,不同步;超管维护)。
import (
"encoding/base64"
"errors"
"fmt"
"strings"
"github.com/wailsapp/wails/v3/pkg/application"
)
var knownLaunchKinds = []string{
"node", "go", "python", "java", "php", "dotnet", "exe", "mysql", "redis", "nginx", "web", "other",
}
func isKnownLaunchKind(k string) bool {
for _, x := range knownLaunchKinds {
if x == k {
return true
}
}
return false
}
func (s *Store) ListKindIcons() (map[string]string, error) {
rows, e := s.db.Query(`SELECT kind,value FROM kind_icons WHERE value!=''`)
if e != nil {
return nil, e
}
defer rows.Close()
m := map[string]string{}
for rows.Next() {
var k, v string
if e = rows.Scan(&k, &v); e != nil {
return nil, e
}
m[k] = v
}
return m, rows.Err()
}
func (s *Store) setKindIcon(kind, value string) error {
now := nowRFC()
_, e := s.db.Exec(`INSERT INTO kind_icons(kind,value,updated_at) VALUES(?,?,?)
ON CONFLICT(kind) DO UPDATE SET value=excluded.value,updated_at=excluded.updated_at`, kind, value, now)
return e
}
func (s *Store) clearKindIcon(kind string) error {
_, e := s.db.Exec(`DELETE FROM kind_icons WHERE kind=?`, kind)
return e
}
func (a *App) kindIconAdminGate() error {
if a.syncUserID() != festivalAdminID {
return errors.New("KIND_ICON_ADMIN_ONLY")
}
return nil
}
// ListKindIcons 返回 kind → dataURL。
func (a *App) ListKindIcons() (map[string]string, error) {
if e := a.ready(); e != nil {
return nil, e
}
return a.store.ListKindIcons()
}
// ListKnownLaunchKinds 固定种类列表(前端管理页用)。
func (a *App) ListKnownLaunchKinds() []string {
return append([]string{}, knownLaunchKinds...)
}
// PickKindIcon 超管为某 kind 选择本地图片(缩放后存 dataURL
func (a *App) PickKindIcon(kind string) (string, error) {
if e := a.ready(); e != nil {
return "", e
}
if e := a.kindIconAdminGate(); e != nil {
return "", e
}
kind = strings.TrimSpace(kind)
if !isKnownLaunchKind(kind) {
return "", errors.New("KIND_UNKNOWN")
}
p, e := application.Get().Dialog.OpenFile().
SetTitle(a.localized("选择种类图标", "Choose kind icon")).
AddFilter(a.localized("图片文件", "Image files"), "*.png;*.jpg;*.jpeg;*.gif;*.webp").
PromptForSingleSelection()
if e != nil || strings.TrimSpace(p) == "" {
return "", e
}
data, mime, e := encodeAvatarFile(p)
if e != nil {
return "", e
}
dataURL := fmt.Sprintf("data:%s;base64,%s", mime, base64.StdEncoding.EncodeToString(data))
if e := a.store.setKindIcon(kind, dataURL); e != nil {
return "", e
}
return dataURL, nil
}
// ClearKindIcon 超管清除某 kind 默认图。
func (a *App) ClearKindIcon(kind string) error {
if e := a.ready(); e != nil {
return e
}
if e := a.kindIconAdminGate(); e != nil {
return e
}
kind = strings.TrimSpace(kind)
if !isKnownLaunchKind(kind) {
return errors.New("KIND_UNKNOWN")
}
return a.store.clearKindIcon(kind)
}
// PickLaunchIconImage 为启动台选本地图片base64 dataURL不强制写库
func (a *App) PickLaunchIconImage() (string, error) {
if e := a.ready(); e != nil {
return "", e
}
p, e := application.Get().Dialog.OpenFile().
SetTitle(a.localized("选择应用图标", "Choose app icon")).
AddFilter(a.localized("图片文件", "Image files"), "*.png;*.jpg;*.jpeg;*.gif;*.webp").
PromptForSingleSelection()
if e != nil || strings.TrimSpace(p) == "" {
return "", e
}
data, mime, e := encodeAvatarFile(p)
if e != nil {
return "", e
}
return fmt.Sprintf("data:%s;base64,%s", mime, base64.StdEncoding.EncodeToString(data)), nil
}
// SetLaunchAppIcon 写回已保存应用的图标(空字符串=清除)。
func (a *App) SetLaunchAppIcon(id int64, icon string) error {
if e := a.ready(); e != nil {
return e
}
if _, e := a.store.GetLaunchApp(id); e != nil {
return errors.New("LAUNCH_APP_NOT_FOUND")
}
if e := a.store.setLaunchIcon(id, icon); e != nil {
return e
}
a.emit("launchpad:changed", nil)
return nil
}
// FetchProjectIcon 从项目目录抓取 icon 并写回 projects.icon。
func (a *App) FetchProjectIcon(id int64) (string, error) {
if e := a.ready(); e != nil {
return "", e
}
p, e := a.store.GetProject(id)
if e != nil {
return "", errors.New("PROJECT_NOT_FOUND")
}
u := iconFromDir(p.Path)
if u == "" {
return "", errors.New("ICON_NOT_FOUND")
}
if e := a.store.setProjectIcon(id, u); e != nil {
return "", e
}
return u, nil
}
// SetProjectIcon 写入/清除项目图标。
func (a *App) SetProjectIcon(id int64, icon string) error {
if e := a.ready(); e != nil {
return e
}
if _, e := a.store.GetProject(id); e != nil {
return errors.New("PROJECT_NOT_FOUND")
}
return a.store.setProjectIcon(id, icon)
}
func (s *Store) setProjectIcon(id int64, icon string) error {
_, e := s.db.Exec(`UPDATE projects SET icon=?,updated_at=? WHERE id=?`, icon, now(), id)
return e
}
// DetectProjectLaunchKind 根据项目路径推断启动台 kind用于默认图标回退
func (a *App) DetectProjectLaunchKind(id int64) (string, error) {
if e := a.ready(); e != nil {
return "", e
}
p, e := a.store.GetProject(id)
if e != nil {
return "", errors.New("PROJECT_NOT_FOUND")
}
prof := detectLaunchProfile(p.Path)
if prof.Kind == "" {
return "other", nil
}
return prof.Kind, nil
}