web

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package web holds akari's server-rendered UI: templ templates and the small view-model helpers they use. Handlers in the httpapi package resolve auth, query the store, and render these templates, so all rendering lives here.

Index

Constants

View Source
const DefaultRange = "year"

DefaultRange is the window the overview opens on: a trailing year, wide enough to read seasonality and longer trends on the activity grid without jumping to all of history.

View Source
const DefaultSessionLimit = 100

DefaultSessionLimit is the global feed's first-page size, matching the store's default cap. "Show more" doubles the limit from here (100 -> 200 -> 400 -> 500).

View Source
const MaxSessionLimit = 500

MaxSessionLimit is the largest page the feed will request; past it the footer drops the "Show more" button and asks the reader to narrow by filter or search.

View Source
const OtherModelLabel = "Other"

OtherModelLabel is the bucket name for the model breakdown's long tail: every model without a pricing entry folds into this one row.

View Source
const SessionsBasePath = "/sessions"

SessionsBasePath is the global (cross-project) session list.

View Source
const UnscoredKey = "unscored"

UnscoredKey is the sentinel a drill-through link and the Grade filter carry for the unscored grade bucket, since the empty string reads as "no grade filter". The Grades panel's unscored bar links with this value.

Variables

View Source
var DateRanges = []DateRange{
	{Key: "7d", Label: "7 days", Days: 7},
	{Key: "30d", Label: "30 days", Days: 30},
	{Key: "90d", Label: "90 days", Days: 90},
	{Key: "year", Label: "Year", Days: 365},
	{Key: "all", Label: "All", Days: 0},
}

DateRanges are the windows the overview offers, narrowest first. The selector renders them in this order, and ParseRange validates against their keys.

View Source
var Static embed.FS

Static holds the embedded static assets (htmx, stylesheet) served under /static/.

Functions

func AgentFacetHref

func AgentFacetHref(f store.SessionFilter, value string) templ.SafeURL

AgentFacetHref and friends return the toggle href for a facet option, holding the rest of the current selection.

func AnalyticsJSON

func AnalyticsJSON(a store.Analytics) string

AnalyticsJSON marshals a session's daily series for the inline chart script.

func AnyFilterActive

func AnyFilterActive(f store.SessionFilter) bool

AnyFilterActive reports whether the global session list is currently narrowed, so the view can show a "clear all" affordance only when it would do something. The content search counts: it is a removable narrowing like the facets, and its chip clears alongside them.

func AttachmentAlt

func AttachmentAlt(a store.AttachmentView) string

AttachmentAlt is the alt text for a rendered image: its filename when one was recovered, else a generic label so the element is never unlabelled.

func AttachmentsByOrdinal

func AttachmentsByOrdinal(atts []store.AttachmentView) map[int][]store.AttachmentView

AttachmentsByOrdinal groups attachments by the message ordinal they belong to, so the session view can render a message's images beneath it.

func BaseName

func BaseName(p string) string

BaseName returns the last path segment of a file path (handling both / and \ separators), for a compact label in the outline. It returns the input unchanged when there is no separator.

func BlobURL

func BlobURL(base, sha string) string

BlobURL builds a tool body's fetch URL from a session blob base and a hash.

func BodyPath

func BodyPath(id int64) string

func ChipLabel

func ChipLabel(mediaType string, bytes int64) string

ChipLabel renders a tool body's media type and size, for example "36.0 KB json".

func ChurnSessions

func ChurnSessions(sessions int) string

ChurnSessions labels how many sessions edited a file, so a path churned across the fleet reads apart from one a single session kept rewriting.

func ConcurrencyBusiest

func ConcurrencyBusiest(c store.ConcurrencyStats) string

ConcurrencyBusiest formats the busiest user's peak for the concurrency panel: the name and their peak simultaneous sessions, or a dash when no user had a measurable span.

func ConcurrencyBusiestHref added in v0.2.2

func ConcurrencyBusiestHref(c store.ConcurrencyStats, rng string) templ.SafeURL

ConcurrencyBusiestHref drills the busiest-user figure into that user's sessions, carrying the current Insights window (rng) so the list matches the panel's period. It is called only when a busiest user exists (the template guards on BusiestUser != ""). IncludeEmpty rides along because the concurrency panel counts sessions regardless of message_count, and RequireSpan narrows to the measured-span cohort the panel actually sweeps (spanFilter): without it the drill would list sessions with no parsed span that the panel never counted, so the feed and the figure would disagree.

func ContextResetsLabel

func ContextResetsLabel(s store.SessionSignals) string

ContextResetsLabel renders how many inferred context resets (compactions or clears) the session went through, for the Tokens tooltip. An unmeasured session reads a dash; a measured session with none reads "0", a real "it never shed context".

func DetailLabel added in v0.2.2

func DetailLabel(s string) string

DetailLabel renders a tool call's Detail (a command, pattern, URL, or other bounded input summary, up to 2048 bytes and possibly multi-line) as a single scannable line for a chip or outline step: every run of whitespace collapses to one space, and the result is capped at 80 runes with a trailing ellipsis. The cap keeps a chip from growing to the size of its input; the full text still reaches the reader through the element's title attribute, so nothing is lost, only deferred to hover. The truncation counts runes, not bytes, so it never splits a multi-byte UTF-8 sequence.

func DiffTool

func DiffTool(name string) bool

DiffTool reports whether a tool's input body is worth rendering as an inline diff rather than raw JSON (the file-editing tools across the three agents). The client reads this off the chip to decide how to expand the body.

func DuplicateIDsLabel

func DuplicateIDsLabel(n int) string

DuplicateIDsLabel is the chip text for a session that repeats tool-call ids, pluralized for the count. The count itself is a bounded scalar from the store (DuplicateCallUIDCount), not computed over the in-memory tool calls.

func EmptyToggleHref added in v0.2.2

func EmptyToggleHref(f store.SessionFilter) templ.SafeURL

EmptyToggleHref flips the include-empty state for the footer's toggle, holding every other facet, search, and sort. It resets the page to the default size for the same reason "Show more" carries the limit: the visible count changes, so the paging restarts rather than keeping a limit sized for the other visibility.

func FeedIsGrouped

func FeedIsGrouped(f store.SessionFilter) bool

FeedIsGrouped reports whether the feed for this filter is split into day headings, which it is only in the default most-recent order.

func FeedTime

func FeedTime(ctx context.Context, t *time.Time) string

