Files
2026-07-13 13:12:56 +08:00

131 lines
5.0 KiB
Go

package main
import (
"embed"
"encoding/json"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/adrg/xdg"
)
const defaultOutputFilename = "freeze.png"
// Config is the configuration options for a screenshot.
type Config struct {
Input string `json:",omitempty" arg:"" help:"Code to screenshot." optional:""`
// Window
Background string `json:"background" help:"Apply a background fill." short:"b" placeholder:"#171717" group:"Window"`
Margin []float64 `json:"margin" help:"Apply margin to the window." short:"m" placeholder:"0" group:"Window"`
Padding []float64 `json:"padding" help:"Apply padding to the code." short:"p" placeholder:"0" group:"Window"`
Window bool `json:"window" help:"Display window controls." group:"Window"`
Width float64 `json:"width" help:"Width of terminal window." short:"W" group:"Window"`
Height float64 `json:"height" help:"Height of terminal window." short:"H" group:"Window"`
// Settings
Version bool `json:"version" help:"Display Freeze's version." short:"v" group:"Settings"`
Config string `json:"config,omitempty" help:"Base configuration file or template." short:"c" group:"Settings" default:"default" placeholder:"base"`
Interactive bool `hidden:"" json:",omitempty" help:"Use an interactive form for configuration options." short:"i" group:"Settings"`
Language string `json:"language,omitempty" help:"Language of code file." short:"l" group:"Settings" placeholder:"go"`
Theme string `json:"theme" help:"Theme to use for syntax highlighting." short:"t" group:"Settings" placeholder:"charm"`
Wrap int `json:"wrap" help:"Wrap lines at a specific width." short:"w" group:"Settings" default:"0" placeholder:"80"`
Output string `json:"output,omitempty" help:"Output location for {{.svg}}, {{.png}}, or {{.webp}}." short:"o" group:"Settings" default:"" placeholder:"freeze.svg"`
Execute string `json:"-" help:"Capture output of command execution." short:"x" group:"Settings" default:""`
ExecuteTimeout time.Duration `json:"-" help:"Execution timeout." group:"Settings" default:"10s" prefix:"execute." name:"timeout" hidden:""`
// Decoration
Border Border `json:"border" embed:"" prefix:"border." group:"Border"`
Shadow Shadow `json:"shadow" embed:"" prefix:"shadow." help:"add a shadow to the window" short:"s" group:"Shadow"`
// Font
Font Font `json:"font" embed:"" prefix:"font." group:"Font"`
// Line
LineHeight float64 `json:"line_height" help:"Line height relative to font size." group:"Line" placeholder:"1.2"`
Lines []int `json:"-" help:"Lines to capture (start,end)." group:"Line" placeholder:"0,-1" value:"0,-1"`
ShowLineNumbers bool `json:"show_line_numbers" help:"" group:"Line" placeholder:"false"`
}
// Shadow is the configuration options for a drop shadow.
type Shadow struct {
Blur float64 `json:"blur" help:"Shadow Gaussian Blur." placeholder:"0"`
X float64 `json:"x" help:"Shadow offset {{x}} coordinate." placeholder:"0"`
Y float64 `json:"y" help:"Shadow offset {{y}} coordinate." placeholder:"0"`
}
// Border is the configuration options for a window border.
type Border struct {
Radius float64 `json:"radius" help:"Corner radius of window." short:"r" placeholder:"0"`
Width float64 `json:"width" help:"Border width thickness." placeholder:"1"`
Color string `json:"color" help:"Border color." placeholder:"#000"`
}
// Font is the configuration options for a font.
type Font struct {
Family string `json:"family" help:"Font family to use for code." placeholder:"monospace"`
File string `json:"file" help:"Font file to embed." placeholder:"monospace.ttf"`
Size float64 `json:"size" help:"Font size to use for code." placeholder:"14"`
Ligatures bool `json:"ligatures" help:"Use ligatures in the font." placeholder:"true" value:"true" negatable:""`
}
//go:embed configurations/*
var configs embed.FS
func expandPadding(p []float64, scale float64) []float64 {
switch len(p) {
case 1:
return []float64{p[top] * scale, p[top] * scale, p[top] * scale, p[top] * scale}
case 2:
return []float64{p[top] * scale, p[right] * scale, p[top] * scale, p[right] * scale}
case 4:
return []float64{p[top] * scale, p[right] * scale, p[bottom] * scale, p[left] * scale}
default:
return []float64{0, 0, 0, 0}
}
}
var expandMargin = expandPadding
type side int
const (
top side = 0
right side = 1
bottom side = 2
left side = 3
)
var userConfigPath = filepath.Join(xdg.ConfigHome, "freeze", "user.json")
func loadUserConfig() (fs.File, error) {
return os.Open(userConfigPath) //nolint: wrapcheck
}
func saveUserConfig(config Config) error {
config.Input = ""
config.Output = ""
config.Interactive = false
err := os.MkdirAll(filepath.Dir(userConfigPath), os.ModePerm) //nolint:gosec
if err != nil {
return err //nolint: wrapcheck
}
f, err := os.Create(userConfigPath)
if err != nil {
return err //nolint: wrapcheck
}
b, err := json.Marshal(config)
if err != nil {
return err //nolint: wrapcheck
}
_, err = f.Write(b)
printFilenameOutput(userConfigPath)
return err //nolint: wrapcheck
}