app

package
v1.5.3 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Package app is declared in page.go; this file adds the App root model.

Package app defines the core types for the PromptArena TUI hub shell: the Page interface that every screen implements, AppContext that carries shared runtime dependencies, and the navigation messages used to push/pop pages or signal quit and config-change events.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultMenu

func DefaultMenu(ctx *AppContext) []menuItem

DefaultMenu returns the canonical menu items for the Home page.

View is always enabled (it only needs a results directory). Run, Chat, and Inspect require a loaded config.

func DiscoverConfig

func DiscoverConfig(dir string) (path string, found bool)

DiscoverConfig resolves an arena config path from dir.

If dir is itself a regular file it is returned directly. If dir is a directory, DiscoverConfig looks for config.arena.yaml inside it. Returns (path, true) on success or ("", false) when no config can be found.

func ResultsDirFromConfig

func ResultsDirFromConfig(configPath string) string

ResultsDirFromConfig returns the conventional results directory (out/) next to the given config file. Callers that build an AppContext directly (e.g. chat/config-inspect subcommands) use this so the path matches what LoadConfig would have set.

func Run

func Run(ctx *AppContext, root Page) error

Run launches the PromptArena TUI hub with root as the bottom page on the navigation stack. The splash screen is shown first — it is pushed on top of root so that dismissing it (via any key or the auto-dismiss timer) reveals root. App.Init() calls the top page's Init (once only), so the splash timer fires automatically under the bubbletea runtime.

The stack is seeded as [root, splash] before tea.NewProgram is called, which means:

  • a.Init() → splash.Init() (timer starts; root is NOT yet inited)
  • splash dismiss → PopPageMsg → root becomes top → root.Init() fires once (via initAndActivate in pop()) together with root.Activate() if applicable

The once-only Init guarantee ensures deep-link pages like ChatPage load their agents/setup state when revealed by splash dismiss, not before. Esc/q at root (the only page remaining after splash dismiss) will quit.

Types

type Activatable

type Activatable interface {
	Activate(send func(tea.Msg)) tea.Cmd
}

Activatable is implemented by pages that need to push async messages into the running bubbletea program. The App calls Activate(send) when the page becomes the active/top page: at root startup (in App.Init) and on push. send is *tea.Program.Send — a thread-safe delivery channel. The returned tea.Cmd is an optional startup command; it is batched with the page's Init cmd by the App.

type App

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

App is the root bubbletea model for the PromptArena TUI hub shell. It owns the page navigation stack and routes messages globally.

func New

func New(ctx *AppContext, root Page) *App

New creates a new App with root as the initial (bottom) page on the stack. root must not be nil.

func (*App) Init

func (a *App) Init() tea.Cmd

Init implements tea.Model. It runs the top page's Init command (once only) and batches it with an optional Activate cmd if the page implements Activatable.

func (*App) SetSend

func (a *App) SetSend(send func(tea.Msg))

SetSend stores the program's Send func so that Activatable pages can push messages back into the bubbletea event loop from goroutines. Call this after tea.NewProgram and before p.Run().

func (*App) Update

func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model. It handles global navigation and key messages, forwarding everything else to the top page.

func (*App) View

func (a *App) View() string

View implements tea.Model. It delegates to the top page.

type AppContext

type AppContext struct {
	Config     *config.Config
	ConfigPath string
	ResultsDir string
	StateStore statestore.Store
	Engine     *engine.Engine
	Version    string
	Voice      *VoiceOptions // nil => text chat
}

AppContext carries the shared runtime dependencies injected into every Page by the hub shell. Fields are set once at startup and then treated as read-only by pages.

func (*AppContext) EnsureEngine

func (c *AppContext) EnsureEngine() (*engine.Engine, error)

EnsureEngine returns the cached engine from ctx, building it from ctx.Config if it has not been built yet. It also sets ctx.StateStore from the engine. Returns an error if no config has been loaded.

func (*AppContext) HasConfig

func (c *AppContext) HasConfig() bool

HasConfig reports whether a config has been loaded into this context.

func (*AppContext) LoadConfig

func (c *AppContext) LoadConfig(path string) error

LoadConfig loads the arena configuration from path, sets Config and ConfigPath on the context, and derives ResultsDir as the out/ directory next to the config file.

type ChatPage

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

ChatPage is a hub Page that drives the interactive chat console. It owns the setup flow (agent / provider / variable selection) and the live chat panel driven from the state store after each turn.

Implements Page and Activatable.

func NewChatPage

func NewChatPage(ctx *AppContext) *ChatPage

NewChatPage constructs a ChatPage bound to the given AppContext. Activate must be called before Init to wire the engine.

func (*ChatPage) Activate

func (p *ChatPage) Activate(send func(tea.Msg)) tea.Cmd

Activate implements Activatable. It is called by App before Init. It calls EnsureEngine so Init can proceed. The send handle is stored so startVoice can push messages into the bubbletea loop from goroutines.

func (*ChatPage) Close

func (p *ChatPage) Close()

Close implements Closeable. It cancels any running voice driver so the mic and pipeline shut down cleanly when the user quits.

func (*ChatPage) Init

func (p *ChatPage) Init() tea.Cmd

Init implements Page. It resolves the first setup step, auto-selecting when there is only one agent or provider so simple configs drop straight into chat.

func (*ChatPage) SetSize

func (p *ChatPage) SetSize(w, h int)

SetSize implements Page.

func (*ChatPage) Title

func (p *ChatPage) Title() string

Title implements Page.

func (*ChatPage) Update

func (p *ChatPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. Routes messages by type and current state. NOTE: Esc/Ctrl+C are handled globally by App — do not handle them here. NOTE: WindowSizeMsg is routed by App via SetSize — do not handle it here.

func (*ChatPage) View

func (p *ChatPage) View() string

View implements Page. Renders the current state.

type Closeable

type Closeable interface {
	Close()
}

Closeable is an optional interface a Page may implement to run cleanup logic when the App exits (e.g. canceling a background voice driver). App calls Close on every page in the stack when it processes a tea.Quit or QuitMsg.

type ConfigChangedMsg

type ConfigChangedMsg struct{ Path string }

ConfigChangedMsg is emitted when the user loads or changes the arena config file. Path is the absolute path to the newly loaded file.

type ConfigSwitchPage

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

ConfigSwitchPage wraps the file browser to let the user pick an arena config at runtime. On a successful selection it calls ctx.LoadConfig then emits both ConfigChangedMsg and PopPageMsg. On error it stays open and surfaces the error in its View.

func NewConfigSwitchPage

func NewConfigSwitchPage(ctx *AppContext, startDir string) *ConfigSwitchPage

NewConfigSwitchPage creates a ConfigSwitchPage rooted at startDir. startDir is the initial directory shown in the file browser; a sensible default (dir of the current config, or ".") is chosen by callers.

func (*ConfigSwitchPage) Init

func (p *ConfigSwitchPage) Init() tea.Cmd

Init implements Page. Delegates to the underlying file browser.

func (*ConfigSwitchPage) SetSize

func (p *ConfigSwitchPage) SetSize(w, h int)

SetSize implements Page. Stores dimensions for View; does NOT call SetDimensions on the browser here because Render() is called from View() where we apply dimensions immediately before rendering.

func (*ConfigSwitchPage) Title

func (p *ConfigSwitchPage) Title() string

Title implements Page.

func (*ConfigSwitchPage) Update

func (p *ConfigSwitchPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. It intercepts FileSelectedMsg from the browser and drives the load-config flow; all other messages are forwarded to the browser.

Filtering note: FileBrowserPage uses AllowedTypes (extension list) which does not support glob patterns like "*.arena.yaml". The browser is therefore left unrestricted; invalid selections (non-YAML, non-arena files) are rejected via the LoadConfig error path and surfaced in the view.

func (*ConfigSwitchPage) View

func (p *ConfigSwitchPage) View() string

View implements Page. Shows the file browser, or an error banner when the last selection failed.

type ConversationViewPage

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

ConversationViewPage wraps pages.ConversationPage for use inside the hub shell navigation stack.

func NewConversationViewPage

func NewConversationViewPage(runID, scenarioID, providerID string, result *statestore.RunResult) *ConversationViewPage

NewConversationViewPage creates a ConversationViewPage pre-loaded with the given run data.

func (*ConversationViewPage) Init

func (p *ConversationViewPage) Init() tea.Cmd

Init implements Page.

func (*ConversationViewPage) SetSize

func (p *ConversationViewPage) SetSize(w, h int)

SetSize implements Page.

func (*ConversationViewPage) Title

func (p *ConversationViewPage) Title() string

Title implements Page.

func (*ConversationViewPage) Update

func (p *ConversationViewPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. Forwards messages to the underlying conversation panel.

func (*ConversationViewPage) View

func (p *ConversationViewPage) View() string

View implements Page.

type Home

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

Home is the top-level navigation page for the PromptArena TUI hub shell. It is generic over its menu items so that the concrete Run/View/Chat/Inspect entries can be injected by the top-level wiring (Task 8) without baking them in here.

func NewHome

func NewHome(ctx *AppContext, items []menuItem) *Home

NewHome creates a Home page backed by ctx and populated with items. Items whose needsConfig field is true are greyed and non-selectable when ctx.HasConfig() returns false.

func (*Home) Init

func (h *Home) Init() tea.Cmd

Init implements Page. Home has no background commands to start.

func (*Home) SetSize

func (h *Home) SetSize(width, height int)

SetSize implements Page.

func (*Home) Title

func (h *Home) Title() string

Title implements Page.

func (*Home) Update

func (h *Home) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. It handles cursor movement (up/down/j/k), selection (Enter), and the 'c' key to open the config switcher.

func (*Home) View

func (h *Home) View() string

View implements Page. It renders a small heading, a config indicator line, and the menu with disabled items rendered faint/greyed.

type InspectPage

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

InspectPage is a scrollable hub page that displays the configuration inspector output (the same content as `promptarena config-inspect`).

func NewInspectPage

func NewInspectPage(ctx *AppContext) *InspectPage

NewInspectPage creates an InspectPage with default render options. If ctx is nil or has no config loaded, a placeholder message is shown instead. This is the entry point used by the Home menu.

func NewInspectPageWithOptions

func NewInspectPageWithOptions(ctx *AppContext, opts inspect.RenderOptions) *InspectPage

NewInspectPageWithOptions creates an InspectPage with the given render options, allowing callers to thread --verbose/--section/--stats/--short flags through from the CLI. If ctx is nil or has no config loaded, a placeholder is shown.

func (*InspectPage) Init

func (p *InspectPage) Init() tea.Cmd

Init implements Page.

func (*InspectPage) SetSize

func (p *InspectPage) SetSize(w, h int)

SetSize implements Page. Initializes (or re-initializes) the viewport.

func (*InspectPage) Title

func (p *InspectPage) Title() string

Title implements Page.

func (*InspectPage) Update

func (p *InspectPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. Handles scroll key bindings.

func (*InspectPage) View

func (p *InspectPage) View() string

View implements Page.

type Page

type Page interface {
	Init() tea.Cmd
	Update(tea.Msg) (Page, tea.Cmd)
	View() string
	Title() string
	SetSize(w, h int)
}

Page is the interface that every screen in the TUI hub must implement. It mirrors the bubbletea Model interface but returns (Page, tea.Cmd) from Update so that the shell can swap the active page in response to navigation messages.

type PopPageMsg

type PopPageMsg struct{}

PopPageMsg instructs the hub shell to pop the current page, returning to the previous one.

type PushPageMsg

type PushPageMsg struct{ Page Page }

PushPageMsg instructs the hub shell to push a new page onto the navigation stack, making it the active page.

type QuitMsg

type QuitMsg struct{}

QuitMsg instructs the hub shell to exit the TUI.

type ResultsPage

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

ResultsPage wraps MainPage to display the results loaded from a summary (index.json). The user can navigate the runs table and press Enter to dive into a ConversationViewPage.

func NewResultsPage

func NewResultsPage(summary map[string]interface{}, resultsDir string) *ResultsPage

NewResultsPage creates a ResultsPage for the given summary and directory.

func (*ResultsPage) Init

func (p *ResultsPage) Init() tea.Cmd

Init implements Page. Triggers async loading of all run results listed in the summary.

func (*ResultsPage) SetSize

func (p *ResultsPage) SetSize(w, h int)

SetSize implements Page.

func (*ResultsPage) Title

func (p *ResultsPage) Title() string

Title implements Page.

func (*ResultsPage) Update

func (p *ResultsPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page.

func (*ResultsPage) View

func (p *ResultsPage) View() string

View implements Page.

type RunPage

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

RunPage drives the live run view inside the hub shell. It wraps the tui.Model (the 3-pane runs/logs/result view) and, on Activate, wires an event bus + adapter to stream runtime events into the model and kicks off engine.ExecuteRuns in a background goroutine. Drill-down into a run's conversation is handled at the App-stack level: when the model reports a selected run, RunPage pushes a ConversationViewPage loaded from the state store.

Implements Page and Activatable.

func NewRunPage

func NewRunPage(
	ctx *AppContext,
	eng *engine.Engine,
	plan *engine.RunPlan,
	concurrency int,
	configFile string,
	totalRuns int,
) *RunPage

NewRunPage builds a RunPage for the given engine and run plan. The run command performs setupEngine / plan generation and hands the results in; RunParameters lives in package main and cannot be imported here, so callers pass the already-built engine, plan, concurrency, configFile and totalRuns.

func NewRunPageFromContext

func NewRunPageFromContext(ctx *AppContext) (*RunPage, error)

NewRunPageFromContext is a convenience constructor for the Home menu factory. It calls EnsureEngine, generates a full (unfiltered) run plan, and returns a RunPage ready to be pushed onto the navigation stack. The default concurrency is 1 so the menu can always succeed without extra flags.

func (*RunPage) Activate

func (p *RunPage) Activate(send func(tea.Msg)) tea.Cmd

Activate implements Activatable. It wires the event bus + adapter to deliver runtime events via send and starts ExecuteRuns in the background. The run results persist to the engine's state store as today. Returns the model's Init command.

func (*RunPage) Cancel

func (p *RunPage) Cancel()

Cancel stops the background run. Safe to call after the hub exits so an early quit ('q' / Ctrl+C) does not leak the ExecuteRuns goroutine.

func (*RunPage) Init

func (p *RunPage) Init() tea.Cmd

Init implements Page. Delegates to the wrapped model.

func (*RunPage) Model

func (p *RunPage) Model() *tui.Model

Model exposes the wrapped tui.Model for testing.

func (*RunPage) Results

func (p *RunPage) Results() (runIDs []string, finished bool, err error)

Results returns the run IDs produced by ExecuteRuns, whether the run finished, and any execution error. Safe for concurrent use; intended to be read by the run command after the hub exits.

func (*RunPage) SetSize

func (p *RunPage) SetSize(w, h int)

SetSize implements Page. Forwards the terminal size to the model as a WindowSizeMsg (the model tracks its own width/height).

func (*RunPage) Title

func (p *RunPage) Title() string

Title implements Page.

func (*RunPage) Update

func (p *RunPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. It delegates to the wrapped model, then checks whether the user selected a run for drill-down; if so it pushes a ConversationViewPage loaded from the state store.

func (*RunPage) View

func (p *RunPage) View() string

View implements Page. Delegates to the wrapped model.

type Splash

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

Splash is the transient splash-screen Page shown at launch. It displays the locked PromptKit logo, a wordmark with the version, and a tagline. It dismisses on any key press or after splashDuration.

func NewSplash

func NewSplash(ctx *AppContext) *Splash

NewSplash creates a Splash page backed by the given AppContext. The version string from ctx is rendered on the screen; pass ctx.Version = "vTEST" (or similar fixed string) in golden tests to keep output byte-stable.

func (*Splash) Init

func (s *Splash) Init() tea.Cmd

Init implements Page. It starts a ~1.5-second timer that fires splashDoneMsg.

func (*Splash) SetSize

func (s *Splash) SetSize(w, h int)

SetSize implements Page.

func (*Splash) Title

func (s *Splash) Title() string

Title implements Page. The splash has no title bar.

func (*Splash) Update

func (s *Splash) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page. Any key press or the expiry timer dismisses the splash by returning a cmd that emits PopPageMsg{}.

func (*Splash) View

func (s *Splash) View() string

View implements Page. It renders the logo, wordmark, version, and tagline centered within the allocated terminal size.

type ViewPage

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

ViewPage wraps a file browser to let the user navigate past Arena result directories. Selecting a file triggers an async load; the result msg is then used to push either a ResultsPage (index.json) or ConversationViewPage.

func NewViewPage

func NewViewPage(resultsDir string) *ViewPage

NewViewPage creates a ViewPage rooted at resultsDir.

func (*ViewPage) Init

func (p *ViewPage) Init() tea.Cmd

Init implements Page. Delegates to the underlying file browser.

func (*ViewPage) SetSize

func (p *ViewPage) SetSize(w, h int)

SetSize implements Page. Stores dimensions for use in View().

func (*ViewPage) Title

func (p *ViewPage) Title() string

Title implements Page.

func (*ViewPage) Update

func (p *ViewPage) Update(msg tea.Msg) (Page, tea.Cmd)

Update implements Page.

  • pages.FileSelectedMsg — start async load (summary or individual result)
  • summaryLoadedViewMsg — push ResultsPage
  • resultLoadedViewMsg — push ConversationViewPage
  • resultLoadErrorViewMsg — store error for View
  • all other messages — forward to browser

func (*ViewPage) View

func (p *ViewPage) View() string

View implements Page.

type VoiceOptions

type VoiceOptions struct {
	STTProviderID string // --voice-stt ("" = ASM/native realtime mode)
	OutputVoice   string // --voice-output-voice
	EchoGuard     bool   // --echo-guard
	BargeIn       bool   // --barge-in (interrupt the agent mid-reply; opt-in)
}

VoiceOptions carries the voice-mode parameters parsed from CLI flags. A nil *VoiceOptions on AppContext means text-chat mode.

Jump to

Keyboard shortcuts

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