input

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 5 Imported by: 0

README

Input Plugins

This directory contains built-in input plugins that extract or generate colour palettes from various sources. Input plugins are the first step in Tinct's pipeline - they produce raw colour palettes that are then categorized into semantic roles (background, foreground, accent1, etc.).

Overview

Input plugins extract colours from sources like:

  • Images - Wallpapers, photos, artwork
  • Remote APIs - JSON theme APIs, CSS variable files
  • Files - Saved palette files
  • Generators - Algorithmic palette generation

The extracted colours are passed to the categorizer which assigns semantic roles based on luminance, contrast, and colour theory.

Built-in Input Plugins

Plugin Description Source Type Theme Detection
image Extract from images using k-means clustering Local files, HTTP(S) URLs ✅ Auto-detects dark/light
file Load from saved palette files JSON, YAML files ✅ Preserves theme type
remotejson Fetch from JSON APIs with JSONPath queries HTTP(S) URLs ❌ Uses categorizer
remotecss Extract from CSS files (variables, hex codes) HTTP(S) URLs ❌ Uses categorizer

Directory Structure

internal/plugin/input/
├── README.md              # This file
├── plugin.go              # Input plugin interface and registry
├── image/                 # Image extraction plugin
│   ├── image.go           # Main plugin implementation
│   └── seed.go            # K-means seed generation
├── file/                  # File loading plugin
│   ├── file.go            # Load from JSON/YAML
│   └── file_test.go       # Unit tests
├── remotejson/            # Remote JSON fetching plugin
│   └── remotejson.go      # Fetch and parse JSON
├── remotecss/             # Remote CSS extraction plugin
│   └── remotecss.go       # Parse CSS variables/hex codes
└── shared/                # Shared utilities
    └── regions/           # Ambient region extraction
        ├── README.md      # Region extraction docs
        └── regions.go     # Edge/corner colour extraction

Plugin Interface

Required Methods

All input plugins must implement the Plugin interface:

type Plugin interface {
    // Name returns the plugin identifier (used in CLI: -i <name>)
    Name() string

    // Description returns human-readable description
    Description() string

    // Version returns plugin version (e.g., "1.0.0")
    Version() string

    // Generate extracts colours from the source
    // Returns raw colour palette (categorisation happens separately)
    Generate(ctx context.Context, opts GenerateOptions) (*colour.Palette, error)

    // RegisterFlags registers CLI flags (e.g., --image.path)
    RegisterFlags(cmd *cobra.Command)

    // Validate checks required configuration is provided
    Validate() error
}
Optional Interfaces

Input plugins can implement optional interfaces for additional functionality:

WallpaperProvider

Provides the source wallpaper path for wallpaper-setting plugins:

type WallpaperProvider interface {
    // WallpaperPath returns path to source wallpaper image
    WallpaperPath() string
}

Implementation: image plugin
Used by: Output plugins like hyprpaper, hyprlock to set wallpaper

ThemeHinter

Suggests theme type (dark/light) to help the categorizer:

type ThemeHinter interface {
    // ThemeHint returns "dark", "light", "auto", or "" for no hint
    ThemeHint() string
}

Implementation: image, file plugins
Purpose: Advisory only - categorizer makes final decision

Plugin Details

image Plugin

Extracts colours from images using k-means clustering.

Features:

  • K-means clustering with configurable seed modes
  • Supports local files and HTTP(S) URLs
  • Optional ambient region extraction (edge/corner colours)
  • Theme detection based on dominant luminance
  • Wallpaper provider for output plugins

Seed Modes:

  • content (default) - Hash of image pixels (deterministic per image)
  • filepath - Hash of file path (deterministic per location)
  • manual - User-provided seed value
  • random - Non-deterministic random seed

CLI Flags:

--image.path, -p          # Image path or URL (required)
--image.algorithm         # Extraction algorithm (default: kmeans)
--image.colours           # Number of colours to extract (default: 16)
--image.extractAmbience   # Extract edge/corner regions
--image.regions           # Number of regions (4, 8, 12, 16)
--image.seed-mode         # Seed mode: content, filepath, manual, random
--image.seed-value        # Seed value (for manual mode)

Example:

tinct generate -i image -p wallpaper.jpg -o hyprland,kitty

See: Ambient Region Extraction

file Plugin

Loads colour palettes from saved files (JSON, YAML).

Features:

  • Load from JSON or YAML palette files
  • Preserves semantic roles and theme type
  • Useful for reusing generated palettes
  • Fast - no extraction needed

File Format:

