package main import ( "embed" "encoding/json" "image" "image/draw" "io/fs" "log" "os" "path" "path/filepath" "sort" "strings" "time" ) //go:embed Presets/*.json var embeddedPresets embed.FS func runWithTimeout[T any](timeout time.Duration, fn func() T) (T, bool) { ch := make(chan T, 1) go func() { ch <- fn() }() select { case out := <-ch: return out, true case <-time.After(timeout): var zero T return zero, false } } func cloneToRGBA(src image.Image, width, height int) *image.RGBA { if src == nil { return image.NewRGBA(image.Rect(0, 0, width, height)) } if rgba, ok := src.(*image.RGBA); ok { out := image.NewRGBA(rgba.Bounds()) copy(out.Pix, rgba.Pix) return out } out := image.NewRGBA(src.Bounds()) draw.Draw(out, out.Bounds(), src, image.Point{}, draw.Src) return out } type presetEntry struct { Name string Path string } type presetInfo struct { Name string Path string Source string } func listEmbeddedPresets() ([]presetEntry, error) { entries, err := fs.ReadDir(embeddedPresets, "Presets") if err != nil { return nil, err } var presets []presetEntry for _, entry := range entries { if entry.IsDir() { continue } name := entry.Name() if !strings.HasSuffix(strings.ToLower(name), ".json") { continue } displayName := strings.TrimSuffix(name, filepath.Ext(name)) presets = append(presets, presetEntry{ Name: displayName, Path: path.Join("Presets", name), }) } sort.Slice(presets, func(i, j int) bool { return presets[i].Name < presets[j].Name }) return presets, nil } func loadEmbeddedPreset(path string) (*Settings, error) { raw, err := embeddedPresets.ReadFile(path) if err != nil { return nil, err } return decodeSettingsJSON(raw) } func listUserPresets(dir string) ([]presetEntry, error) { entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return []presetEntry{}, nil } return nil, err } var presets []presetEntry for _, entry := range entries { if entry.IsDir() { continue } name := entry.Name() if !strings.HasSuffix(strings.ToLower(name), ".json") { continue } displayName := strings.TrimSuffix(name, filepath.Ext(name)) presets = append(presets, presetEntry{ Name: displayName, Path: filepath.Join(dir, name), }) } sort.Slice(presets, func(i, j int) bool { return presets[i].Name < presets[j].Name }) return presets, nil } func buildPresetOptions(settings *Settings, userPresetDir string) ([]string, map[string]presetInfo) { hidden := map[string]bool{} for _, name := range settings.HiddenPresets { hidden[name] = true } info := map[string]presetInfo{} var names []string embedded, err := listEmbeddedPresets() if err != nil { log.Println("Error loading embedded presets:", err) } else { for _, preset := range embedded { if hidden[preset.Name] { continue } info[preset.Name] = presetInfo{ Name: preset.Name, Path: preset.Path, Source: "embedded", } names = append(names, preset.Name) } } userPresets, err := listUserPresets(userPresetDir) if err != nil { log.Println("Error loading user presets:", err) } else { for _, preset := range userPresets { if _, exists := info[preset.Name]; exists { continue } info[preset.Name] = presetInfo{ Name: preset.Name, Path: preset.Path, Source: "user", } names = append(names, preset.Name) } } sort.Strings(names) return append([]string{"No Preset"}, names...), info } func savePresetToFile(path string, settings *Settings) error { raw, err := json.Marshal(settings) if err != nil { return err } var data map[string]any if err := json.Unmarshal(raw, &data); err != nil { return err } delete(data, "seed") delete(data, "last_export_path") delete(data, "image_view_state") pretty, err := json.MarshalIndent(data, "", " ") if err != nil { return err } return os.WriteFile(path, pretty, 0644) } func decodeSettingsJSON(raw []byte) (*Settings, error) { var imported Settings if err := json.Unmarshal(raw, &imported); err != nil { return nil, err } var rawKeys map[string]json.RawMessage if err := json.Unmarshal(raw, &rawKeys); err != nil { return nil, err } normalizeSettings(&imported, rawKeys) return &imported, nil } func saveSettingsToFile(path string, settings *Settings) error { exportSettings := *settings exportSettings.LastExportPath = "" exportSettings.ImageViewState = 0 data, err := json.MarshalIndent(&exportSettings, "", " ") if err != nil { return err } return os.WriteFile(path, data, 0644) }