FeedTime is the clock time a feed row shows, in the viewer's timezone. The day already rides the group heading, so the row needs only the time of day; the exact stamp is the cell's title on hover.

func FmtAvgConcurrent

func FmtAvgConcurrent(v float64) string

FmtAvgConcurrent renders the average concurrency to one decimal, the granularity that reads as a rate ("1.4 concurrent") without implying false precision.

func FmtBytes

func FmtBytes(n int64) string

FmtBytes renders a byte count compactly (the tool-body metadata chips).

func FmtContextStamp added in v0.2.2

func FmtContextStamp(u store.TurnUsage) string

FmtContextStamp renders a turn's context occupancy as the compact "ctx 82k" stamp the transcript shows inline; the full per-class split and cost ride the breakdown card the stamp hosts (see turnCard in session.templ), not a hover title.

func FmtCost

func FmtCost(usd float64, incomplete bool) string

FmtCost renders a USD cost. Sub-cent costs still show enough precision to be meaningful.

func FmtDuration

func FmtDuration(start, end *time.Time) string

FmtDuration renders the span between start and end, or a dash.

func FmtErrorRate

func FmtErrorRate(v float64) string

FmtErrorRate renders a failure share (a 0..1 fraction) as a whole-percent figure, with a "<1%" floor so a small but real error rate never rounds down to a reassuring "0%".

func FmtLatency

func FmtLatency(d time.Duration) string

FmtLatency renders a turn-cycle latency at the coarsest unit that still reads honestly: seconds under a minute, minutes and seconds under an hour, hours and minutes beyond. A zero or negative duration (no measured turn) shows a dash rather than "0s", so an absent figure never reads as an instantaneous reply. The value rounds to whole seconds before the unit split, so 59.6s reads as "1m 0s" rather than a misleading "60s".

func FmtPercent

func FmtPercent(f float64) string

FmtPercent renders a 0..1 fraction as a whole-number percent, for the cache hit rate. A real but tiny rate (under 1%) rounds up to 1% rather than 0%, so a scope that did hit the cache never reads as a total miss; a true zero stays 0%.

func FmtRate

func FmtRate(v float64) string

FmtRate renders a per-minute throughput to one decimal, the same granularity as the concurrency average so the velocity and concurrency figures read in one register.

func FmtRelTime

func FmtRelTime(ctx context.Context, t *time.Time) string

FmtRelTime renders a timestamp as a coarse "time ago" for the recent past (today, 1 day ago, ...), falling back to an absolute stamp once it is a week or more old, where a relative phrasing stops being useful. It reads "now" from the wall clock; relTime holds the testable core. It backs the "Updated" column on both the projects index and the per-project session table, so the two read alike (the global feed groups by day instead and uses FeedTime).

func FmtSavings

func FmtSavings(usd float64, incomplete bool) string

FmtSavings renders a cache saving for the Cache tile. A non-negative saving reads as "saved $X"; the rare negative, where cache was written but never re-read enough to repay the creation premium, reads as "cost $X" on its magnitude, so the figure stays honest without printing a minus sign into a "saved" label.

An incomplete saving reads "... partial", NOT the "$X+" lower-bound marker the cost figures use. A saving omitted for an unpriced model can be negative (a Claude cache write is priced above input), so the true figure could be lower OR higher than shown: "partial" says it is incomplete without implying a direction the data cannot support.

func FmtTime

func FmtTime(ctx context.Context, t *time.Time) string

FmtTime renders a timestamp in the viewer's timezone (UTC when none is set), or a dash when absent. It keeps the bare "2006-01-02 15:04" form for a visible cell; FmtTimeLong adds the zone abbreviation for a hover title, where naming the zone earns its width.

func FmtTimeAt

func FmtTimeAt(ctx context.Context, t time.Time) string

FmtTimeAt renders a non-pointer timestamp in the viewer's timezone, or a dash when zero.

func FmtTimeLong added in v0.2.2

func FmtTimeLong(ctx context.Context, t *time.Time) string

FmtTimeLong is FmtTime with the zone abbreviation appended, for the hover title on a stamp shown short elsewhere. Naming the zone (PST, UTC, ...) lets a reader tell which zone a full stamp is in without cluttering every visible cell with it.

func FmtTokens

func FmtTokens(n int64) string

FmtTokens renders a token count with thousands separators.

func FmtTokensCompact

func FmtTokensCompact(n int64) string

FmtTokensCompact renders a token count to a short magnitude (2.1B, 1.7M, 63.0k, 412), for the feed's inline figure where the exact value lives in the hover card. The thousands-separated FmtTokens stays the form for places that show the full number. The buckets mirror fmtTok in static/charts.js, its client-side counterpart, so a figure reads the same on either surface.

func FmtTurnLatency added in v0.2.2

func FmtTurnLatency(d time.Duration) string

FmtTurnLatency renders a turn's reply latency as the inline "+6s" stamp: the leading plus reads it as an elapsed gap from the prompt rather than an absolute time. It reuses FmtLatency for the unit split.

func FoldUnknownModels

func FoldUnknownModels(bs []store.Breakdown) []store.Breakdown

FoldUnknownModels collapses every model without a pricing entry into a single "Other" row, leaving the priced (well-known) models untouched and in their incoming order. Only models we have rates for surface by name on the overview; the rest contribute their totals to "Other" but never their IDs, so a model still under a codename can be exercised without leaking its name.

The input is taken in its store order (cost descending); "Other" is appended last regardless of its totals, the usual place for a catch-all bucket. Summing the per-model session counts can overcount a session that spanned several unpriced models, the same approximation the by-model split already makes across its rows.

func GradeChipLabel added in v0.2.2

func GradeChipLabel(grade string) string

GradeChipLabel and OutcomeChipLabel render the active-filter chip value for a grade or outcome, terse to match the agent/user chips ("grade A", "outcome abandoned").

func GradeClearHref added in v0.2.2

func GradeClearHref(f store.SessionFilter) templ.SafeURL

GradeClearHref, OutcomeClearHref, and RangeClearHref are the removal links for the grade, outcome, and range chips: each drops just its own param while holding every other facet, search, sort, and window, matching the agent/user chip behavior.

func GradeFilterKey added in v0.2.2

func GradeFilterKey(distKey string) string

GradeFilterKey maps a Grades-distribution key to the ?grade= value that drills into the matching sessions: the empty (unscored) bucket becomes the sentinel, a letter stays itself. It is the inverse of gradeLabel for URL building.

