Documentation
¶
Overview ¶
Package editor is baifo's embedded text editor component.
Why we wrote our own instead of using bubbles/textarea: the textarea in bubbles v2 is excellent for chat-style input but it does not expose a hook for per-line styling, which we need to render YAML syntax highlighting (PR B.2b) and to overlay autocomplete modals (PR B.2c). Fork or hack-with-ANSI-escapes were the alternatives; both cost more than writing a small editor of our own.
Scope of this iteration (B.2a):
- rune-by-rune buffer with cursor + selection
- arrow / home / end / pgup / pgdn / ctrl+home / ctrl+end navigation
- selection with Shift+ navigation keys
- copy / cut / paste via the bubbletea v2 OSC52 clipboard helpers
- line numbers gutter, header title, footer with hotkeys + error bar
- Ctrl+S triggers a user-supplied validator; errors stay in the footer and the buffer stays open. On success the editor emits SaveMsg and closes.
- Esc with unsaved changes opens a confirmation modal ("Discard changes? y/N"). Destructive actions always confirm.
What does NOT live here yet (kept as hooks for future PRs):
- LineStyler is wired but always nil in B.2a (highlighting in B.2b)
- Triggers map is wired but always nil in B.2a (completions in B.2c)
The editor is content-agnostic. The caller passes the initial text, a title, and an OnSave func; YAML knowledge lives elsewhere.
Index ¶
- type CancelMsg
- type Completion
- type CompletionContext
- type CompletionProvider
- type LineStyler
- type Model
- func (m *Model) Blur()
- func (m Model) Dirty() bool
- func (m *Model) Focus()
- func (m Model) Focused() bool
- func (Model) Init() tea.Cmd
- func (m *Model) SetSize(width, height int)
- func (m *Model) SetTitle(t string)
- func (m *Model) SetValue(s string)
- func (m Model) Update(msg tea.Msg) (Model, tea.Cmd)
- func (m Model) Value() string
- func (m Model) View() string
- type Options
- type SaveMsg
- type StyledSpan
- type Styles
- type Validator
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CancelMsg ¶
type CancelMsg struct{}
CancelMsg is emitted when the user pressed Esc on a clean buffer, or confirmed the discard prompt. The parent Model closes the editor on receipt; the buffer is not delivered.
type Completion ¶
Completion is a single autocomplete entry. The View will be shown in the picker; Insert is what lands in the buffer when the user picks.
type CompletionContext ¶
type CompletionContext struct {
Line int
Col int
// Lines is a snapshot of the whole buffer at trigger time, one
// entry per row. Providers that need sibling context — e.g. a
// `reasoning:` completer that wants to read the `model:` line
// above it — scan this. Read-only; do not mutate.
Lines []string
}
CompletionContext is what the editor passes to a CompletionProvider when its trigger fires. Kept lean on purpose; B.2c may extend it.
type CompletionProvider ¶
type CompletionProvider func(prefix string, ctx CompletionContext) []Completion
CompletionProvider is the hook B.2c will use for autocomplete modals. Declared here so B.2a-callers can already wire empty maps.
type LineStyler ¶
type LineStyler func(lineNum int, content string) []StyledSpan
LineStyler is the per-line tokenisation hook. The styler receives the full raw line and returns the styled spans the editor should paint. By returning spans rather than a pre-styled string, we let the editor compose highlighting with selection and cursor styles cleanly: the editor knows which byte ranges to override and which to delegate to the styler.
In B.2a this hook is nil (no styling). yamlhl plugs in here.
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the BubbleTea component. Construct with New, drive with Update, render with View. The host Model owns the lifecycle and reads SaveMsg / CancelMsg to decide when to close the editor.
func New ¶
New builds a Model from Options. The cursor starts at (0,0); the buffer comes from Options.InitialValue.
func (*Model) Blur ¶
func (m *Model) Blur()
Blur stops the editor from consuming key events. View() still renders the current state.
func (Model) Dirty ¶
Dirty reports whether the buffer was modified since the last save or load. Useful for the host Model to render a "(modified)" marker in its own header if it likes.
func (*Model) Focus ¶
func (m *Model) Focus()
Focus marks the editor as receiving keypresses. The default Model returned by New is already focused; Focus is here for symmetry with the rest of bubbles.
func (*Model) SetSize ¶
SetSize updates the editor's outer dimensions. Sub-component sizes (viewport, footer area) are derived from these.
func (*Model) SetValue ¶
SetValue replaces the buffer and resets the cursor to (0, 0). Dirty state and validation errors are cleared too \u2014 SetValue is used to switch documents, not to mutate the current one.
func (Model) Update ¶
Update implements tea.Model. The editor handles every keystroke itself when focused; an unfocused editor ignores keys but still receives clipboard and resize messages so View renders correctly.
func (Model) Value ¶
Value returns the full buffer text joined with '\n' and without a trailing newline.
func (Model) View ¶
View implements tea.Model. The layout is:
┌─ {title} ─────────────────────────── {modified-marker} ─┐
│ 1 first line of the buffer │
│ 2 second line, with the cursor here →█ │
│ ... │
│ (rest scrolls in the viewport) │
│ ⚠ validation error 1 │ (optional)
│ ⚠ validation error 2 │
└─ ctrl+s save · esc cancel · ? help ─────────────────────┘
The output is always EXACTLY m.height lines tall. Without this guarantee a paste that grows the buffer beyond the viewport can push the footer below the visible area on some terminals; we pad the body with empty lines to keep the chrome anchored.
type Options ¶
type Options struct {
// Title appears in the header bar. Defaults to "Editor".
Title string
// InitialValue is the buffer text on open. May be empty.
InitialValue string
// OnSave validates the buffer when the user hits Ctrl+S. Required;
// passing nil here defaults to a no-op validator that always
// allows save (useful for free-form edits like /config edit).
OnSave Validator
// LineStyler is the per-line render hook. nil in B.2a.
LineStyler LineStyler
// Triggers maps a literal substring (e.g. "${secret:") to its
// completion provider. nil/empty in B.2a.
Triggers map[string]CompletionProvider
// RequireSaveConfirm, when true, makes Ctrl+S open a "Apply
// changes? [y/N]" prompt before running OnSave. Use this for
// editors that mutate critical state (config upserts).
RequireSaveConfirm bool
// Styles overrides the editor's visual palette. nil uses DefaultStyles().
Styles *Styles
}
Options configures a Model at construction time. Fields are picked to match what the first real consumer (PR A's /config edit) needs.
type SaveMsg ¶
type SaveMsg struct {
// Value is the full buffer text, joined with '\n' line separators
// and WITHOUT a trailing newline. Callers that need POSIX-style
// "final newline" semantics should add one explicitly.
Value string
}
SaveMsg is emitted when the user pressed Ctrl+S and OnSave reported no errors. The parent Model uses it to persist the buffer and close the editor overlay.
type StyledSpan ¶
StyledSpan is one (from, to) byte range with a lipgloss style. The byte indexing matches the raw string the styler received; the editor converts to rune positions when overlaying selection/cursor.
type Styles ¶
type Styles struct {
// Header and footer band (background + foreground + padding).
Header lipgloss.Style
// Gutter is the line-number column style.
Gutter lipgloss.Style
// ErrorLine is the validation-error footer style.
ErrorLine lipgloss.Style
// Selection is the highlighted text style.
Selection lipgloss.Style
// Cursor is the block cursor style.
Cursor lipgloss.Style
// SearchMatch styles a non-current search hit.
SearchMatch lipgloss.Style
// SearchCurrentMatch styles the active search hit.
SearchCurrentMatch lipgloss.Style
// Modal chrome colors for in-editor prompts (discard/save/help).
ModalBorder color.Color
ModalTitleBg color.Color
ModalTitleFg color.Color
ModalText color.Color
ModalDim color.Color
// Completer popup colors.
CompleterBg color.Color
CompleterSelBg color.Color
CompleterFg color.Color
CompleterDim color.Color
CompleterAccent color.Color
CompleterBorder color.Color
}
Styles holds every visual property the editor uses internally. Build one via DefaultStyles() and override fields; nil Options.Styles falls back to DefaultStyles().
func DefaultStyles ¶
func DefaultStyles() Styles
DefaultStyles returns the built-in neutral styles (xterm-256 palette). These read well on most dark terminals and are the stand-alone defaults when no host styles are injected via Options.Styles.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mdhl provides a per-line Markdown highlighter for the embedded editor.
|
Package mdhl provides a per-line Markdown highlighter for the embedded editor. |
|
Package yamlhl provides a single-line YAML syntax highlighter for the embedded editor.
|
Package yamlhl provides a single-line YAML syntax highlighter for the embedded editor. |