2023-06-24 20:39:55 +08:00
|
|
|
// SiYuan - Refactor your thinking
|
2022-05-26 15:18:53 +08:00
|
|
|
// Copyright (c) 2020-present, b3log.org
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-06 17:44:20 +08:00
|
|
|
"context"
|
2022-05-26 15:18:53 +08:00
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2026-03-08 11:09:46 +08:00
|
|
|
"os"
|
2022-05-26 15:18:53 +08:00
|
|
|
"path/filepath"
|
2024-03-21 10:44:34 +08:00
|
|
|
"sync"
|
2024-03-21 22:35:21 +08:00
|
|
|
"time"
|
2022-05-26 15:18:53 +08:00
|
|
|
|
2024-04-14 23:52:09 +08:00
|
|
|
"github.com/88250/gulu"
|
2025-10-25 17:24:24 +08:00
|
|
|
"github.com/emirpasic/gods/sets/hashset"
|
2024-04-14 23:52:09 +08:00
|
|
|
"github.com/siyuan-note/logging"
|
2022-05-26 15:18:53 +08:00
|
|
|
"github.com/siyuan-note/siyuan/kernel/bazaar"
|
2025-10-25 11:33:10 +08:00
|
|
|
"github.com/siyuan-note/siyuan/kernel/task"
|
2024-04-14 23:52:09 +08:00
|
|
|
"github.com/siyuan-note/siyuan/kernel/util"
|
|
|
|
|
"golang.org/x/mod/semver"
|
2026-03-08 11:09:46 +08:00
|
|
|
"golang.org/x/sync/singleflight"
|
2022-05-26 15:18:53 +08:00
|
|
|
)
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// installedPackageInfo 描述了本地集市包的包与目录名信息
|
|
|
|
|
type installedPackageInfo struct {
|
|
|
|
|
Pkg *bazaar.Package
|
|
|
|
|
DirName string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getPackageInstallPath(pkgType, packageName string) (string, string, error) {
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "plugins":
|
|
|
|
|
return filepath.Join(util.DataDir, "plugins", packageName), "plugin.json", nil
|
|
|
|
|
case "themes":
|
|
|
|
|
return filepath.Join(util.ThemesPath, packageName), "theme.json", nil
|
|
|
|
|
case "icons":
|
|
|
|
|
return filepath.Join(util.IconsPath, packageName), "icon.json", nil
|
|
|
|
|
case "templates":
|
|
|
|
|
return filepath.Join(util.DataDir, "templates", packageName), "template.json", nil
|
|
|
|
|
case "widgets":
|
|
|
|
|
return filepath.Join(util.DataDir, "widgets", packageName), "widget.json", nil
|
|
|
|
|
default:
|
|
|
|
|
logging.LogErrorf("invalid package type: %s", pkgType)
|
|
|
|
|
return "", "", errors.New("invalid package type")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// updatePackages 更新一组集市包
|
|
|
|
|
func updatePackages(packages []*bazaar.Package, pkgType string, count *int, total int) bool {
|
|
|
|
|
for _, pkg := range packages {
|
|
|
|
|
installPath, _, err := getPackageInstallPath(pkgType, pkg.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
err = bazaar.InstallPackage(pkg.RepoURL, pkg.RepoHash, installPath, Conf.System.ID, pkgType, pkg.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logging.LogErrorf("update %s [%s] failed: %s", pkgType, pkg.Name, err)
|
|
|
|
|
util.PushErrMsg(fmt.Sprintf(Conf.language(238), pkg.Name), 5000)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
*count++
|
|
|
|
|
util.PushEndlessProgress(fmt.Sprintf(Conf.language(236), *count, total, pkg.Name))
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BatchUpdatePackages 更新所有集市包
|
|
|
|
|
func BatchUpdatePackages(frontend string) {
|
|
|
|
|
plugins, widgets, icons, themes, templates := GetUpdatedPackages(frontend)
|
2024-03-21 22:35:21 +08:00
|
|
|
|
|
|
|
|
total := len(plugins) + len(widgets) + len(icons) + len(themes) + len(templates)
|
|
|
|
|
if 1 > total {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
util.PushEndlessProgress(fmt.Sprintf(Conf.language(235), 1, total))
|
|
|
|
|
defer util.PushClearProgress()
|
|
|
|
|
count := 1
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
if !updatePackages(plugins, "plugins", &count, total) {
|
|
|
|
|
return
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
if !updatePackages(themes, "themes", &count, total) {
|
|
|
|
|
return
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
if !updatePackages(icons, "icons", &count, total) {
|
|
|
|
|
return
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
if !updatePackages(templates, "templates", &count, total) {
|
|
|
|
|
return
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
if !updatePackages(widgets, "widgets", &count, total) {
|
|
|
|
|
return
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
util.ReloadUI()
|
2024-09-05 18:07:04 +08:00
|
|
|
task.AppendAsyncTaskWithDelay(task.PushMsg, 3*time.Second, util.PushMsg, fmt.Sprintf(Conf.language(237), total), 5000)
|
2024-03-21 22:35:21 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// GetUpdatedPackages 获取所有类型集市包的更新列表
|
|
|
|
|
func GetUpdatedPackages(frontend string) (plugins, widgets, icons, themes, templates []*bazaar.Package) {
|
2024-03-21 10:44:34 +08:00
|
|
|
wg := &sync.WaitGroup{}
|
|
|
|
|
wg.Add(5)
|
2026-03-08 11:09:46 +08:00
|
|
|
|
2024-03-21 10:44:34 +08:00
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-03-08 11:09:46 +08:00
|
|
|
plugins = getUpdatedPackages("plugins", frontend, "")
|
2024-03-21 10:44:34 +08:00
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-03-08 11:09:46 +08:00
|
|
|
themes = getUpdatedPackages("themes", "", "")
|
2024-03-21 10:44:34 +08:00
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-03-08 11:09:46 +08:00
|
|
|
icons = getUpdatedPackages("icons", "", "")
|
2024-03-21 10:44:34 +08:00
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-03-08 11:09:46 +08:00
|
|
|
templates = getUpdatedPackages("templates", "", "")
|
2024-03-21 10:44:34 +08:00
|
|
|
}()
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-03-08 11:09:46 +08:00
|
|
|
widgets = getUpdatedPackages("widgets", "", "")
|
2024-03-21 10:44:34 +08:00
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
wg.Wait()
|
2024-03-21 10:32:16 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// getUpdatedPackages 获取单个类型集市包的更新列表
|
|
|
|
|
func getUpdatedPackages(pkgType, frontend, keyword string) (updatedPackages []*bazaar.Package) {
|
|
|
|
|
installedPackages := GetInstalledPackages(pkgType, frontend, keyword)
|
|
|
|
|
updatedPackages = []*bazaar.Package{} // 确保返回空切片而非 nil
|
|
|
|
|
for _, pkg := range installedPackages {
|
|
|
|
|
if !pkg.Outdated {
|
|
|
|
|
continue
|
2023-04-25 18:52:19 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
updatedPackages = append(updatedPackages, pkg)
|
|
|
|
|
pkg.PreferredReadme = "" // 清空这个字段,前端会请求在线的 README
|
2023-04-25 18:52:19 +08:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// GetInstalledPackageInfos 获取本地集市包信息,并返回路径相关字段供调用方复用
|
|
|
|
|
func GetInstalledPackageInfos(pkgType string) (installedPackageInfos []installedPackageInfo, basePath, baseURLPathPrefix string, err error) {
|
|
|
|
|
var jsonFileName string
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "plugins":
|
|
|
|
|
basePath, jsonFileName, baseURLPathPrefix = filepath.Join(util.DataDir, "plugins"), "plugin.json", "/plugins/"
|
|
|
|
|
case "themes":
|
|
|
|
|
basePath, jsonFileName, baseURLPathPrefix = util.ThemesPath, "theme.json", "/appearance/themes/"
|
|
|
|
|
case "icons":
|
|
|
|
|
basePath, jsonFileName, baseURLPathPrefix = util.IconsPath, "icon.json", "/appearance/icons/"
|
|
|
|
|
case "templates":
|
|
|
|
|
basePath, jsonFileName, baseURLPathPrefix = filepath.Join(util.DataDir, "templates"), "template.json", "/templates/"
|
|
|
|
|
case "widgets":
|
|
|
|
|
basePath, jsonFileName, baseURLPathPrefix = filepath.Join(util.DataDir, "widgets"), "widget.json", "/widgets/"
|
|
|
|
|
default:
|
|
|
|
|
logging.LogErrorf("invalid package type: %s", pkgType)
|
|
|
|
|
err = errors.New("invalid package type")
|
|
|
|
|
return
|
2023-05-05 23:54:24 +08:00
|
|
|
}
|
2023-04-25 18:52:19 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
dirs, err := bazaar.ReadInstalledPackageDirs(basePath)
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
logging.LogWarnf("read %s folder failed: %s", pkgType, err)
|
|
|
|
|
return
|
2023-04-25 18:52:19 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
if len(dirs) == 0 {
|
|
|
|
|
return
|
2023-04-25 18:52:19 +08:00
|
|
|
}
|
2023-05-06 10:35:52 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// 过滤内置包
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "themes":
|
|
|
|
|
filtered := make([]os.DirEntry, 0, len(dirs))
|
|
|
|
|
for _, d := range dirs {
|
|
|
|
|
if isBuiltInTheme(d.Name()) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
filtered = append(filtered, d)
|
2023-05-06 10:35:52 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
dirs = filtered
|
|
|
|
|
case "icons":
|
|
|
|
|
filtered := make([]os.DirEntry, 0, len(dirs))
|
|
|
|
|
for _, d := range dirs {
|
|
|
|
|
if isBuiltInIcon(d.Name()) {
|
|
|
|
|
continue
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
filtered = append(filtered, d)
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
dirs = filtered
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
for _, dir := range dirs {
|
|
|
|
|
dirName := dir.Name()
|
|
|
|
|
pkg, parseErr := bazaar.ParsePackageJSON(filepath.Join(basePath, dirName, jsonFileName))
|
|
|
|
|
if nil != parseErr || nil == pkg {
|
|
|
|
|
continue
|
2023-10-22 11:03:49 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
installedPackageInfos = append(installedPackageInfos, installedPackageInfo{Pkg: pkg, DirName: dirName})
|
2023-10-22 11:03:49 +08:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
var getInstalledPackagesFlight singleflight.Group
|
2022-09-05 12:46:57 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// GetInstalledPackages 获取本地集市包列表
|
|
|
|
|
func GetInstalledPackages(pkgType, frontend, keyword string) (installedPackages []*bazaar.Package) {
|
|
|
|
|
key := "getInstalledPackages:" + pkgType + ":" + frontend + ":" + keyword
|
|
|
|
|
v, err, _ := getInstalledPackagesFlight.Do(key, func() (interface{}, error) {
|
|
|
|
|
return getInstalledPackages0(pkgType, frontend, keyword), nil
|
|
|
|
|
})
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
return []*bazaar.Package{}
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
return v.([]*bazaar.Package)
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
func getInstalledPackages0(pkgType, frontend, keyword string) (installedPackages []*bazaar.Package) {
|
|
|
|
|
installedPackages = []*bazaar.Package{}
|
|
|
|
|
|
|
|
|
|
installedInfos, basePath, baseURLPathPrefix, err := GetInstalledPackageInfos(pkgType)
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
return
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
// 本地没有该类型的集市包时,直接返回,避免请求云端数据
|
|
|
|
|
if len(installedInfos) == 0 {
|
|
|
|
|
return
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
bazaarPackages := bazaar.GetBazaarPackages(pkgType, frontend)
|
|
|
|
|
bazaarPackagesMap := make(map[string]*bazaar.Package, len(bazaarPackages))
|
|
|
|
|
for _, pkg := range bazaarPackages {
|
|
|
|
|
if "" != pkg.Name {
|
|
|
|
|
bazaarPackagesMap[pkg.Name] = pkg
|
2023-10-22 11:03:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
for _, info := range installedInfos {
|
|
|
|
|
pkg := info.Pkg
|
|
|
|
|
installPath := filepath.Join(basePath, info.DirName)
|
|
|
|
|
baseURLPath := baseURLPathPrefix + info.DirName + "/"
|
|
|
|
|
// 设置本地集市包的通用元数据
|
|
|
|
|
if !bazaar.SetInstalledPackageMetadata(pkg, installPath, baseURLPath, pkgType, bazaarPackagesMap) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
installedPackages = append(installedPackages, pkg)
|
2022-09-05 12:46:57 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
installedPackages = bazaar.FilterPackages(installedPackages, keyword)
|
2022-05-26 15:18:53 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// 设置本地集市包的额外元数据
|
|
|
|
|
var petals []*Petal
|
|
|
|
|
if pkgType == "plugins" {
|
|
|
|
|
petals = getPetals()
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
for _, pkg := range installedPackages {
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "plugins":
|
|
|
|
|
incompatible := bazaar.IsIncompatiblePlugin(pkg, frontend)
|
|
|
|
|
pkg.Incompatible = &incompatible
|
|
|
|
|
petal := getPetalByName(pkg.Name, petals)
|
|
|
|
|
if nil != petal {
|
|
|
|
|
enabled := petal.Enabled
|
|
|
|
|
pkg.Enabled = &enabled
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
case "themes":
|
|
|
|
|
pkg.Current = pkg.Name == Conf.Appearance.ThemeDark || pkg.Name == Conf.Appearance.ThemeLight
|
|
|
|
|
case "icons":
|
|
|
|
|
pkg.Current = pkg.Name == Conf.Appearance.Icon
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// GetBazaarPackages 获取在线集市包列表
|
|
|
|
|
func GetBazaarPackages(pkgType, frontend, keyword string) (bazaarPackages []*bazaar.Package) {
|
|
|
|
|
bazaarPackages = bazaar.GetBazaarPackages(pkgType, frontend)
|
|
|
|
|
bazaarPackages = bazaar.FilterPackages(bazaarPackages, keyword)
|
|
|
|
|
installedInfos, _, _, err := GetInstalledPackageInfos(pkgType)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
2026-02-04 17:14:40 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
installedMap := make(map[string]*bazaar.Package, len(installedInfos))
|
|
|
|
|
for _, info := range installedInfos {
|
|
|
|
|
installedMap[info.Pkg.Name] = info.Pkg
|
|
|
|
|
}
|
|
|
|
|
for _, pkg := range bazaarPackages {
|
|
|
|
|
installedPkg, ok := installedMap[pkg.Name]
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
pkg.Installed = true
|
|
|
|
|
pkg.Outdated = 0 > semver.Compare("v"+installedPkg.Version, "v"+pkg.Version)
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "themes":
|
|
|
|
|
pkg.Current = pkg.Name == Conf.Appearance.ThemeDark || pkg.Name == Conf.Appearance.ThemeLight
|
|
|
|
|
case "icons":
|
|
|
|
|
pkg.Current = pkg.Name == Conf.Appearance.Icon
|
2023-10-22 11:03:49 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
func GetBazaarPackageREADME(ctx context.Context, repoURL, repoHash, pkgType string) (ret string) {
|
|
|
|
|
ret = bazaar.GetBazaarPackageREADME(ctx, repoURL, repoHash, pkgType)
|
2022-09-05 12:46:57 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
func InstallBazaarPackage(pkgType, repoURL, repoHash, packageName string, themeMode int) error {
|
|
|
|
|
installPath, jsonFileName, err := getPackageInstallPath(pkgType, packageName)
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
return err
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
installedPkg, parseErr := bazaar.ParsePackageJSON(filepath.Join(installPath, jsonFileName))
|
|
|
|
|
update := parseErr == nil && installedPkg != nil && installedPkg.Name == packageName
|
2022-05-26 15:18:53 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
err = bazaar.InstallPackage(repoURL, repoHash, installPath, Conf.System.ID, pkgType, packageName)
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
return fmt.Errorf(Conf.Language(46), packageName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch pkgType {
|
|
|
|
|
case "plugins":
|
|
|
|
|
if update {
|
|
|
|
|
// 已启用的插件更新之后需要重载
|
|
|
|
|
petals := getPetals()
|
|
|
|
|
petal := getPetalByName(packageName, petals)
|
|
|
|
|
if nil != petal && petal.Enabled {
|
|
|
|
|
reloadPluginSet := hashset.New(packageName)
|
|
|
|
|
PushReloadPlugin(nil, nil, reloadPluginSet, nil, "")
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
case "themes":
|
|
|
|
|
if !update {
|
|
|
|
|
// 更新主题后不需要切换到该主题 https://github.com/siyuan-note/siyuan/issues/4966
|
|
|
|
|
if 0 == themeMode {
|
|
|
|
|
Conf.Appearance.ThemeLight = packageName
|
|
|
|
|
} else {
|
|
|
|
|
Conf.Appearance.ThemeDark = packageName
|
|
|
|
|
}
|
|
|
|
|
Conf.Appearance.Mode = themeMode
|
|
|
|
|
Conf.Appearance.ThemeJS = gulu.File.IsExist(filepath.Join(util.ThemesPath, packageName, "theme.js"))
|
|
|
|
|
Conf.Save()
|
2023-10-22 11:03:49 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
InitAppearance()
|
|
|
|
|
WatchThemes()
|
|
|
|
|
util.BroadcastByType("main", "setAppearance", 0, "", Conf.Appearance)
|
|
|
|
|
case "icons":
|
|
|
|
|
if !update {
|
|
|
|
|
// 更新图标后不需要切换到该图标
|
|
|
|
|
Conf.Appearance.Icon = packageName
|
|
|
|
|
Conf.Save()
|
|
|
|
|
}
|
|
|
|
|
InitAppearance()
|
|
|
|
|
util.BroadcastByType("main", "setAppearance", 0, "", Conf.Appearance)
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
func UninstallPackage(pkgType, packageName string) error {
|
|
|
|
|
installPath, _, err := getPackageInstallPath(pkgType, packageName)
|
2024-09-04 04:40:50 +03:00
|
|
|
if err != nil {
|
2026-03-08 11:09:46 +08:00
|
|
|
return err
|
2022-05-26 15:18:53 +08:00
|
|
|
}
|
2023-10-22 11:57:51 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
err = bazaar.UninstallPackage(installPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf(Conf.Language(47), err.Error())
|
2026-02-03 09:59:36 +08:00
|
|
|
}
|
2023-10-25 16:08:59 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
// 删除集市包的持久化信息
|
|
|
|
|
bazaar.RemovePackageInfo(pkgType, packageName)
|
2026-02-03 09:59:36 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
switch pkgType {
|
|
|
|
|
case "plugins":
|
|
|
|
|
petals := getPetals()
|
|
|
|
|
var tmp []*Petal
|
|
|
|
|
for i, petal := range petals {
|
|
|
|
|
if petal.Name != packageName {
|
|
|
|
|
tmp = append(tmp, petals[i])
|
|
|
|
|
}
|
2023-10-25 16:08:59 +08:00
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
petals = tmp
|
|
|
|
|
savePetals(petals)
|
2023-10-22 11:57:51 +08:00
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
uninstallPluginSet := hashset.New(packageName)
|
|
|
|
|
PushReloadPlugin(uninstallPluginSet, nil, nil, nil, "")
|
|
|
|
|
case "themes":
|
|
|
|
|
InitAppearance()
|
|
|
|
|
WatchThemes()
|
|
|
|
|
case "icons":
|
|
|
|
|
InitAppearance()
|
2023-10-22 11:57:51 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 11:09:46 +08:00
|
|
|
return nil
|
2023-10-22 11:57:51 +08:00
|
|
|
}
|
2026-03-04 22:33:35 +08:00
|
|
|
|
|
|
|
|
// isBuiltInTheme 通过包名或目录名判断是否为内置主题
|
|
|
|
|
func isBuiltInTheme(name string) bool {
|
|
|
|
|
return "daylight" == name || "midnight" == name
|
|
|
|
|
}
|
2026-03-08 11:09:46 +08:00
|
|
|
|
|
|
|
|
// isBuiltInIcon 通过包名或目录名判断是否为内置图标
|
|
|
|
|
func isBuiltInIcon(name string) bool {
|
|
|
|
|
return "ant" == name || "material" == name
|
|
|
|
|
}
|