func GradedNote added in v0.2.2

func GradedNote(q store.QualityDistribution) string

GradedNote is the coverage caption for a quality distribution panel: the share of in-scope sessions that carry a usable grade, so a reader can weigh a mostly ungraded window's bars for what they are. It reads "" when the window is empty (the panel shows its own empty state), and rounds to whole percent, with a "<1% graded" floor so a nonzero-but-tiny coverage does not round away to "0% graded".

func HygieneCount

func HygieneCount(n, d int) string

HygieneCount renders the raw count behind a hygiene rate ("12 of 340"), the sub-line under each figure, so a reader sees the magnitude and not only the proportion (3% of 1000 prompts and 3% of 30 read very differently).

func HygienePct

func HygienePct(n, d int) string

HygienePct renders a prompt-hygiene rate (a count over the prompt or session total) as a whole-percent figure. It keeps the "<1%" floor the tool error rate uses, so a rare but present signal does not round away to a clean 0%. A zero count reads as a real 0% (no such prompts), not a dash; the dash is only the guard for an empty denominator, which the panel already avoids by gating on PromptHygiene.HasData.

func IsGrade added in v0.2.2

func IsGrade(v string) bool

IsGrade reports whether v is a grade the session list can filter by: a letter A..F or the unscored sentinel. The handler uses it to reject a tampered ?grade= value.

func IsImageMedia

func IsImageMedia(mediaType string) bool

IsImageMedia reports whether a media type is an image the transcript can show inline, so a non-image attachment falls back to a download link instead.

func IsLocalKind

func IsLocalKind(kind string) bool

IsLocalKind reports whether a project kind is one of the non-remote kinds (standalone or orphaned), which are grouped and labeled apart from git-remote projects in the UI.

func IsOutcome added in v0.2.2

func IsOutcome(v string) bool

IsOutcome reports whether v is a filterable outcome, so the handler can reject a tampered ?outcome= value.

func Loc added in v0.2.2

func Loc(ctx context.Context) *time.Location

Loc is the viewer's timezone for the current render, or UTC when none was set. The formatting helpers localize every stamp and day heading through it, so a reader sees times in their own zone; a page rendered outside a request (or before the cookie is set) reads UTC. templ exposes the render ctx implicitly, so a template calls FmtTime(ctx, t) and this reads the zone off that ctx.

func LocalPath

func LocalPath(p store.ProjectSummary) string

LocalPath recovers the working-directory path from a local project's synthetic key ("local:machine:path"), for display beside the folder name. It returns "" for a remote project.

func MachineFacetHref

func MachineFacetHref(f store.SessionFilter, value string) templ.SafeURL

func NextSessionLimit added in v0.2.2

func NextSessionLimit(cur int) int

NextSessionLimit doubles the current page size for the "Show more" control, clamped to MaxSessionLimit, so the feed grows 100 -> 200 -> 400 -> 500 rather than jumping straight to the cap.

func Notice added in v0.2.2

func Notice(ctx context.Context) string

Notice is the current render's one-shot notice banner text, or "" when none was set. The authed layout renders it once at the top of main when non-empty.

func OutcomeChipLabel added in v0.2.2

func OutcomeChipLabel(outcome string) string

func OutcomeClearHref added in v0.2.2

func OutcomeClearHref(f store.SessionFilter) templ.SafeURL

func OutcomeLabel

func OutcomeLabel(outcome string) string

OutcomeLabel renders a stored outcome string title-cased for display. An empty or unrecognized value reads "Unknown", so the tile never shows a blank cell.

func OutcomeMixTitle added in v0.2.2

func OutcomeMixTitle(u store.UserQuality) string

OutcomeMixTitle is the hover title for a People-row outcome mix bar, spelling out the four outcome counts the bar renders proportionally.

func OutlineStepClass

func OutlineStepClass(t store.ToolCallView) string

OutlineStepClass maps a tool step to its CSS modifier, flagging an errored step in rose.

func OutlineStepHasBody

func OutlineStepHasBody(t store.ToolCallView) bool

OutlineStepHasBody reports whether a tool step has a stored input or result body, so the outline can wire only those steps to the inspector.

func OutlineTitle

func OutlineTitle(m store.Message) string

OutlineTitle is a compact one-line label for an outline turn, drawn from the start of the message content. It collapses runs of whitespace and emits at most 48 runes, and it never scans more than scanCap runes of input, so even a whitespace-heavy message costs a fixed amount, independent of message size.

func OutlineTurnClass

func OutlineTurnClass(role string, steps []store.ToolCallView) string

OutlineTurnClass maps a turn (its role and its tool steps) to its CSS modifier: role tone, with an error override so a turn with a failed tool reads in rose.

func OverviewPath

func OverviewPath(rng string, userIDs []int64) string

OverviewPath builds the overview URL for a range key and a set of selected user ids: the shared target the range buttons link to and the user filter submits, so the two controls always round-trip the full window-and-users state together rather than each clobbering the other's selection.

func ParseRange

func ParseRange(key string) string

ParseRange normalizes a range query value to a known key, falling back to the default for anything empty or unrecognized.

func PeakContextLabel

func PeakContextLabel(s store.SessionSignals) string

PeakContextLabel renders the heaviest single-turn context the session reached, in full tokens, for the Tokens tooltip. It is a window-independent measure of how loaded the session got. An unmeasured session (no usage) reads a dash rather than a misleading zero.

func ProjectFacetHref

func ProjectFacetHref(f store.SessionFilter, id int64) templ.SafeURL

ProjectFacetHref toggles the project selection for a facet row, holding the rest of the current selection.

func ProjectFacetLabel

func ProjectFacetLabel(pf store.ProjectFacet) string

ProjectFacetLabel is the label for a project option in the session filter rail, friendly for a local project and the remote key otherwise.

func ProjectHref

func ProjectHref(id int64) templ.SafeURL

ProjectHref and friends return sanitized internal URLs for href attributes.

func ProjectPath

func ProjectPath(id int64) string

ProjectPath returns the plain string path, used for htmx attributes (which are not URL-sanitized like href).

func ProjectTitle

func ProjectTitle(p store.ProjectSummary) string

ProjectTitle is the heading shown for a project. A remote project shows its canonical remote key; a standalone or orphaned project shows its folder name, since its synthetic key ("local:machine:path") is an internal detail.

func PublicBlobBase

