NewProgram creates a new [Program].
(model Model, opts ...ProgramOption)
| 593 | |
| 594 | // NewProgram creates a new [Program]. |
| 595 | func NewProgram(model Model, opts ...ProgramOption) *Program { |
| 596 | p := &Program{ |
| 597 | initialModel: model, |
| 598 | msgs: make(chan Msg), |
| 599 | errs: make(chan error, 1), |
| 600 | rendererDone: make(chan struct{}), |
| 601 | } |
| 602 | |
| 603 | // Apply all options to the program. |
| 604 | for _, opt := range opts { |
| 605 | opt(p) |
| 606 | } |
| 607 | |
| 608 | // A context can be provided with a ProgramOption, but if none was provided |
| 609 | // we'll use the default background context. |
| 610 | if p.externalCtx == nil { |
| 611 | p.externalCtx = context.Background() |
| 612 | } |
| 613 | // Initialize context and teardown channel. |
| 614 | p.ctx, p.cancel = context.WithCancel(p.externalCtx) |
| 615 | |
| 616 | // if no output was set, set it to stdout |
| 617 | if p.output == nil { |
| 618 | p.output = os.Stdout |
| 619 | } |
| 620 | |
| 621 | // if no environment was set, set it to os.Environ() |
| 622 | if p.environ == nil { |
| 623 | p.environ = os.Environ() |
| 624 | } |
| 625 | |
| 626 | if p.fps < 1 { |
| 627 | p.fps = defaultFPS |
| 628 | } else if p.fps > maxFPS { |
| 629 | p.fps = maxFPS |
| 630 | } |
| 631 | |
| 632 | tracePath, traceOk := os.LookupEnv("TEA_TRACE") |
| 633 | if traceOk && len(tracePath) > 0 { |
| 634 | // We have a trace filepath. |
| 635 | if f, err := os.OpenFile(tracePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600); err == nil { |
| 636 | p.logger = log.New(f, "bubbletea: ", log.LstdFlags|log.Lshortfile) |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | return p |
| 641 | } |
| 642 | |
| 643 | func (p *Program) handleSignals() chan struct{} { |
| 644 | ch := make(chan struct{}) |
searching dependent graphs…