{
  "colours": [
    {"hex": "#1e1e2e", "role": "background"},
    {"hex": "#cdd6f4", "role": "foreground"},
    {"hex": "#f38ba8", "role": "accent1"}
  ],
  "theme_type": "dark"
}

CLI Flags:

--file.path               # Path to palette file (required)

Example:

tinct generate -i file -p saved-palette.json -o kitty
remotejson Plugin

Fetches colour palettes from remote JSON APIs.

Features:

  • HTTP(S) URL support
  • JSONPath queries for complex JSON structures
  • Colour format auto-detection (hex, rgb, hsl)
  • Useful for theme repositories (Catppuccin, Dracula, etc.)

CLI Flags:

--remote-json.url         # JSON URL (required)
--remote-json.path        # JSONPath query (optional)
--remote-json.format      # Colour format hint: hex, rgb, hsl (optional)

Example:

tinct generate -i remote-json \
  --remote-json.url "https://raw.githubusercontent.com/catppuccin/palette/main/palette.json" \
  -o hyprland,kitty
remotecss Plugin

Extracts colours from remote CSS files.

Features:

  • Parse CSS custom properties (variables)
  • Extract hex colour codes
  • Useful for loading CSS frameworks or theme files

CLI Flags:

--remote-css.url          # CSS file URL (required)

Example:

tinct generate -i remote-css \
  --remote-css.url "https://example.com/theme.css" \
  -o waybar

Creating a New Input Plugin

Step-by-Step Guide
  1. Create plugin directory:

    mkdir -p internal/plugin/input/myplugin
    cd internal/plugin/input/myplugin
    
  2. Implement the Plugin interface (myplugin.go):

    package myplugin
    
    import (
        "context"
        "fmt"
        "image/colour"
    
        "github.com/jmylchreest/tinct/internal/colour"
        "github.com/jmylchreest/tinct/internal/plugin/input"
        "github.com/spf13/cobra"
    )
    
    type Plugin struct {
        sourcePath string
        verbose    bool
    }
    
    func New() *Plugin {
        return &Plugin{}
    }
    
    func (p *Plugin) Name() string {
        return "myplugin"
    }
    
    func (p *Plugin) Description() string {
        return "Extract colours from my source"
    }
    
    func (p *Plugin) Version() string {
        return "1.0.0"
    }
    
    func (p *Plugin) RegisterFlags(cmd *cobra.Command) {
        cmd.Flags().StringVar(&p.sourcePath, "myplugin.path", "", "Source path (required)")
    }
    
    func (p *Plugin) Validate() error {
        if p.sourcePath == "" {
            return fmt.Errorf("source path is required (use --myplugin.path)")
        }
        return nil
    }
    
    func (p *Plugin) Generate(ctx context.Context, opts input.GenerateOptions) (*colour.Palette, error) {
        p.verbose = opts.Verbose
    
        if opts.Verbose {
            fmt.Println("Extracting colours from:", p.sourcePath)
        }
    
        // Extract colours from your source
        colours := []colour.Colour{
            colour.RGBA{R: 30, G: 30, B: 46, A: 255},   // Example colours
            colour.RGBA{R: 205, G: 214, B: 244, A: 255},
        }
    
        return &colour.Palette{
            Colours: colours,
        }, nil
    }
    
  3. Implement optional interfaces (if needed):

    // If your plugin provides a wallpaper
    func (p *Plugin) WallpaperPath() string {
        return p.sourcePath
    }
    
    // If your plugin can detect theme type
    func (p *Plugin) ThemeHint() string {
        // Analyze your source and return "dark" or "light"
        return "dark"
    }
    
  4. Add tests (myplugin_test.go):

    package myplugin
    
    import (
        "context"
        "testing"
    
        "github.com/jmylchreest/tinct/internal/plugin/input"
    )
    
    func TestGenerate(t *testing.T) {
        p := New()
        p.sourcePath = "test-source"
    
        opts := input.GenerateOptions{
            Verbose: false,
            DryRun:  false,
        }
    
        palette, err := p.Generate(context.Background(), opts)
        if err != nil {
            t.Fatalf("Generate failed: %v", err)
        }
    
        if len(palette.Colours) == 0 {
            t.Error("Expected colours, got empty palette")
        }
    }
    
  5. Register the plugin in internal/cli/generate.go:

    import "github.com/jmylchreest/tinct/internal/plugin/input/myplugin"
    
    func init() {
        // ... existing plugins ...
        registerInputPlugin(myplugin.New())
    }
    
  6. Add README (internal/plugin/input/myplugin/README.md):

    • Describe what the plugin does
    • Document CLI flags
    • Provide usage examples
    • List any special requirements
  7. Test the plugin:

    go build ./cmd/tinct
    ./tinct generate -i myplugin --myplugin.path "source" -o hyprland --verbose
    

