yedit

package module
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 0 Imported by: 0

README

yedit

A reusable TUI library for editing structured YAML files in Go.

yedit turns any Go struct annotated with yaml tags into a two-panel bubbletea editor. The left panel lists the top-level keys discovered from the struct; the right panel shows a live YAML preview. Pressing Enter on a key opens a full-screen block editor where sub-fields can be toggled on/off, edited with presets, or written directly in YAML.

The library is schema-agnostic — clients supply a Go struct, optional presets, and any cross-field validation rules.

Requirements

Go 1.24+

Install

go get github.com/lucasassuncao/yedit

Quick start

package main

import (
    "log"
    "github.com/lucasassuncao/yedit/editor"
)

type Config struct {
    Name  string `yaml:"name"`
    Image string `yaml:"image"`
    Build *struct {
        Dockerfile string `yaml:"dockerfile"`
        Context    string `yaml:"context"`
    } `yaml:"build"`
}

func main() {
    if err := editor.Run(editor.Config{
        Path:   "config.yaml",
        Schema: &Config{},
        Title:  "my editor",
    }); err != nil {
        log.Fatal(err)
    }
}

A non-existent Path is not an error — the editor starts with an empty document and saves to that path on Ctrl+S.

UI layout

The editor presents three distinct panels depending on the field type:

Main list — shows all top-level keys split into ADDED (present in the file) and AVAILABLE (schema-known but not yet set). Enter opens a block.

Struct tree (KindStruct) — left panel lists sub-fields in ADDED / AVAILABLE sections; Space toggles a field on/off. Enter expands or collapses a node. The right panel shows a live YAML preview.

Sequence navigator (KindSlice with child defs) — left panel shows [0] item, [1] item … and a [+ add new] row. Enter expands an item or adds a new one. Ctrl+D deletes the selected item.

YAML-only (KindScalar, KindMap, plain []string) — left panel shows (no fields); the YAML editor takes focus immediately.

Keyboard reference

Main list
Key Action
/ k, / j Navigate
g / G Jump to top / bottom
/ Filter list
Enter Open or add block
Ctrl+D Delete block (with confirmation)
Ctrl+U Undo last change
Tab Switch to YAML editor
Ctrl+S Save changes
Ctrl+L Validate document
Esc / q Quit (prompts if unsaved)
Block-edit tree
Key Action
, Navigate
, Expand / collapse node
Enter Add field / sequence item
Ctrl+D Remove field / sequence item
p Open preset picker
Tab Switch to YAML editor
Ctrl+S Commit changes
Esc Back (prompts if uncommitted)
YAML editor (right panel)

Only Tab and Esc are captured — all other keys go to the textarea.

Tags

Only the yaml tag is required. The rest are optional enrichments:

Tag Effect
yaml:"name" Required. The YAML key name.
yaml:"-" Hide field from the editor.
validate:"required" Marks field as required (stored in FieldDef.Required).
validate:"oneof=a b c" Restricts accepted values (stored in FieldDef.OneOf).
jsonschema:"required" Alternative way to mark required.
jsonschema:"default=X" Default value (stored in FieldDef.Default).
jsonschema_description:"..." Description text (stored in FieldDef.Description).

Required, Default, OneOf, and Description are available to external tooling (doc generators, custom renderers) via schema.FieldDef. They are not yet rendered by the built-in UI.

Full Config

editor.Run(editor.Config{
    // Required
    Path:   "config.yaml",
    Schema: &MyConfig{},

    // Optional
    Title:  "my editor",

    // Preset snippets loaded from an fs.FS (see presets.FromFS)
    Presets: presets.FromFS(embedFS, "."),

    // Cross-field validation rules
    Validators: []editor.Validator{
        editor.MutuallyExclusive("image", "build"),
        editor.RequiredWith("service", "dockerComposeFile"),
    },

    // Sub-fields to pre-check when a new block is opened for the first time
    PreCheckedFields: map[string][]string{
        "build": {"dockerfile", "context"},
    },

    // Default YAML snippets inserted when a sub-field is toggled on
    FieldSnippets: map[string]map[string]string{
        "build": {
            "dockerfile": "  dockerfile: Dockerfile\n",
            "context":    "  context: .\n",
        },
    },

    // Top-level keys to hide (e.g. legacy aliases)
    Hidden: []string{"dockerFile"},
})