func PublicBlobBase(publicID string) string

func PublicHref

func PublicHref(publicID string) templ.SafeURL

func PublicOverviewHref

func PublicOverviewHref(username string) templ.SafeURL

PublicOverviewHref is the sanitized href form of PublicOverviewPath, for the account page's link and the signed-in overview badge.

func PublicOverviewPath

func PublicOverviewPath(username string) string

PublicOverviewPath is the plain-string path of a user's public usage overview, rooted at /u/<username>. The username is path-escaped so an unusual character cannot break the URL or escape the segment. The range selector on the public page builds its buttons from this base (via RangeOptions), so switching the window refetches the public path rather than the authed overview, and the account page shows it as the shareable link.

func PublicPath

func PublicPath(publicID string) string

PublicPath is the plain-string public URL, shown to the owner as the shareable link to copy.

func QualityGrade

func QualityGrade(s store.SessionSignals) string

QualityGrade is the headline of the session Quality tile: the letter grade for a scored session, or a plain dash for an unscored one (an unknown outcome with no tool signal, where a letter would invent a verdict the transcript does not support).

func QualityGradeClass

func QualityGradeClass(s store.SessionSignals) string

QualityGradeClass is the CSS modifier for the Quality tile, banding its colour the way a report card reads: A and B good, C watch, D and F poor, an unscored session neutral. It maps to the status palette already in the sheet (sage, peach, rose) rather than introducing new hues, keeping to the One Voice Rule.

func QualityScoreLabel

func QualityScoreLabel(s store.SessionSignals) string

QualityScoreLabel renders the numeric score for the Quality tooltip: "n / 100" for a scored session, "not scored" otherwise, so an unscored session reads as a deliberate abstention rather than a zero.

func RangeBounds added in v0.2.2

func RangeBounds(key string) bool

RangeBounds reports whether a range key names a bounded trailing window (a known key with a positive day span). It is false for "all", the empty string, and any unknown value, which the sessions feed treats as all-history: those add no ?range param, keeping the bare feed unbounded. It is the whitelist the feed's range param passes through (like IsOutcome / IsGrade for their params), so a stale or hand-edited ?range never bounds the list to a made-up window.

func RangeChipLabel added in v0.2.2

func RangeChipLabel(key string) string

RangeChipLabel is the active-filter chip value for the window, reusing the range selector's own option wording ("30 days", "Year") so the chip reads the same as the button that could have set it. It falls back to the raw key for an unknown value, though the handler validates the key before it reaches here.

func RangeClearHref added in v0.2.2

func RangeClearHref(f store.SessionFilter) templ.SafeURL

func RangeLabel added in v0.2.2

func RangeLabel(key string) string

RangeLabel returns a range key's display label ("30 days", "Year"), for the sessions feed's active-range chip. An unknown key returns the key itself, so a stale value still reads rather than rendering blank.

func RangeSince

func RangeSince(key string, now time.Time) time.Time

RangeSince converts a range key to the lower time bound to pass to the store, measured back from now. The "all" window (zero Days) returns the zero time, which the store reads as "no bound".

func ReparsePercent

func ReparsePercent(done, total int) int

ReparsePercent is the completed fraction of a reparse as an integer percent, for the progress bar's fill width. It is 0 before the total is known and is clamped to 100, so a late count revision can never overflow the track.

func RoleClass

func RoleClass(role string) string

RoleClass maps a message role to a CSS class for styling.

func RowTokens

func RowTokens(s store.SessionSummary) int64

RowTokens is a session's total token volume across all four classes (input, output, cache read, cache write), matching the overview heatmap's notion of a day's "total tokens" so the figure and its breakdown agree across views.

func SSEPath

func SSEPath(id int64) string

SSEPath and BodyPath are the live-update endpoints for a session, carried on data attributes for the static app.js to wire up.

func ScoreBreakdownItems added in v0.2.2

func ScoreBreakdownItems(s store.SessionSignals) []quality.ScoreBreakdownItem

ScoreBreakdownItems returns the penalty lines behind a scored session's grade (label plus the points it subtracted), for the "score arithmetic" group in the quality tooltip. It is empty when the session is unscored (there is no arithmetic) OR scored with no penalties (a clean 100): the caller distinguishes the two through the session's Scored() flag and shows a single "no penalties" row for the clean-scored case.

func SearchClearHref added in v0.2.2

func SearchClearHref(f store.SessionFilter) templ.SafeURL

SearchClearHref is the toggle link for the active search chip: it drops the query while holding every other facet, sort, and the empty toggle, so removing a search leaves the rest of the narrowing in place.

func SegmentStyle added in v0.2.2

func SegmentStyle(seg OutcomeSegment) string

SegmentStyle is the inline style for one outcome mix slice: its proportional width and its color.

func SelectedUserIDs

func SelectedUserIDs(raw []string, users []store.User) []int64

SelectedUserIDs parses the overview's repeated ?user= ids against the known accounts, keeping only ids that name a real user and returning them in the users-list order. A tampered, stale, or non-numeric id silently drops out, and the stable order keeps the collapsed pills from reshuffling between requests.

func SessionBlobBase

func SessionBlobBase(id int64) string

SessionBlobBase and PublicBlobBase are the per-session prefixes under which CAS blobs are served, for the authenticated and logged-out views respectively. A blob URL is the base plus "/blob/{sha256}"; serving is gated on the session referencing the hash.

func SessionHref

func SessionHref(id int64) templ.SafeURL

func SessionPath

func SessionPath(id int64) string

SessionPath is the plain-string session path, used for the row-navigation data attribute that makes a whole table row a click target.

func SessionProjectLabel

func SessionProjectLabel(d store.SessionDetail) string

SessionProjectLabel is the project name shown in a session header: the folder name for a local session, the remote key otherwise. It keeps the synthetic "local:machine:path" key out of the heading.

func SessionRowProject

func SessionRowProject(r store.SessionRow) string

SessionRowProject is the project label shown beside a session in the global session list: the folder name for a local project, the remote key otherwise.

func SessionsHref

func SessionsHref(f store.SessionFilter) templ.SafeURL

SessionsHref is the sanitized href form of SessionsPath, for anchor tags.

func SessionsPath

func SessionsPath(f store.SessionFilter) string

SessionsPath is the full global session-list path for the current selection, used as the htmx swap target so a facet click updates the URL coherently.

func ShedLabel added in v0.2.2

