feat(bonfire): SSU widgets on charm v2 behind the Output interface

- Spinner, gradient progress bar and the multi-row process view ported
  from SSU to bubbletea v2 (input off, no signal handler, command
  context); boxed tables through lipgloss/table
- Prompts use huh fields and a single-select port of SSU's selector with
  its confirmation screen on an interactive terminal; line prompts off a
  terminal and under --no-ansi are unchanged (piped passwords still work)
- -q hides decoration but keeps data and errors; -n returns defaults and
  Secret fails with a hint on a terminal stdin; --no-ansi and non-TTY
  runs print today's plain lines
- Helpers: GlobalsOf, VerbosityOf, IsQuiet, Interactive, ConfirmProceed,
  Process, Current, Failed, PrintAbove, ItemProgress, ErrAborted
- diary's styled console lines print above a running live view
This commit is contained in:
Jakub Zych
2026-10-21 12:30:53 +02:00
parent cb38c493ce
commit 3f38862b22
15 changed files with 1266 additions and 198 deletions

View File

@@ -39,7 +39,8 @@ type ConsoleOptions struct {
// as dotted key prefixes. Control characters other than tab are removed from
// the message and attribute values, so logged data cannot inject terminal
// escape sequences. Each line is written with one Write call, serialized
// across the handler and its WithAttrs and WithGroup clones.
// across the handler and its WithAttrs and WithGroup clones; while a bonfire
// live view runs, lines print above it through bonfire.PrintAbove.
func NewConsoleHandler(w io.Writer, opts ConsoleOptions) slog.Handler {
source := new(atomic.Bool)
source.Store(opts.AddSource)
@@ -154,6 +155,11 @@ func (h *consoleHandler) Handle(_ context.Context, r slog.Record) error {
b.WriteString(p.Muted.Render(filepath.Base(frame.File) + ":" + strconv.Itoa(frame.Line)))
}
}
// While a bonfire live view (spinner, progress, process view) runs,
// the line prints above it instead of tearing it.
if bonfire.PrintAbove(b.String()) {
return nil
}
b.WriteByte('\n')
h.mu.Lock()