Files
freezelib/freeze.go

446 lines
12 KiB
Go

// Package freezelib provides a Go library for generating beautiful code screenshots
// from source code and terminal output.
//
// This library is based on the freeze CLI tool by Charm and provides a programmatic
// interface for creating code screenshots with syntax highlighting, themes, and
// various styling options.
package freezelib
import (
"fmt"
"io"
"os"
"sort"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
)
// Freeze is the main interface for generating code screenshots
type Freeze struct {
generator *Generator
config *Config
}
// New creates a new Freeze instance with default configuration
func New() *Freeze {
config := DefaultConfig()
return &Freeze{
generator: NewGenerator(config),
config: config,
}
}
// NewWithConfig creates a new Freeze instance with the provided configuration
func NewWithConfig(config *Config) *Freeze {
if config == nil {
config = DefaultConfig()
}
return &Freeze{
generator: NewGenerator(config),
config: config,
}
}
// NewWithPreset creates a new Freeze instance with a preset configuration
func NewWithPreset(presetName string) *Freeze {
config := GetPreset(presetName)
return &Freeze{
generator: NewGenerator(config),
config: config,
}
}
// Config returns the current configuration
func (f *Freeze) Config() *Config {
return f.config
}
// SetConfig updates the configuration and recreates the generator
func (f *Freeze) SetConfig(config *Config) *Freeze {
f.config = config
f.generator = NewGenerator(config)
return f
}
// UpdateConfig allows modifying the current configuration
func (f *Freeze) UpdateConfig(fn func(*Config)) *Freeze {
fn(f.config)
f.generator = NewGenerator(f.config)
return f
}
// GenerateFromCode generates an SVG screenshot from source code
func (f *Freeze) GenerateFromCode(code, language string) ([]byte, error) {
return f.generator.GenerateFromCode(code, language)
}
// GenerateFromCodeAuto generates an SVG screenshot from source code with automatic language detection
func (f *Freeze) GenerateFromCodeAuto(code string) ([]byte, error) {
return f.generator.GenerateFromCode(code, "")
}
// GenerateFromFile generates an SVG screenshot from a source code file
func (f *Freeze) GenerateFromFile(filename string) ([]byte, error) {
return f.generator.GenerateFromFile(filename)
}
// GenerateFromReader generates an SVG screenshot from a reader containing source code
func (f *Freeze) GenerateFromReader(reader io.Reader, language string) ([]byte, error) {
content, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("failed to read from reader: %w", err)
}
return f.generator.GenerateFromCode(string(content), language)
}
// GenerateFromANSI generates an SVG screenshot from ANSI terminal output
func (f *Freeze) GenerateFromANSI(ansiOutput string) ([]byte, error) {
return f.generator.GenerateFromANSI(ansiOutput)
}
// GeneratePNGFromCode generates a PNG screenshot from source code
func (f *Freeze) GeneratePNGFromCode(code, language string) ([]byte, error) {
svgData, err := f.generator.GenerateFromCode(code, language)
if err != nil {
return nil, err
}
// Calculate dimensions for PNG (use 4x scale for better quality)
width := f.config.Width
height := f.config.Height
if width == 0 || height == 0 {
// Use default dimensions with 4x scale
width = 800 * 4
height = 600 * 4
} else {
width *= 4
height *= 4
}
return f.generator.ConvertToPNG(svgData, width, height)
}
// GeneratePNGFromCodeAuto generates a PNG screenshot from source code with automatic language detection
func (f *Freeze) GeneratePNGFromCodeAuto(code string) ([]byte, error) {
svgData, err := f.generator.GenerateFromCode(code, "")
if err != nil {
return nil, err
}
// Calculate dimensions for PNG (use 4x scale for better quality)
width := f.config.Width
height := f.config.Height
if width == 0 || height == 0 {
// Use default dimensions with 4x scale
width = 800 * 4
height = 600 * 4
} else {
width *= 4
height *= 4
}
return f.generator.ConvertToPNG(svgData, width, height)
}
// GeneratePNGFromFile generates a PNG screenshot from a source code file
func (f *Freeze) GeneratePNGFromFile(filename string) ([]byte, error) {
svgData, err := f.generator.GenerateFromFile(filename)
if err != nil {
return nil, err
}
// Calculate dimensions for PNG
width := f.config.Width
height := f.config.Height
if width == 0 || height == 0 {
width = 800 * 4
height = 600 * 4
} else {
width *= 4
height *= 4
}
return f.generator.ConvertToPNG(svgData, width, height)
}
// GeneratePNGFromANSI generates a PNG screenshot from ANSI terminal output
func (f *Freeze) GeneratePNGFromANSI(ansiOutput string) ([]byte, error) {
svgData, err := f.generator.GenerateFromANSI(ansiOutput)
if err != nil {
return nil, err
}
// Calculate dimensions for PNG
width := f.config.Width
height := f.config.Height
if width == 0 || height == 0 {
width = 800 * 4
height = 600 * 4
} else {
width *= 4
height *= 4
}
return f.generator.ConvertToPNG(svgData, width, height)
}
// SaveToFile saves the generated SVG to a file
func (f *Freeze) SaveToFile(data []byte, filename string) error {
return os.WriteFile(filename, data, 0644)
}
// SaveCodeToFile generates and saves a code screenshot to a file
func (f *Freeze) SaveCodeToFile(code, language, filename string) error {
var data []byte
var err error
if isPNGFile(filename) {
data, err = f.GeneratePNGFromCode(code, language)
} else {
data, err = f.GenerateFromCode(code, language)
}
if err != nil {
return err
}
return f.SaveToFile(data, filename)
}
// SaveCodeToFileAuto generates and saves a code screenshot to a file with automatic language detection
func (f *Freeze) SaveCodeToFileAuto(code, filename string) error {
var data []byte
var err error
if isPNGFile(filename) {
data, err = f.GeneratePNGFromCodeAuto(code)
} else {
data, err = f.GenerateFromCodeAuto(code)
}
if err != nil {
return err
}
return f.SaveToFile(data, filename)
}
// SaveFileToFile generates and saves a file screenshot to a file
func (f *Freeze) SaveFileToFile(inputFile, outputFile string) error {
var data []byte
var err error
if isPNGFile(outputFile) {
data, err = f.GeneratePNGFromFile(inputFile)
} else {
data, err = f.GenerateFromFile(inputFile)
}
if err != nil {
return err
}
return f.SaveToFile(data, outputFile)
}
// SaveANSIToFile generates and saves an ANSI screenshot to a file
func (f *Freeze) SaveANSIToFile(ansiOutput, filename string) error {
var data []byte
var err error
if isPNGFile(filename) {
data, err = f.GeneratePNGFromANSI(ansiOutput)
} else {
data, err = f.GenerateFromANSI(ansiOutput)
}
if err != nil {
return err
}
return f.SaveToFile(data, filename)
}
// Clone creates a copy of the Freeze instance with the same configuration
func (f *Freeze) Clone() *Freeze {
return NewWithConfig(f.config.Clone())
}
// WithTheme creates a new Freeze instance with the specified theme
func (f *Freeze) WithTheme(theme string) *Freeze {
clone := f.Clone()
clone.config.SetTheme(theme)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithFont creates a new Freeze instance with the specified font
func (f *Freeze) WithFont(family string, size float64) *Freeze {
clone := f.Clone()
clone.config.SetFont(family, size)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithBackground creates a new Freeze instance with the specified background color
func (f *Freeze) WithBackground(color string) *Freeze {
clone := f.Clone()
clone.config.SetBackground(color)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithWindow creates a new Freeze instance with window controls enabled/disabled
func (f *Freeze) WithWindow(enabled bool) *Freeze {
clone := f.Clone()
clone.config.SetWindow(enabled)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithLineNumbers creates a new Freeze instance with line numbers enabled/disabled
func (f *Freeze) WithLineNumbers(enabled bool) *Freeze {
clone := f.Clone()
clone.config.SetLineNumbers(enabled)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithShadow creates a new Freeze instance with shadow settings
func (f *Freeze) WithShadow(blur, x, y float64) *Freeze {
clone := f.Clone()
clone.config.SetShadow(blur, x, y)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithBorder creates a new Freeze instance with border settings
func (f *Freeze) WithBorder(width, radius float64, color string) *Freeze {
clone := f.Clone()
clone.config.SetBorder(width, radius, color)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithPadding creates a new Freeze instance with padding settings
func (f *Freeze) WithPadding(values ...float64) *Freeze {
clone := f.Clone()
clone.config.SetPadding(values...)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithMargin creates a new Freeze instance with margin settings
func (f *Freeze) WithMargin(values ...float64) *Freeze {
clone := f.Clone()
clone.config.SetMargin(values...)
clone.generator = NewGenerator(clone.config)
return clone
}
// WithDimensions creates a new Freeze instance with specific dimensions
func (f *Freeze) WithDimensions(width, height float64) *Freeze {
clone := f.Clone()
clone.config.SetDimensions(width, height)
clone.generator = NewGenerator(clone.config)
return clone
}
// DetectLanguage detects the programming language from code content
func (f *Freeze) DetectLanguage(code string) string {
return f.generator.DetectLanguage(code)
}
// DetectLanguageFromFilename detects the programming language from filename
func (f *Freeze) DetectLanguageFromFilename(filename string) string {
return f.generator.DetectLanguageFromFilename(filename)
}
// DetectLanguageFromFile detects language from both filename and content
func (f *Freeze) DetectLanguageFromFile(filename, content string) string {
return f.generator.DetectLanguageFromFile(filename, content)
}
// GetSupportedLanguages returns a sorted list of all supported programming languages
func (f *Freeze) GetSupportedLanguages() []string {
languages := lexers.Names(false)
sort.Strings(languages)
return languages
}
// IsLanguageSupported checks if a language is supported
func (f *Freeze) IsLanguageSupported(language string) bool {
return f.generator.IsLanguageSupported(language)
}
// SetLanguageDetector sets a custom language detector
func (f *Freeze) SetLanguageDetector(detector *LanguageDetector) *Freeze {
f.generator.SetLanguageDetector(detector)
return f
}
// GetLanguageDetector returns the current language detector
func (f *Freeze) GetLanguageDetector() *LanguageDetector {
return f.generator.GetLanguageDetector()
}
// GetSupportedThemes returns a sorted list of all supported themes
func (f *Freeze) GetSupportedThemes() []string {
var themes []string
for name := range styles.Registry {
themes = append(themes, name)
}
sort.Strings(themes)
return themes
}
// IsThemeSupported checks if a theme is supported
func (f *Freeze) IsThemeSupported(theme string) bool {
return f.generator.IsThemeSupported(theme)
}
// GetAvailablePresets returns a sorted list of all available presets
func (f *Freeze) GetAvailablePresets() []string {
presets := ListPresets()
sort.Strings(presets)
return presets
}
// isPNGFile checks if the filename has a PNG extension
func isPNGFile(filename string) bool {
return len(filename) > 4 && filename[len(filename)-4:] == ".png"
}
// Global convenience functions for accessing supported options
// GetSupportedLanguages returns a sorted list of all supported programming languages
func GetSupportedLanguages() []string {
languages := lexers.Names(false)
sort.Strings(languages)
return languages
}
// GetSupportedThemes returns a sorted list of all supported themes
func GetSupportedThemes() []string {
var themes []string
for name := range styles.Registry {
themes = append(themes, name)
}
sort.Strings(themes)
return themes
}
// GetAvailablePresets returns a sorted list of all available presets
func GetAvailablePresets() []string {
presets := ListPresets()
sort.Strings(presets)
return presets
}
// Version information
const (
Version = "1.0.0"
Author = "Charm"
)