func ShedLabel(m ShedMark) string

ShedLabel renders a shed divider's centered label, "context shed: 145k -> 12k", with a real arrow between the before and after occupancy figures.

func ShowMorePath added in v0.2.2

func ShowMorePath(f store.SessionFilter) string

ShowMorePath is the plain-string path the "Show more" button fetches: the same filter with the page size doubled, used as the htmx GET target so the swap re-renders the whole list (day grouping and footer included) at the larger page.

func Sparkline

func Sparkline(vals []float64) string

Sparkline renders a tiny inline SVG line of daily cost for a project row in the index. It is purely decorative trend context, so it carries aria-hidden and no axis; the cost column beside it holds the real number. Returns an empty string when there is nothing to draw (the cell stays blank).

func StatusClass

func StatusClass(status string) string

StatusClass maps a tool result status to a CSS class.

func ToolFilePath added in v0.2.2

func ToolFilePath(t store.ToolCallView) string

ToolFilePath is the path a tool chip and outline step should DISPLAY: the worktree-relative form when it is known, else the absolute path. The same repo file then reads the same across the worktrees it was edited from.

func ToolFileTitle added in v0.2.2

func ToolFileTitle(t store.ToolCallView) string

ToolFileTitle is the hover title for a tool chip's path: the absolute path when it differs from the displayed (relative) form, so the full location is one hover away without cluttering the chip. It is empty when the two are the same (a path with no relative form), so no redundant title is attached.

func ToolsByOrdinal

func ToolsByOrdinal(tools []store.ToolCallView) map[int][]store.ToolCallView

ToolsByOrdinal groups tool calls by the message ordinal they belong to, so the session view can render a message's tool calls beneath it.

func TurnCostLabel added in v0.2.2

func TurnCostLabel(u store.TurnUsage) string

TurnCostLabel renders a turn's cost for its breakdown card: the dollar figure when the turn was priced, or "unpriced" when it carries no cost (a turn whose usage never got a rate), so the card never shows a misleading "$0.00" for an unmeasured cost. A priced-but-incomplete turn (some of its token-bearing rows were unpriced) renders the same "$X+" lower-bound marker every other token/cost figure uses, so the card's cost never reads as exact next to unpriced token rows.

func TurnTokenTotal added in v0.2.2

func TurnTokenTotal(u store.TurnUsage) int64

TurnTokenTotal is the four-class spend total for one turn (input + output + cache read + cache write), the headline the turn's breakdown card carries above its per-class split. It is a real all-four-classes total, distinct from the context-occupancy figure the visible "ctx" stamp shows (which excludes output), so the card reconciles with the four rows beneath it.

func UserAvgScore added in v0.2.2

func UserAvgScore(u store.UserQuality) string

UserAvgScore formats a People-row average quality score, or a dash when the user has no graded session to average.

func UserFacetHref

func UserFacetHref(f store.SessionFilter, value string) templ.SafeURL

func UserGradedLabel added in v0.2.2

func UserGradedLabel(u store.UserQuality) string

UserGradedLabel is the People-row coverage cell: how many of the user's sessions carry a usable grade out of their total.

func UserQualityHref added in v0.2.2

func UserQualityHref(u store.UserQuality, rng string) templ.SafeURL

UserQualityHref links a People row to the session feed scoped to that user, carrying the active window so the drill-through opens the same cohort the row summarized. IncludeEmpty rides along for the same reason the grade and outcome drills carry it: the People panel counts a user's sessions regardless of message_count, so the drilled feed must show empties too or its list would fall short of the row's count.

func VelocityMsgsRate

func VelocityMsgsRate(v store.VelocityStats) string

VelocityMsgsRate and VelocityToolsRate format the throughput figures, dashing them when the scope had no active time to divide by (see VelocityStats.HasThroughput): a 0.0 over an undefined denominator would read as a real "no work happened" rate rather than "no rate to show".

func VelocityToolsRate

func VelocityToolsRate(v store.VelocityStats) string

func VizColor

func VizColor(i int) string

VizColor returns the categorical color for index i, wrapping past the eighth.

func WithLoc added in v0.2.2

func WithLoc(ctx context.Context, loc *time.Location) context.Context

WithLoc returns a context carrying the viewer's timezone, for the httpapi render path to attach before a component renders. A nil location is ignored so the accessor's UTC default still applies.

func WithNotice added in v0.2.2

func WithNotice(ctx context.Context, notice string) context.Context

WithNotice returns a context carrying a one-shot notice banner's text, for the httpapi render path to attach before a component renders. An empty string is ignored, so the accessor's no-notice default still applies.

Types

type BreakdownRow

type BreakdownRow struct {
	Label      string
	Cost       string
	CostUSD    float64
	Tokens     int64
	Input      int64
	Output     int64
	CacheRead  int64
	CacheWrite int64
	Reasoning  int64
	Sessions   int
	Pct        float64
	Color      string
	// Incomplete marks a slice whose cost folded in an unpriced usage event, so the
	// row shows the same "$X+" lower-bound marker the per-session figures use.
	Incomplete bool
}

BreakdownRow is one bar in a by-model or by-agent breakdown: the label, its formatted cost, the raw token volume (total plus the four-class split that feeds the hover card), a fill width as a percentage of the largest token volume in the set, and its categorical color. The token fields stay raw int64s so the template runs them through the same FmtTokens the rest of the app uses.

func BuildBreakdown

func BuildBreakdown(bs []store.Breakdown) []BreakdownRow

BuildBreakdown turns store breakdowns into renderable bar rows. Bar width is proportional to token volume so model and agent shares compare on the one figure every slice carries: a model still under a codename (unpriced, so its cost folds to zero) still draws a bar that reflects how much it was used.

type ChurnBar

type ChurnBar struct {
	// Project is the file's project display label, shown as a tag beside the path so the
	// churn list groups the same relative path per project across worktrees.
	Project  string
	Path     string
	Edits    int
	Sessions int
	Pct      float64
}

ChurnBar is one file's bar in the churn list: sized by edit count, labelled with its path, and annotated with how many sessions returned to it.

func ChurnBars

func ChurnBars(c store.FileChurn) []ChurnBar

ChurnBars turns the most-edited files into renderable bars, each width its share of the most-churned file so the worst hotspot reads full and the rest relative.

type DateRange

type DateRange struct {
	Key   string
	Label string
	Days  int
}