Union types

Reflection cannot infer the shape of types that wrap a union (scalar / sequence / mapping). Such types opt into a small interface:

type Provider interface {
    YeditSchema() []schema.FieldDef
}

If a field's type implements Provider, the editor uses the returned []FieldDef instead of descending by reflection. The field kind is set to KindUnion and the left panel shows (no fields) — the YAML editor takes focus directly.

type TimeoutValue struct{}

func (TimeoutValue) YeditSchema() []schema.FieldDef {
    return []schema.FieldDef{
        {YAMLName: "connect", Kind: schema.KindScalar},
        {YAMLName: "read",    Kind: schema.KindScalar},
        {YAMLName: "write",   Kind: schema.KindScalar},
    }
}

Presets

Each field can have named presets. Implement presets.Source or use presets.FromFS with a directory tree:

my-presets/
├── build/
│   ├── base.yaml
│   └── multi-stage.yaml
└── customizations/
    └── vscode-go.yaml

The preset picker is opened with p in the block-edit tree panel.

Environment

Variable Effect
NO_COLOR Disables all color output (monochrome mode).

Minimum terminal size: 80 × 20. Below that the editor shows a resize prompt.

Sub-packages

Package Purpose
editor Two-panel TUI; main entry point (editor.Run)
schema Reflection over Go structs into a FieldDef tree; opt-in Provider for union types
document YAML document state: block-level parse / insert / remove / replace, undo, save
presets Source interface + FromFS for per-field YAML snippets
viewer Read-only preset browser TUI
theme Shared palette and layout primitives
components Reusable bubbletea widgets (alert)

Examples

See examples/test for a self-contained program that exercises all three UI patterns, KindUnion, nested slices, deep nesting, oneof, MutuallyExclusive, and unknown-key validation.

Status

Pre-1.0. The API may change between minor versions until v1.0.0.

License

MIT — see LICENSE.

Documentation

Overview

Package yedit provides reusable building blocks for TUI editors over structured YAML files.

The library is composed of independent sub-packages:

  • schema: reflection over the client's Go structs (yaml tag required; validate and jsonschema_description optional)
  • document: YAML state with block-level mutations, history, and parsing
  • editor: two-panel bubbletea TUI that ties the pieces together
  • presets: Source interface + fs.FS-backed implementation for per-field YAML snippets
  • viewer: read-only TUI to browse a preset Source
  • theme: palette and layout primitives (header, panels, two-column layout)
  • components: bubbletea widgets (alert) that depend only on theme

yedit is intentionally headless of any specific YAML schema. Clients pass a pointer to their own annotated struct and (optionally) a preset Source; the editor introspects the struct via the schema package and renders an add/edit/remove UI keyed by the canonical top-level order.

See editor.Run and editor.Config for the main entry point.

Directories

Path Synopsis
components
alert
Package alert provides a modal alert/confirm component for bubbletea TUIs.
Package alert provides a modal alert/confirm component for bubbletea TUIs.
Package document provides primitives for editing YAML files structured as a flat mapping of top-level keys ("blocks").
Package document provides primitives for editing YAML files structured as a flat mapping of top-level keys ("blocks").
Package editor provides the bubbletea TUI for editing a YAML file driven by a struct-based schema and a preset source.
Package editor provides the bubbletea TUI for editing a YAML file driven by a struct-based schema and a preset source.
examples
test command
Command test is a self-contained yedit example that exercises every schema pattern and known edge case.
Command test is a self-contained yedit example that exercises every schema pattern and known edge case.
Package presets defines the Source interface for per-field YAML presets and provides a filesystem-backed implementation.
Package presets defines the Source interface for per-field YAML presets and provides a filesystem-backed implementation.
Package schema discovers the editable shape of a Go struct via reflection over yaml/validate/jsonschema tags.
Package schema discovers the editable shape of a Go struct via reflection over yaml/validate/jsonschema tags.
Package theme provides the palette, base lipgloss styles, and shared layout primitives used across yedit-built TUIs.
Package theme provides the palette, base lipgloss styles, and shared layout primitives used across yedit-built TUIs.
Package viewer is a read-only TUI that browses the presets exposed by a presets.Source.
Package viewer is a read-only TUI that browses the presets exposed by a presets.Source.

Jump to

Keyboard shortcuts

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