package main
import (
_ "embed"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
)
//go:embed web/overlay.html
var overlayHTML string
//go:embed web/themes/aurora.html
var themeAurora string
//go:embed web/themes/starfield.html
var themeStarfield string
//go:embed web/themes/gradient.html
var themeGradient string
//go:embed web/themes/particles.html
var themeParticles string
//go:embed web/themes/fractal.html
var themeFractal string
//go:embed web/themes/text.html
var themeText string
var themeMap = map[ThemeName]string{
ThemeAurora: themeAurora,
ThemeStar: themeStarfield,
ThemeGradient: themeGradient,
ThemeParticle: themeParticles,
ThemeFractal: themeFractal,
ThemeText: themeText,
}
func buildWallpaperHTML(cfg *Config) string {
var bg string
if cfg.WallpaperType == WPTheme {
if t, ok := themeMap[cfg.Theme]; ok {
bg = t
} else {
bg = themeAurora
}
} else {
bg = buildBgHTML(cfg)
}
if bg == "" {
bg = themeAurora
}
bgWrapped := fmt.Sprintf(`
%s
`, bg)
if cfg.HideWallpaper {
bgWrapped = ``
}
initialData := map[string]interface{}{
"backgroundHtml": bgWrapped,
"layout": string(cfg.Layout),
"showSeconds": cfg.ShowSeconds,
"userZodiac": cfg.Zodiac,
"wallpaperVisible": !cfg.HideWallpaper,
"photoFrameMode": cfg.PhotoFrameMode && cfg.PhotoDir != "",
"cardVisibility": map[string]bool{
"time": !cfg.HideTime,
"weather": !cfg.HideWeather,
"zodiac": !cfg.HideZodiac,
"knowledge": !cfg.HideKnowledge,
"ainews": !cfg.HideAINews,
"photo": !cfg.HidePhoto,
},
}
dataJSON, _ := json.Marshal(initialData)
inject := fmt.Sprintf(``, string(dataJSON))
// 注入自定义文字
if cfg.WallpaperType == WPTheme && cfg.Theme == ThemeText && cfg.WallpaperText != "" {
escaped, _ := json.Marshal(cfg.WallpaperText)
inject += fmt.Sprintf(``, string(escaped))
}
return strings.Replace(overlayHTML, "", inject+"", 1)
}
func imageToDataURI(path string) string {
data, err := os.ReadFile(path)
if err != nil {
log.Println("读取图片失败:", err)
return ""
}
ext := strings.ToLower(filepath.Ext(path))
mime := "image/jpeg"
switch ext {
case ".png":
mime = "image/png"
case ".gif":
mime = "image/gif"
case ".webp":
mime = "image/webp"
case ".bmp":
mime = "image/bmp"
}
return fmt.Sprintf("data:%s;base64,%s", mime, base64.StdEncoding.EncodeToString(data))
}
func reloadWallpaper() {
if wv == nil || wvHwnd == 0 {
return
}
cfg := loadConfig()
// 非主题壁纸切换:仅替换 #bg-layer,不销毁卡片状态
if cfg.WallpaperType != WPTheme {
updateBackground(cfg)
return
}
// 主题切换需要 SetHtml(canvas+script),之后恢复卡片数据
html := buildWallpaperHTML(cfg)
select {
case htmlQueue <- html:
default:
}
procPostMessageW.Call(wvHwnd, wmSetHtml, 0, 0)
go reloadAllCards()
}
func buildBgHTML(cfg *Config) string {
switch cfg.WallpaperType {
case WPImage:
if cfg.ImagePath != "" {
src := imageToDataURI(cfg.ImagePath)
if src != "" {
return buildCoverImgHTML(src)
}
}
case WPBing:
if p := getCurrentBingPath(); p != "" {
if _, err := os.Stat(p); err == nil {
src := imageToDataURI(p)
if src != "" {
return buildCoverImgHTML(src)
}
}
}
case WPColor:
if cfg.ColorGradient && cfg.Color2 != "" {
return fmt.Sprintf(``, cfg.Color1, cfg.Color2)
}
return fmt.Sprintf(``, cfg.Color1)
}
return ""
}
const coverImgTpl = `
`
func buildCoverImgHTML(src string) string {
return fmt.Sprintf(coverImgTpl, src)
}
func updateBackground(cfg *Config) {
bg := buildBgHTML(cfg)
if bg == "" {
return
}
display := ""
if cfg.HideWallpaper {
display = ` style="display:none"`
}
html := fmt.Sprintf(`%s
`, display, bg)
// Update Vue reactive state via bridge
evalJS(fmt.Sprintf(`if(window.__updateBackgroundHtml) window.__updateBackgroundHtml(%q);`, html))
// Fallback: direct DOM update for non-Vue contexts
evalJS(fmt.Sprintf(`var el=document.getElementById('bg-layer'); if(el && !window.__updateBackgroundHtml){el.outerHTML=%q;}`, html))
}
func reloadAllCards() {
time.Sleep(800 * time.Millisecond)
evalJS(fmt.Sprintf(`window.userZodiac = %q;`, loadConfig().Zodiac))
if cached := loadHoroscopeCache(); cached != nil {
pushHoroscopeInfo(cached)
}
if cached := loadAINewsCache(); cached != nil {
pushAINews(cached)
}
cfg := loadConfig()
if cfg.KnowledgeKeyword != "" && !cfg.HideKnowledge {
if cached := getRandomKnowledgeCard(cfg.KnowledgeKeyword); cached != "" {
pushKnowledgeJSON(cached, cfg.KnowledgeKeyword)
}
}
if city := getCurrentCity(); city.ID != "" {
go fetchAndPushWeather(city)
}
if cfg.PhotoDir != "" && !cfg.HidePhoto {
go func() {
time.Sleep(500 * time.Millisecond)
pushCurrentPhoto(cfg.PhotoInterval)
}()
}
}
func bingReloadImage() {
if wv == nil || wvHwnd == 0 {
return
}
p := getCurrentBingPath()
if p == "" {
return
}
src := imageToDataURI(p)
if src == "" {
return
}
evalJS(fmt.Sprintf(`var bg=document.querySelector('#bg-layer img'); if(bg) bg.src=%q;`, src))
}