DateRange is one option in the trailing-window selector. Days is the width of the window; a zero Days means all of history (no lower bound).

type DistRow

type DistRow struct {
	Label string
	Count int
	Pct   float64
	Color string
	// Href, when set, drills the row into the matching session list (a /sessions link
	// carrying the grade or outcome filter plus the current window). It is empty for a
	// dimension with no session-list filter (archetypes) and for a zero-count row, which
	// stays plain text rather than linking to an empty list.
	Href string
}

DistRow is one bar in an Insights distribution: a display label, the session count, the fill width as a percent of the largest bar in the set, and the bar colour. It reuses the breakdown bars' markup and animation, so a distribution reads with the same instrument styling as the cost breakdowns.

func ArchetypeBars

func ArchetypeBars(counts []store.LabeledCount) []DistRow

ArchetypeBars renders the archetype mix, lightest to heaviest, each in its own categorical hue. Archetypes have no session-list filter, so their rows do not link.

func GradeBars

func GradeBars(counts []store.LabeledCount, base store.SessionFilter, rng string) []DistRow

GradeBars renders the grade distribution: A through F then the unscored bucket, each banded in the report-card tone the session Quality tile uses. Each non-empty bar links into the matching sessions, carrying the current window (rng) so the session list is scoped to the same period the panel counted. base is the drill-down's starting filter: the fleet Insights page passes an empty filter (a fleet-wide drill), while the project page passes a filter already scoped to the project so the drill stays inside it. IncludeEmpty rides along because the panel counts sessions regardless of message_count (a zero-message session can still carry a grade), so the drilled feed must show empties too or its count would fall short of the bar it drilled from.

func OutcomeBars

func OutcomeBars(counts []store.LabeledCount, base store.SessionFilter, rng string) []DistRow

OutcomeBars renders the outcome distribution, reusing OutcomeLabel for the title-cased names and a semantic tone per outcome. Each non-empty bar links into the matching sessions, carrying base (the project scope on the project page, empty on the fleet Insights page), the current window (rng), and IncludeEmpty for the same reason GradeBars does: the panel scope counts zero-message sessions, so the drilled feed must include them to match.

type Facets

type Facets struct {
	Agents   []string
	Machines []string
	Users    []string
}

Facets are the distinct filter values available for a project's session list.

type FeedRow

type FeedRow struct {
	Row store.SessionRow
	// FadeProject marks a row whose project repeats the row directly above it in
	// the same day group, so the template can mute the repeated label and let a run
	// of same-project sessions read as one burst rather than a stuttering column.
	FadeProject bool
	// TokenPct is the row's total token volume as a percent of the feed's largest
	// session, backing a magnitude bar so the big runs stand out without the reader
	// parsing seven-digit figures. It is 0 for a zero-token session.
	TokenPct int
}

FeedRow is one session prepared for the feed: its source row plus the derived bits the template would otherwise have to compute inline.

type FilterOption added in v0.2.2

type FilterOption struct {
	Key   string
	Label string
}

FilterOption is one choice in a toolbar filter select: the canonical key that rides the query string and its display label. Outcome and grade both use it, so the two selects render from one shape.

func GradeFilterOptions added in v0.2.2

func GradeFilterOptions() []FilterOption

GradeFilterOptions are the grade filter's choices: the five letters best to worst, then the "unscored" sentinel that matches a session with no gated grade (the Insights Grades panel's empty bucket). The letter labels are the letters themselves; the sentinel reuses gradeLabel so it reads "Unscored", the same word the grade bar carries.

func OutcomeFilterOptions added in v0.2.2

func OutcomeFilterOptions() []FilterOption

OutcomeFilterOptions are the outcome filter's choices, in the distribution's canonical order (best to worst, unknown last), so the toolbar select reads the same order the Insights outcome bars do. The labels reuse OutcomeLabel, so the select and the session detail's Outcome field name a bucket identically.

type GuideLink struct {
	Label string
	Route string
}

GuideLink is a prev/next navigation target.

type GuideNavItem

type GuideNavItem struct {
	// Num is the zero-padded reading position ("00".."08"), shown in mono beside
	// the label.
	Num    string
	Label  string
	Route  string
	Active bool
}

GuideNavItem is one chapter in the docs sidebar rail.

type GuideTocItem

type GuideTocItem struct {
	ID    string
	Text  string
	Level int
}

GuideTocItem is one heading in the on-this-page table of contents. Level is 2 or 3; an H3 is indented under the H2 it follows.

type GuideView

type GuideView struct {
	Title   string
	Summary string
	// BodyHTML is the chapter rendered to HTML by the guide package's goldmark
	// pipeline over our own trusted Markdown, injected verbatim into the prose
	// column.
	BodyHTML string
	// RawMarkdown is the exact Markdown source, embedded once so "Copy page" can
	// place it on the clipboard without a network round-trip.
	RawMarkdown string
	// RawPath is the chapter's raw-Markdown URL (/guide/<slug>.md), the "View as
	// Markdown" target and the rel=alternate the head advertises.
	RawPath   string
	GithubURL string
	Nav       []GuideNavItem
	Toc       []GuideTocItem
	Prev      *GuideLink
	Next      *GuideLink
	// LoggedIn switches the header's corner action between "Open app" and
	// "Log in", since the guide is reachable in either state.
	LoggedIn bool
}

GuideView is everything the docs page needs to render one chapter.

type HeaderStats

type HeaderStats struct {
	Cache   store.CacheStats
	Signals store.SessionSignals
}

HeaderStats bundles the derived stat-tile inputs the session instrument header renders beside the token figures: prompt-cache effectiveness and the session's quality signals. Threading one struct keeps the SessionMain / sessionStats / public render seams stable as the header grows, rather than widening every signature each time a new tile lands.

type MsgMetrics added in v0.2.2

type MsgMetrics struct {
	Latency time.Duration
	Shed    *ShedMark
}

MsgMetrics is the render-only overlay one transcript message carries: its reply latency (a zero duration means the message opens no answered turn) and a shed marker (nil unless this turn shed context relative to the previous measured one). The duplicate-prompt verdict is NOT here: it rides on store.Message.DuplicatePrompt, folded in the message read, so the walker holds no state for it.

type OGMeta

type OGMeta struct {
	Title       string
	Description string
	// Image is the absolute URL of the preview card. Open Graph requires an absolute
	// URL here, so the handler builds it from the request origin; empty omits it.
	Image string
	// URL is the absolute canonical URL of the page; empty omits og:url.
	URL string
}