Generate Options

Input plugins receive GenerateOptions during generation:

type GenerateOptions struct {
    // Verbose enables detailed logging to stderr
    Verbose bool

    // DryRun indicates generation without side effects
    DryRun bool

    // ColourOverrides are manual colour specifications (role=hex)
    // Example: ["background=#1e1e2e", "accent1=#f38ba8"]
    ColourOverrides []string

    // PluginArgs are custom arguments from --plugin-args
    PluginArgs map[string]any
}

Best Practices:

  • Respect Verbose flag - log helpful info to stderr when true
  • Respect DryRun flag - don't download/cache files or make network requests
  • Handle ColourOverrides if appropriate (usually done by categorizer)
  • Parse PluginArgs for plugin-specific configuration

Colour Extraction Best Practices

Number of Colours
  • Too few (< 8): May not capture colour variety
  • Optimal (12-24): Good balance for most use cases
  • Too many (> 32): Diminishing returns, slower categorisation
Colour Quality
  1. Remove duplicates - Colours too similar muddy the palette
  2. Check viability - Very dark/light colours may not categorize well
  3. Preserve variety - Include range of hues, saturations, luminances
Theme Detection

If implementing ThemeHinter:

  • Calculate average luminance of extracted colours
  • If avg luminance < 0.5 → probably dark theme
  • If avg luminance > 0.5 → probably light theme
  • Return "auto" if unsure (let categorizer decide)

Testing

Unit Tests

Test core extraction logic:

go test ./internal/plugin/input/myplugin/...
Integration Tests

Test with actual Tinct commands:

# Test basic extraction
./tinct generate -i myplugin --myplugin.path "test" -o hyprland --dry-run

# Test with verbose
./tinct generate -i myplugin --myplugin.path "test" --verbose

# Test with preview
./tinct generate -i myplugin --myplugin.path "test" --preview
Test Coverage

Aim for > 80% coverage:

go test -cover ./internal/plugin/input/myplugin/...

Shared Utilities

Ambient Region Extraction

The shared/regions package provides edge/corner colour extraction for ambient lighting:

import "github.com/jmylchreest/tinct/internal/plugin/input/shared/regions"

// Extract 8 regions (top, topRight, right, etc.)
regionColors := regions.ExtractRegions(img, regions.Config8Regions)

See: Region Extraction README

Documentation

Each input plugin should document:

What it extracts from - Source type (files, URLs, APIs)
CLI flags - All configuration options
Usage examples - Common use cases
Requirements - Dependencies or prerequisites
Limitations - Known issues or constraints

Contributing

When adding or modifying input plugins:

  1. Follow the Plugin interface - Implement all required methods
  2. Handle errors gracefully - Return meaningful error messages
  3. Add comprehensive tests - Unit tests for core logic
  4. Document thoroughly - README + code comments
  5. Support dry-run - Don't make side effects in dry-run mode
  6. Respect verbose flag - Log helpful info when requested
  7. Consider optional interfaces - Implement WallpaperProvider/ThemeHinter if applicable

Resources

Questions?

  • Check existing plugin implementations in this directory
  • Read the comprehensive development guide in docs/DEVELOPMENT.md
  • Open an issue on GitHub for clarification
  • See examples in contrib/plugins/input/ for external plugin patterns

Documentation

Overview

Package input provides the interface and base types for input plugins.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FlagHelp

type FlagHelp = protocol.FlagHelp

FlagHelp is a type alias to protocol.FlagHelp for backward compatibility. Internal plugins can use input.FlagHelp, while external plugins should use protocol.FlagHelp.

type GenerateOptions

type GenerateOptions struct {
	// Verbose enables verbose output.
	Verbose bool

	// DryRun generates output without side effects.
	DryRun bool

	// Backend is the colour extraction backend to use.
	Backend string

	// ColourOverrides are manual colour specifications (role=hex).
	ColourOverrides []string

	// PluginArgs are custom arguments for this plugin.
	PluginArgs map[string]any
}

GenerateOptions holds options passed to input plugins during generation.

type GenerateResult

type GenerateResult struct {
	// Palette is the generated colour palette.
	Palette *colour.Palette

	// WallpaperPath is the optional path to the source wallpaper image.
	// This is used when --set-wallpaper flag is enabled.
	WallpaperPath string
}

GenerateResult holds the result of input plugin generation.

type Plugin

