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 }