OGMeta carries the Open Graph and Twitter card metadata a public page emits so a shared link unfurls with a title, description, and preview image. The zero value emits only the basic title tags; Image (an absolute URL) switches the Twitter card to the large-image form and adds og:image. Description and URL are optional.

type OutcomeSegment added in v0.2.2

type OutcomeSegment struct {
	Color string
	Pct   float64
	Count int
	Label string
}

OutcomeSegment is one slice of a People-row outcome mix bar: its width (Pct of the user's sessions), its color, and the count and label the title reads back.

func OutcomeSegments added in v0.2.2

func OutcomeSegments(u store.UserQuality) []OutcomeSegment

OutcomeSegments splits a user's sessions into the proportional slices of their outcome mix bar, dropping empty slices so a zero-count outcome adds no invisible segment. It returns nil when the user has no sessions (the row shows a placeholder instead).

type Page

type Page struct {
	Title    string
	Username string
	IsAdmin  bool
	LoggedIn bool
	// Active is the sidebar nav key for the current page ("overview",
	// "sessions", "projects", "account"), so the shell can mark the
	// current section. Empty leaves no item active.
	Active string
	// OverviewPublic reports whether the signed-in user has published their own
	// usage overview, which drives the account page's Publicity controls and the
	// "Public" badge on the signed-in overview. The share link is /u/<username>, so
	// Username (already on the page) is all the badge and account section need to
	// build it. This is populated from the same UserByID lookup that fills Username,
	// so reading it costs no extra query.
	OverviewPublic bool
}

Page is the shared layout context for every rendered page.

type RangeOption

type RangeOption struct {
	Label  string
	Href   string
	Active bool
}

RangeOption is one rendered button in the trailing-window selector: the window label, the URL the button refetches the usage panel from, and whether it is the active window. The href is built per panel so the selector can sit on any page (the global overview or one project) and refetch from that page's own path.

func ProjectRangeOptions

func ProjectRangeOptions(projectID int64, sel store.SessionFilter, active string) []RangeOption

ProjectRangeOptions is RangeOptions for a project's usage panel: it roots the selector at the project path and preserves the active session filters, so the window control and the filter rail share the URL without clobbering each other.

func RangeOptions

func RangeOptions(basePath string, preserve url.Values, active string) []RangeOption

RangeOptions builds the selector's buttons for a panel rooted at basePath. Any params in preserve (except range, which each button sets) ride along on every href, so switching the window on a page that also carries other query state (the project page's session filters) does not drop that state from the URL.

type ReparseView

type ReparseView struct {
	InProgress bool
	Done       int
	Total      int
	Failed     int
}

ReparseView is the reparse status the account page renders: whether one is running and how far along. The httpapi layer fills it from the reparse service, so the web package stays free of a dependency on that service.

type SessionDayGroup

type SessionDayGroup struct {
	Label string
	Rows  []FeedRow
}

SessionDayGroup is a run of feed rows under one day heading. Label is empty when the feed is not day-grouped (any order other than most-recent), in which case the template renders the rows without a heading.

func BuildSessionFeed

func BuildSessionFeed(ctx context.Context, rows []store.SessionRow, grouped bool) []SessionDayGroup

BuildSessionFeed groups session rows for the feed. When grouped is true (the most-recent order) the rows, already sorted newest first, are split under day headings ("Today", "Yesterday", a weekday, then a date); otherwise they form a single unlabeled group in the order the query returned. Within a group a row whose project repeats the previous row's is flagged to mute its label. Token bars scale against the largest session across the whole feed, so magnitudes are comparable between groups.

type SessionFooter added in v0.2.2

type SessionFooter struct {
	// Shown is how many rows the feed currently renders. When HasMore is false it is
	// also the exact total, since the whole matching set fit in the page; when HasMore
	// is true more rows match beyond it and the count reads "Showing N".
	Shown int
	// HasMore reports that at least one more row matches past the page (from
	// ListAllSessions' limit+1 probe), so the footer offers the next page and the count
	// reads "Showing N" rather than the exact "N sessions".
	HasMore bool
	// MoreHref is the "Show more" target (a doubled-limit path), set only when more
	// rows match than are shown and the page is below the cap.
	MoreHref string
	// AtCap reports the feed is showing the maximum page (500) yet more match, so the
	// footer names the cap and drops the button, asking the reader to narrow instead.
	AtCap bool
	// HasEmpty reports whether the current scope holds at least one empty (zero-message)
	// session, so the toggle appears only when it would change the feed; IncludeEmpty
	// reports whether those empties are being shown. Together they drive the terse
	// toggle: "empty hidden · show" when hidden, "showing empty · hide" when shown.
	HasEmpty     bool
	IncludeEmpty bool
	EmptyHref    string
}

SessionFooter is the state the feed's footer renders under the list: the session count, whether a "Show more" button applies, and the empty-hidden toggle. It is computed once from the loaded rows and two bounded store probes (hasMore and hasEmpty) so the template stays a dumb renderer and the render cost stays linear in the page rather than the corpus: the old "N of M" carried a count(*) over the whole matching history, which the incremental-efficiency gate flagged.

func BuildSessionFooter added in v0.2.2

func BuildSessionFooter(f store.SessionFilter, shown int, hasMore, hasEmpty bool) SessionFooter

BuildSessionFooter assembles the footer state from the loaded rows, the filter, and two bounded probes: hasMore (does a further page exist) and hasEmpty (does any empty session sit in scope). shown is len(rows); the "Show more" button appears only when more rows match and the page is below the cap, and the empty toggle appears only when the scope actually holds an empty session (or already shows them, so the reader can hide them again).

func (SessionFooter) CountLabel added in v0.2.2

func (ft SessionFooter) CountLabel() string

CountLabel is the footer's session-count figure, tabular and terse. When the whole matching set fit in the page (no further page) the shown count IS the exact total, so it reads "N sessions"; when more match beyond the page it reads "Showing N", since the exact total is deliberately not counted.

func (SessionFooter) EmptyToggleLabel added in v0.2.2

func (ft SessionFooter) EmptyToggleLabel() (text, verb string)

EmptyToggleLabel is the terse toggle copy: "empty hidden" and a "show" verb when empties are hidden, or "showing empty" and a "hide" verb when they are shown. It returns the two parts (the state text and the verb) so the template can style the verb as the link affordance. The count is gone with the O(total) aggregate that produced it: the toggle only reports the state, not the magnitude.