type Plugin interface {
	// Name returns the plugin's name (e.g., "image", "file").
	Name() string

	// Description returns a human-readable description of the plugin.
	Description() string

	// Version returns the plugin version (e.g., "1.0.0").
	Version() string

	// Generate creates a raw Palette from plugin-specific inputs.
	// opts contains flags and arguments passed from the CLI.
	// Returns a simple list of colors - categorization happens separately.
	Generate(ctx context.Context, opts GenerateOptions) (*colour.Palette, error)

	// RegisterFlags registers plugin-specific flags with cobra command.
	RegisterFlags(cmd *cobra.Command)

	// Validate checks if the plugin has all required inputs configured.
	Validate() error

	// GetFlagHelp returns help information for plugin-specific flags.
	// This allows dynamic help generation based on selected plugins.
	GetFlagHelp() []FlagHelp
}

Plugin represents an input plugin that generates a colour palette.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds all registered input plugins.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new input plugin registry.

func (*Registry) All

func (r *Registry) All() map[string]Plugin

All returns all registered plugins (including disabled ones).

func (*Registry) Get

func (r *Registry) Get(name string) (Plugin, bool)

Get retrieves a plugin by name.

func (*Registry) List

func (r *Registry) List() []string

List returns all registered plugin names.

func (*Registry) Register

func (r *Registry) Register(plugin Plugin)

Register adds a plugin to the registry.

type ThemeHinter

type ThemeHinter interface {
	// ThemeHint returns a suggested theme type based on the plugin's analysis.
	// Returns one of: "auto", "dark", "light", or "" for no hint.
	ThemeHint() string
}

ThemeHinter is an optional interface that input plugins can implement. to provide hints about theme detection to the categorizer. This is purely advisory - the categorizer makes the final decision.

type WallpaperProvider

type WallpaperProvider interface {
	// WallpaperPath returns the canonical path to the source wallpaper image.
	// This path is resolved to be usable from any working directory:
	// - Relative paths are converted to absolute paths
	// - Tilde-prefixed paths (~/...) are preserved for portability
	// - URLs are returned as-is
	// Returns empty string if no wallpaper is available.
	WallpaperPath() string

	// WallpaperRawPath returns the literal path as provided by the user.
	// This is the unmodified input before any path canonicalization.
	// Returns empty string if no wallpaper is available.
	WallpaperRawPath() string
}

WallpaperProvider is an optional interface that input plugins can implement. to provide the source wallpaper path. This is used with the --set-wallpaper flag to allow output plugins to set the wallpaper alongside the generated themes.

Directories

Path Synopsis
Package file provides an input plugin for loading colour palettes from files or manual specifications.
Package file provides an input plugin for loading colour palettes from files or manual specifications.
Package googlegenai provides an input plugin for generating images using Google's Imagen models.
Package googlegenai provides an input plugin for generating images using Google's Imagen models.
Package image provides an input plugin for extracting colour palettes from images.
Package image provides an input plugin for extracting colour palettes from images.
Package markdown provides an input plugin for loading themes from markdown theme files.
Package markdown provides an input plugin for loading themes from markdown theme files.
Package openrouter provides an input plugin for generating images using OpenRouter.ai models.
Package openrouter provides an input plugin for generating images using OpenRouter.ai models.
Package remotecss provides an input plugin for fetching colour palettes from remote CSS sources.
Package remotecss provides an input plugin for fetching colour palettes from remote CSS sources.
Package remotejson provides an input plugin for fetching colour palettes from remote JSON sources with JSONPath queries.
Package remotejson provides an input plugin for fetching colour palettes from remote JSON sources with JSONPath queries.
shared
aiflags
Package aiflags provides shared flags for AI image generation plugins.
Package aiflags provides shared flags for AI image generation plugins.
commonflags
Package commonflags provides shared unprefixed flags for input plugins.
Package commonflags provides shared unprefixed flags for input plugins.
imagecolours
Package imagecolours provides shared helpers for extracting colour palettes from images and saving generation metadata.
Package imagecolours provides shared helpers for extracting colour palettes from images and saving generation metadata.
palettebuilder
Package palettebuilder provides shared helpers for building colour palettes from map[string]string colour sources with optional role-mapping support.
Package palettebuilder provides shared helpers for building colour palettes from map[string]string colour sources with optional role-mapping support.
regions
Package regions provides utilities for extracting colors from specific.
Package regions provides utilities for extracting colors from specific.
seed
Package seed provides utilities for deterministic seed generation for k-means clustering.
Package seed provides utilities for deterministic seed generation for k-means clustering.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL