transcript

package
v1.0.0-beta.1 Latest Latest
Warning

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

Go to latest
Published: May 24, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package transcript owns the scrollback model of the v2 TUI. It stores every logical entry the user reads in the viewport (banner, user prompts, assistant text, thinking, tool calls + results, system rows, errors, compaction inflight) as a slice of Block values, and renders the scrollback as one styled string the App's viewport scrolls through.

Design highlights vs. v1 (internal/ui/bubbletea/transcript.go):

  • Blocks are an interface, not a tagged struct. Each Kind has its own concrete type with its own internal state. Adding a new Kind is one new file, not a new branch in five switches.

  • PlainText() is a first-class method on Block — yank-mode (M8) and search (M9) read from it. v1 had no way to recover plain text; it always re-rendered.

  • Each Block exposes a Rev() counter. The transcript's per-block render cache keys on (Width, Theme.Rev, Markdown.Rev, Block.Rev, RenderOpts.Rev) so unchanged blocks skip re-rendering on every frame. v1 re-ran glamour on every TextBlock every frame.

  • Inflight blocks (streaming text/thinking, animating compact row) are tracked by pointer, not by index. Pruning blocks ahead of an inflight pointer can't desync state.

  • Gutter glyphs (│, ├─) are applied inside each Block's Render method, not by a transcript-level wrapping pass. The cache holds final form; the transcript only joins blocks with inter-block spacers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BannerBlock

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

BannerBlock renders the spec as a centered double-bordered box. On resize the cache invalidates (width changed) and the block re-renders with the new centering. No special path needed — this is the M3 simplification vs. v1's reflowBanner.

func NewBannerBlock

func NewBannerBlock(spec BannerSpec) *BannerBlock

func (*BannerBlock) ID

func (b *BannerBlock) ID() uint64

func (*BannerBlock) Kind

func (b *BannerBlock) Kind() Kind

func (*BannerBlock) PlainText

func (b *BannerBlock) PlainText() string

PlainText returns the art + greeting + info rows as plain text, useful for the M8 yank-mode copy of the welcome block.

func (*BannerBlock) Render

func (b *BannerBlock) Render(ctx RenderContext) string

func (*BannerBlock) Rev

func (b *BannerBlock) Rev() uint64

func (*BannerBlock) SetSpec

func (b *BannerBlock) SetSpec(spec BannerSpec)

SetSpec replaces the spec and bumps Rev. Used by App.Attach when the controller's metadata (agent ID, model) becomes known after initial construction.

type BannerInfo

type BannerInfo struct {
	Label string
	Value string
}

BannerInfo is a single labeled row in the banner footer.

type BannerSpec

type BannerSpec struct {
	Art      string
	Greeting string
	Info     []BannerInfo
}

BannerSpec captures the per-session welcome data: the ASCII art, the greeting line, and a list of labeled info rows the App populates after Attach (agent ID, model, started-at).

type Block

type Block interface {
	ID() uint64
	Rev() uint64
	Kind() Kind
	PlainText() string
	Render(ctx RenderContext) string
}

Block is the unit the transcript stores. Implementations live in blocks_*.go. The interface is deliberately small: an ID for cache keying, a Rev for cache invalidation, a Kind tag, a PlainText extractor, and a Render method.

Concrete blocks are always passed by pointer — mutation (Append during streaming, SetResult for tool pairing, SetFrame for compact animation) bumps Rev, and a value-receiver would lose those mutations.

type CompactingBlock

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

CompactingBlock animates a spinner while a compaction is in flight. The transcript drives SetFrame from the App's SpinnerTickMsg handler; the frame index bumps Rev so the cache invalidates and the row re-renders.

func (*CompactingBlock) ID

func (b *CompactingBlock) ID() uint64

func (*CompactingBlock) Kind

func (b *CompactingBlock) Kind() Kind

func (*CompactingBlock) PlainText

func (b *CompactingBlock) PlainText() string

func (*CompactingBlock) Render

func (b *CompactingBlock) Render(ctx RenderContext) string

func (*CompactingBlock) Rev

func (b *CompactingBlock) Rev() uint64

func (*CompactingBlock) SetFrame

func (b *CompactingBlock) SetFrame(frame int)

SetFrame updates the spinner index for the next render. Cheap; no allocation. Bumps Rev so the cache invalidates one row.

func (*CompactingBlock) SetKind

func (b *CompactingBlock) SetKind(kind string)

SetKind updates the compaction type label. Called when the agent emits a second KindCompacting (rare — happens when the manual chooser fires while auto-compact is mid-flight).

type ErrorBlock

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

func (*ErrorBlock) ID

func (b *ErrorBlock) ID() uint64

func (*ErrorBlock) Kind

func (b *ErrorBlock) Kind() Kind

func (*ErrorBlock) PlainText

func (b *ErrorBlock) PlainText() string

func (*ErrorBlock) Render

func (b *ErrorBlock) Render(ctx RenderContext) string

func (*ErrorBlock) Rev

func (b *ErrorBlock) Rev() uint64

type Kind

type Kind int

Kind tags a Block so the transcript-level inter-block spacer can decide whether to emit a `│` gutter line or a blank. Components outside the transcript (yank mode, search) also switch on Kind.

const (
	KindBanner     Kind = iota // welcome box (no timeline gutter)
	KindUserPrompt             // cuts the timeline with a scanline header
	KindText                   // assistant final text / markdown
	KindThinking               // dim reasoning text
	KindTool                   // tool_use_start + optional result
	KindSystem                 // draining / cancelled / iter-limit
	KindError                  // KindError red banner
	KindSynthetic              // pre-formatted block injected by the UI
	KindCompacting             // animated inflight compact row
)

type Markdown

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

Markdown wraps glamour with a width-aware constructor so the transcript can re-render TextBlock content when the terminal resizes.

glamour's TermRenderer parses its style once at construction, so we cache one per width — recreate-on-resize keeps line wrapping in sync with the viewport without re-parsing styles on every chunk.

Rev is bumped each time a new instance is constructed; the transcript's block cache keys on it so a width change invalidates every TextBlock without per-block bookkeeping. Atomic so the rev is safe to read from any goroutine, though in practice only the bubbletea main loop touches it.

func NewMarkdown

func NewMarkdown(width int) *Markdown

NewMarkdown builds a renderer keyed to width. Returns a non-nil *Markdown even when glamour init fails — Render falls back to passthrough so callers don't need to nil-check.

width < 20 is clamped to 20 — too narrow to wrap usefully, and glamour throws on tiny widths.

func (*Markdown) Render

func (m *Markdown) Render(s string) string

Render returns the markdown-rendered version of s, falling back to the raw text if glamour is unavailable or the render errors. Trailing newlines are trimmed so blocks join cleanly in the transcript.

func (*Markdown) Rev

func (m *Markdown) Rev() uint64

Rev is the cache-invalidation token. Bumped per construction.

func (*Markdown) Width

func (m *Markdown) Width() int

Width reports the column count this renderer was built for. Transcript uses this to decide whether to rebuild on resize.

type Range

type Range struct {
	Start, End int
}

Range marks a byte offset span inside PlainText() — used by the search overlay (M9) to highlight matches. Start is inclusive, End exclusive (Go slice convention).

type RenderContext

type RenderContext struct {
	Width    int
	Theme    *theme.Theme
	Markdown *Markdown
	Opts     RenderOpts
}

RenderContext carries everything Block.Render needs from the transcript layer: terminal width, the live theme, the active markdown renderer (nil if not constructed yet), and the per-frame opts.

We pass a struct rather than four positional args so future additions (e.g. line-number cursor for M9 search jump-to) don't break every Block implementation.

type RenderOpts

type RenderOpts struct {
	Focused    bool
	Highlights []Range
}

RenderOpts carries ephemeral per-frame flags that influence how a block draws. Default (zero-value) means "normal scrollback render with no decoration".

  • Focused: yank-mode (M8) has selected this block. Render adds a left-edge accent so the user can see what would be copied.
  • Highlights: search (M9) match positions inside PlainText(). Render decorates those byte ranges with a highlight style.

type SyntheticBlock

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

func (*SyntheticBlock) ID

func (b *SyntheticBlock) ID() uint64

func (*SyntheticBlock) Kind

func (b *SyntheticBlock) Kind() Kind

func (*SyntheticBlock) PlainText

func (b *SyntheticBlock) PlainText() string

func (*SyntheticBlock) Render

func (b *SyntheticBlock) Render(ctx RenderContext) string

func (*SyntheticBlock) Rev

func (b *SyntheticBlock) Rev() uint64

type SystemBlock

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

func (*SystemBlock) ID

func (b *SystemBlock) ID() uint64

func (*SystemBlock) Kind

func (b *SystemBlock) Kind() Kind

func (*SystemBlock) PlainText

func (b *SystemBlock) PlainText() string

func (*SystemBlock) Render

func (b *SystemBlock) Render(ctx RenderContext) string

func (*SystemBlock) Rev

func (b *SystemBlock) Rev() uint64

type TextBlock

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

TextBlock holds assistant final text (KindText) or the rolling accumulator for a streamed turn (KindTextChunk). Markdown rendering is deferred to Render time so a streaming chunk only mutates the raw string + bumps Rev; glamour runs on the next cache miss.

Concurrency: not safe for concurrent Append. The bubbletea event loop is the sole writer.

func (*TextBlock) Append

func (b *TextBlock) Append(chunk string)

Append adds chunk to the accumulator and bumps Rev so the cache re-renders this block on the next View pass. Empty chunks are no-ops — they shouldn't bump Rev or the cache thrashes.

func (*TextBlock) ID

func (b *TextBlock) ID() uint64

func (*TextBlock) Kind

func (b *TextBlock) Kind() Kind

func (*TextBlock) PlainText

func (b *TextBlock) PlainText() string

PlainText returns the raw markdown source — useful for yank/copy (users want the markdown, not the rendered ANSI) and for search (search the source, not the post-glamour decorations).

func (*TextBlock) Render

func (b *TextBlock) Render(ctx RenderContext) string

Render builds the gutter-prefixed, glamour-rendered final form. Falls back to th.Assistant when no Markdown renderer is attached (early boot, before the first WindowSizeMsg).

func (*TextBlock) Rev

func (b *TextBlock) Rev() uint64

type ThinkingBlock

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

ThinkingBlock mirrors TextBlock for KindThinking / KindThinkingChunk. Rendered in muted italic (no glamour) — thinking blocks should read as the model's internal aside, not as final content.

func (*ThinkingBlock) Append

func (b *ThinkingBlock) Append(chunk string)

func (*ThinkingBlock) ID

func (b *ThinkingBlock) ID() uint64

func (*ThinkingBlock) Kind

func (b *ThinkingBlock) Kind() Kind

func (*ThinkingBlock) PlainText

func (b *ThinkingBlock) PlainText() string

func (*ThinkingBlock) Render

func (b *ThinkingBlock) Render(ctx RenderContext) string

func (*ThinkingBlock) Rev

func (b *ThinkingBlock) Rev() uint64

type ThinkingSpriteBlock

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

ThinkingSpriteBlock is the "walking" sprite shown when the agent state is StateThinking. Managed by Transcript.ShowThinkingSprite / HideThinkingSprite.

func (*ThinkingSpriteBlock) ID

func (b *ThinkingSpriteBlock) ID() uint64

func (*ThinkingSpriteBlock) Kind

func (b *ThinkingSpriteBlock) Kind() Kind

func (*ThinkingSpriteBlock) PlainText

func (b *ThinkingSpriteBlock) PlainText() string

func (*ThinkingSpriteBlock) Render

func (b *ThinkingSpriteBlock) Render(ctx RenderContext) string

func (*ThinkingSpriteBlock) Rev

func (b *ThinkingSpriteBlock) Rev() uint64

func (*ThinkingSpriteBlock) SetFrame

func (b *ThinkingSpriteBlock) SetFrame(frame int)

SetFrame bumps the animation frame. No-op when frame hasn't changed (avoids cache churn on consecutive ticks with the same index).

type ToolBlock

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

ToolBlock holds a tool call's invocation head and (eventually) its result. Pairing with the result event is done by the transcript via ToolID lookup; ToolBlock itself just exposes setters.

State fields:

  • head: the styled `◢ name(args)` line shown on the gutter
  • rawHead: same content but plain — fed to PlainText() and to the search index
  • name: the tool name (used to decide e.g. web-summary collapse on the result; M3 supports a basic hideResult flag for tool_search)
  • toolID: correlation token from the agent event stream
  • resultBody: styled multi-line result (lazy: empty until the KindToolUseResult event lands and SetResult is called)
  • rawResult: same plain — for PlainText/search
  • resultLines: line count of resultBody, used to drive fold
  • hasError: true when the result was IsError — affects the status glyph and the styling choice
  • hasDiff: true when Metadata carried a *fs.FileDiff — diff results bypass the fold pass (their entire value is the visible artifact)
  • hideResult: true for tool_search and similar — payload is replaced with a "schema loaded" placeholder
  • expanded: transcript-level Ctrl+O fold override

func (*ToolBlock) Expanded

func (b *ToolBlock) Expanded() bool

Expanded reports whether this block's per-block override is currently on. Used by yank mode to flip a single block independently of the transcript-wide Ctrl+O state.

func (*ToolBlock) ID

func (b *ToolBlock) ID() uint64

func (*ToolBlock) Kind

func (b *ToolBlock) Kind() Kind

func (*ToolBlock) Name

func (b *ToolBlock) Name() string

func (*ToolBlock) PlainText

func (b *ToolBlock) PlainText() string

func (*ToolBlock) Render

func (b *ToolBlock) Render(ctx RenderContext) string

Render builds the gutter-prefixed call head + optional folded / expanded result body.

func (*ToolBlock) Rev

func (b *ToolBlock) Rev() uint64

func (*ToolBlock) SetExpanded

func (b *ToolBlock) SetExpanded(v bool)

SetExpanded toggles the per-block "show full result" override. expanded=true forces full render; false defers to fold logic. The transcript-wide Ctrl+O wires through this on every tool block.

func (*ToolBlock) SetResult

func (b *ToolBlock) SetResult(content string, isError bool, diffMeta *fs.FileDiff, imageBlocks []tools.ContentBlock)

SetResult attaches the result body to the block. content is the model-facing summary; isError true means the call failed; diffMeta is non-nil for write_file / edit_file calls. imageBlocks carries multimodal content (e.g. read of a PNG) for inline rendering.

The transcript calls this when the matching KindToolUseResult event arrives. Idempotent — re-applying the same result is a no-op (Rev not bumped, cache not invalidated).

func (*ToolBlock) ToolID

func (b *ToolBlock) ToolID() string

type Transcript

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

Transcript is the scrollback model — the slice of Block values the user reads in the viewport, plus the bookkeeping that pairs streamed events to in-flight blocks.

Concurrency: not safe for concurrent mutation. The bubbletea main loop is the only writer.

func New

func New() *Transcript

New constructs a transcript with no blocks. The caller must call SetTheme + SetWidth before View; until then the markdown renderer is nil and TextBlocks fall back to the plain Assistant style.

func (*Transcript) AppendBlock

func (t *Transcript) AppendBlock(b Block)

AppendBlock appends a pre-built block verbatim. Used by the App for synthetic insertions (e.g. an all-tasks-complete snapshot in M6) and by tests. Resets streaming markers so the next chunk starts a fresh entry.

func (*Transcript) AppendSynthetic

func (t *Transcript) AppendSynthetic(text string)

AppendSynthetic injects a pre-formatted styled block. The text is rendered verbatim with the standard line gutter.

func (*Transcript) AppendUserPrompt

func (t *Transcript) AppendUserPrompt(text string)

AppendUserPrompt records a prompt the user just submitted. Resets streaming and clears the tool-block map: tool IDs from the previous turn are gone, and a stale entry could route the next turn's result to the wrong block.

func (*Transcript) Blocks

func (t *Transcript) Blocks() []Block

Blocks returns a snapshot of the current block slice. Read-only access for M8 yank mode + M9 search. The returned slice shares backing storage with the transcript; callers must not mutate.

func (*Transcript) Expanded

func (t *Transcript) Expanded() bool

Expanded reports the current Ctrl+O override state. The App's status bar reads this to show "expanded" / "folded" hint.

func (*Transcript) FocusedBlock

func (t *Transcript) FocusedBlock() uint64

FocusedBlock returns the currently focused Block ID, or 0 when no yank focus is active. Test-only / yank-mode internal use.

func (*Transcript) HasInflightCompacting

func (t *Transcript) HasInflightCompacting() bool

HasInflightCompacting reports whether a CompactingBlock is currently mounted. App uses this to decide whether to schedule a spinner tick — no compaction means no animation needed.

func (*Transcript) HasThinkingSprite

func (t *Transcript) HasThinkingSprite() bool

HasThinkingSprite reports whether the thinking sprite is currently mounted. App uses this to advance the frame on spinner ticks.

func (*Transcript) HideThinkingSprite

func (t *Transcript) HideThinkingSprite()

HideThinkingSprite removes the animated thinking sprite from the blocks slice. No-op when not shown.

func (*Transcript) IngestEvent

func (t *Transcript) IngestEvent(e event.Event) bool

IngestEvent translates an agent event into a transcript mutation (or updates an in-flight block). Returns true if anything changed and the App should refresh the viewport.

Mirrors v1's foldEvent semantics but operates on typed blocks. Events that don't concern the transcript (RunStart/End, Usage, StoreUpdate) return false silently.

func (*Transcript) LineOffsetOf

func (t *Transcript) LineOffsetOf(id uint64) int

LineOffsetOf returns the rendered line index where the block with the given ID begins, or -1 when the block isn't in the scrollback. Used by the View's RevealBlock to scroll the viewport so the target is visible.

Walks the cached output by counting newlines in each block's rendered string plus the inter-block spacer. Cheap — strings are already in the cache, no re-render happens.

func (*Transcript) LoadFromMessages

func (t *Transcript) LoadFromMessages(msgs []llm.Message)

LoadFromMessages rehydrates the transcript from a persisted session. Reset() is called first so this replaces the current view rather than appending. Each llm.Message maps to one or more blocks:

  • RoleUser{Content} → UserPromptBlock
  • RoleAssistant{Thinking} → ThinkingBlock (when non-empty)
  • RoleAssistant{Content} → TextBlock (when non-empty)
  • RoleAssistant{ToolCalls[]} → one ToolBlock per call
  • RoleTool{ToolResults[]} → SetResult on the matching ToolBlock (paired by Call.ID)