func (SessionFooter) HasEmptyToggle added in v0.2.2

func (ft SessionFooter) HasEmptyToggle() bool

HasEmptyToggle reports whether the footer shows the empty-hidden toggle.

type SessionSortOption

type SessionSortOption struct {
	Key   string
	Label string
}

SessionSortOption is one choice in the feed's sort control. The cross-project feed sorts by recency, volume, or message count; the categorical dimensions (agent, project, machine, user) are served by the filters instead, so the control stays a short list rather than a header per column.

func SessionSortOptions

func SessionSortOptions() []SessionSortOption

SessionSortOptions are the feed's sort choices, in menu order. "updated" (the default) is the only one that day-groups the feed; the others read as a flat, ranked list.

type ShedMark added in v0.2.2

type ShedMark struct {
	FromTokens int64
	ToTokens   int64
	// FromUsage and ToUsage carry the full per-turn usage behind the two occupancy figures, so
	// the shed divider's breakdown card can spell out each side's token classes and cost rather
	// than the label's two bare context totals. FromUsage is the prior measured turn's usage,
	// ToUsage this turn's; both are the same *TurnUsage the message carries on Message.Usage
	// (never nil here: a shed is detected only between two turns that both presented a context).
	FromUsage store.TurnUsage
	ToUsage   store.TurnUsage
}

ShedMark records that a message's turn shed context relative to the turn before it (a compaction or a manual clear): FromTokens is the prior turn's context occupancy, ToTokens is this turn's. The transcript draws a divider labelled "context shed: <from> -> <to>" above the marked message. Both figures are context occupancy (input + cache read + cache write), the same three-class sum quality.IsContextReset judges, so the label and the reset decision never disagree.

The shed dividers are derived from Message.Usage, the per-message fold grouped by message_ordinal with NULL-ordinal usage rows dropped (see store.messagesFullQuery). This is deliberately a different fold from the stored session_signals.context_reset_count, which gatherContextHealth derives from the RAW usage_events stream in source order (every row, NULL-ordinal ones included, no ordinal collapse). The two agree whenever each ordinal carries exactly one dated usage row, the shape a real agent produces; they can diverge (a session shows context_reset_count = 1 yet no shed divider, or vice versa) only for a multi-row turn or an unattributed row, which the schema permits but the parser does not emit. This divergence is intentional and does not need to reconcile: the divider attaches to a rendered message, so it can only mark a drop between two turns that each presented a context, while the signal measures the raw turn sequence. It is the same divergence the per-turn usage fold carries, documented on store.messagesFullQuery and pinned by store's TestMessagesTurnUsageDivergesFromContextFold.

type SnippetParts added in v0.2.2

type SnippetParts struct {
	Before string
	Match  string
	After  string
}

SnippetParts splits a search snippet into its before/match/after text runs so the template can render each as an auto-escaped text node and wrap only the middle in <mark>. The <mark> element is thus template structure around plain text: the matched content is never interpolated as markup, so a query or a message that contains "<script>" renders as escaped text inside the highlight rather than injecting an element. Offsets out of range (a malformed snippet) collapse to the whole text as the "before" run with an empty match, so a bad window degrades to plain unhighlighted text rather than a panic.

func SplitSnippet added in v0.2.2

func SplitSnippet(s store.SearchSnippet) SnippetParts

SnippetParts computes the three runs from a store snippet's byte offsets.

type ToolBar

type ToolBar struct {
	Name    string
	Calls   int
	Pct     float64
	ErrText string // "" when the tool never failed, else its error rate ("12%")
	Color   string
}

ToolBar is one tool's bar in the mix: sized by call volume, coloured by its error band, and annotated with its error rate when it had any failures. The dual encoding reads mix (bar length) and reliability (colour and the error suffix) in one row.

func ToolBars

func ToolBars(t store.ToolStats) []ToolBar

ToolBars turns the fleet's busiest tools into renderable bars: each width is its share of the most-called tool, so the busiest tool is full and the rest are relative, and each colour bands the tool's reliability. A non-zero bar always shows at least a sliver so a rarely-called tool never reads as absent beside a dominant one.

type TranscriptWalker added in v0.2.2

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

TranscriptWalker derives the two carried per-message instruments (reply latency and context sheds) as the render walks the message slice in order, holding only O(1) state between messages rather than a second session-sized structure. The render path already holds the message slice the page renders; the walker adds nothing that scales with the session beyond that. Instantiate one per transcript render and call Next once per message in order.

The carried state is exactly:

  • anchor: the pending user prompt's timestamp (nil when no turn is open), for latency.
  • prevContext / prevUsage / havePrev: the previous measured turn's context occupancy and its full usage, for shed detection against the current turn.

The two sub-computations keep their own semantics:

  • Latency pairs each answered user prompt with the reply that followed it. The walk holds the time of the most recent timestamped user message; the FIRST following timestamped assistant message closes that turn (latency = its time minus the anchor's), and the anchor is then cleared so a later reply in the same stretch is not double-counted. A new user message resets the anchor, so an interleaved exchange measures each prompt against its own reply. A negative gap (clock skew, or timestamps out of order relative to ordinal order) is dropped rather than shown as a nonsense stamp. A message missing a timestamp is transparent: a user prompt with none sets no anchor, and an assistant with none cannot close one.

  • Shed marks the turns where context dropped (a compaction or a clear). For each message that carries usage (Message.Usage != nil), it compares that turn's context occupancy against the previous message-with-usage's through quality.IsContextReset; messages without usage are skipped, so the comparison is always between two turns that actually presented a context. The shed is attributed to the turn AFTER the drop (the smaller, post-compaction context), where the transcript draws the divider: above the message that first ran on the shed-down context. This reads the ordinal-grouped Message.Usage fold, so it can intentionally diverge from session_signals.context_reset_count (the raw-stream fold); see ShedMark for why the two need not reconcile.

The duplicate-prompt badge is not derived here: it is folded in the message read as store.Message.DuplicatePrompt over the whole session (see store.messagesFullQuery), so the walker needs no digest set and the render reads m.DuplicatePrompt directly.

func (*TranscriptWalker) Next added in v0.2.2

Next advances the walker over one message and returns that message's latency and shed marks. Messages must be passed in transcript (ordinal) order, once each; the walker's carried state depends on the order.

Jump to

Keyboard shortcuts

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