System messages are skipped (they don't appear in live transcripts either). Block IDs are freshly allocated — the persisted file does not carry UI-side identifiers.

Called by the /resume flow after Controller.ResumeSession swaps the agent's live session with the loaded snapshot.

func (*Transcript) Markdown

func (t *Transcript) Markdown() *Markdown

func (*Transcript) MatchedBlocks

func (t *Transcript) MatchedBlocks() []uint64

MatchedBlocks returns the IDs of every block with at least one search match, in transcript order. Search overlay uses this as the navigation cursor target list.

func (*Transcript) Reset

func (t *Transcript) Reset()

Reset wipes all blocks except the banner. Used by /clear and by /model after a provider swap — both want a fresh conversation view but keep the welcome block at the top.

func (*Transcript) SetBanner

func (t *Transcript) SetBanner(spec BannerSpec)

SetBanner installs (or replaces) the welcome block at index 0. First call appends; subsequent calls mutate in place.

func (*Transcript) SetFocusedBlock

func (t *Transcript) SetFocusedBlock(id uint64)

SetFocusedBlock marks one block as the yank-mode cursor target. id==0 clears the focus (no block highlighted).

The cache invalidates only the previously-focused and the newly- focused entries — every other block stays cached. Cost is one render per focus shift, not one full re-render.

func (*Transcript) SetSearchMatches

func (t *Transcript) SetSearchMatches(m map[uint64][]Range)

SetSearchMatches installs the current search-result map. View() passes RenderOpts.Highlights for any block in the map; blocks without an entry render normally.

Passing nil clears the search highlight. The cache invalidates for each block whose Highlights signature changed (via optsRev), which means: every previously-matched block re-renders, every newly-matched block re-renders, and untouched blocks stay cached.

func (*Transcript) SetSpinnerFrame

func (t *Transcript) SetSpinnerFrame(frame int)

SetSpinnerFrame updates the live compaction row's animation frame, if one exists. No-op when no compaction is in flight.

func (*Transcript) SetTheme

func (t *Transcript) SetTheme(th *theme.Theme)

SetTheme installs the active theme. Subsequent renders will use it; cache invalidation is automatic via the theme's Rev field.

func (*Transcript) SetThinkingSpriteFrame

func (t *Transcript) SetThinkingSpriteFrame(frame int)

SetThinkingSpriteFrame advances the thinking sprite's animation frame. No-op when the sprite isn't mounted.

func (*Transcript) SetWidth

func (t *Transcript) SetWidth(width int)

SetWidth installs (or re-installs) the column count. When it changes, the markdown renderer is rebuilt; the cache invalidates automatically since the cache key includes width + mdRev.

width < 1 is treated as "unknown yet" and ignored — defends against early WindowSizeMsg with zero dims on some terminals.

func (*Transcript) ShowThinkingSprite

func (t *Transcript) ShowThinkingSprite()

ShowThinkingSprite appends the animated thinking sprite to the end of the blocks slice. No-op when already shown.

func (*Transcript) Theme

func (t *Transcript) Theme() *theme.Theme

func (*Transcript) ToggleExpand

func (t *Transcript) ToggleExpand()

ToggleExpand flips the transcript-wide Ctrl+O override. Walks all tool blocks and bumps their Rev so the cache invalidates them.

func (*Transcript) View

func (t *Transcript) View() string

View renders the whole scrollback as one newline-joined string, honouring the cache. Called from the viewport wrapper.

Empty width returns "" — callers shouldn't render until SetWidth has been called.

func (*Transcript) Width

func (t *Transcript) Width() int

Width / Theme / Markdown accessors — used by the App when constructing RenderContext for outside-Transcript renders (e.g. the M10 permission overlay's diff preview).

type TurnEndBlock

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

TurnEndBlock draws a barely-there horizontal rule between agent loop iterations so the user can scan the transcript and spot turn boundaries without the separator competing with real content.

func (*TurnEndBlock) ID

func (b *TurnEndBlock) ID() uint64

func (*TurnEndBlock) Kind

func (b *TurnEndBlock) Kind() Kind

func (*TurnEndBlock) PlainText

func (b *TurnEndBlock) PlainText() string

func (*TurnEndBlock) Render

func (b *TurnEndBlock) Render(ctx RenderContext) string

func (*TurnEndBlock) Rev

func (b *TurnEndBlock) Rev() uint64

type UserPromptBlock

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

func (*UserPromptBlock) ID

func (b *UserPromptBlock) ID() uint64

func (*UserPromptBlock) Kind

func (b *UserPromptBlock) Kind() Kind

func (*UserPromptBlock) PlainText

func (b *UserPromptBlock) PlainText() string

func (*UserPromptBlock) Render

func (b *UserPromptBlock) Render(ctx RenderContext) string

func (*UserPromptBlock) Rev

func (b *UserPromptBlock) Rev() uint64

type View

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

View wraps bubbles/viewport with follow-mode bookkeeping: the scrollback auto-snaps to the bottom on content change unless the user has scrolled up.

M3 ships the minimum viable wrapper: SetSize, MarkDirty, View/Update passthroughs. M5 binds PgUp/PgDn/Home/End key handling (it's currently handled by viewport.Model's default bindings). M8 routes wheel events here.

func NewView

func NewView(tr *Transcript) *View

NewView constructs a viewport wrapper for the given transcript. Follow mode is on by default — typical user expectation is that new content scrolls into view.

func (*View) Following

func (v *View) Following() bool

Following reports follow-mode state. Test-only.

func (*View) GotoBottom

func (v *View) GotoBottom()

GotoBottom re-enables follow mode and jumps to the latest content. Useful for a future End key binding (M5).

func (*View) MarkDirty

func (v *View) MarkDirty()

MarkDirty re-renders the transcript into the viewport. Called by the App after every mutation that could change the visible content (event ingest, prompt append, banner update).

Cheap when nothing changed: the block cache returns memoised strings, so the work is roughly proportional to the number of blocks.

func (*View) RevealBlock

func (v *View) RevealBlock(id uint64) bool

RevealBlock scrolls the viewport so the block with the given ID is visible (its starting line lands near the top of the visible window). Disables follow mode so streaming content doesn't yank the user back to the bottom mid-search.

Returns true when the block was found and scrolled; false when the block isn't in the scrollback.

func (*View) SetSize

func (v *View) SetSize(width, height int)

SetSize updates the viewport's display dims and the transcript's rendering width. Snapshots follow-mode-at-bottom across the resize so a layout change doesn't break "I'm reading the latest".

func (*View) Update

func (v *View) Update(msg tea.Msg) tea.Cmd

Update routes messages to the underlying viewport. M3 only handles what bubbles/viewport handles by default (PgUp/PgDn/ Home/End). M8 will route wheel events here too.

func (*View) View

func (v *View) View() string

View returns the viewport's current visible window.

Jump to

Keyboard shortcuts

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