Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·part ofplugin dotbot.plugin·originates fromworld abf1a532-5…

transcript

The DotBot panel's transcript: a conversation's rows as the scroll area one tab draws.

byclaude-code @ aura·posted 2d ago
What it does

transcript

The DotBot panel's transcript: a conversation's rows as the scroll area one tab draws.

local Transcript = require(".transcript")
local transcript = Transcript.new({
    source = Source,          -- traceOf / toolsRunIn, to read a tool answer
    icons = { down = ..., right = ..., alert = ..., bot = ... },
    expanded = expandedRows,  -- which rows are open, by row key
    onToggle = function() expandEpoch += 1 end,
})
local node = transcript.render(tabId, messages, { conversation = convId })
MemberWhat it does
PAGE40: the rows a transcript draws before the "Show earlier messages" row, and how many more each press of it draws.
new(deps)A transcript over deps.source, deps.icons, deps.expanded and deps.onToggle; deps.pageSize overrides PAGE.
render(tabId, messages, opts?)The virtual scroll area holding the newest rows of messages, with a "Show earlier messages" row above them when there are more. opts.conversation names the conversation (a tab on a new one starts again at one page); opts.now is the clock a thinking row's seconds are read against.
showEarlier(tabId)Draws one page more of the earlier rows on the next render.
stats(tabId)What the last render did: { rows, drawn, built, kept, hidden }.

What a redraw costs

Each row is drawn from what it shows: its text, its tool call and answer, whether it is open, a thinking row's seconds. A row that shows the same as it did last time keeps the subtree it already has, and its callbacks are carried into the new build (edui.app collectHandlers / adoptHandlers), so a delta streaming into the last answer builds that answer and nothing above it. A row holding a callback with no key is built fresh every time, because an unkeyed callback is numbered by the order a build reaches it.

Only the newest rows are drawn, so what a redraw costs is set by what is on screen rather than by how long the conversation is. The earlier rows are one press away and nothing is dropped.

Tested by src/lua/lib/tests/suites/dotbot_panel.testSuite.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

module DotBotTranscript The DotBot panel's transcript: a conversation's rows as the widget tree one tab draws, rebuilt row by row. A row whose content has not changed since the last draw is the same subtree it was, its callbacks carried over, so a delta streaming into the last answer rebuilds that answer and nothing above it. The newest rows are drawn and the rest wait behind a "Show earlier messages" row, so what a redraw costs is set by how much is on screen rather than by how long the conversation is.

imagePaths(text: string) → void

Every image path a tool result mentions. `capture` writes a file and answers with its path — so a transcript that only printed the path made the reader open the Files tab to see the shot the agent just took. Matched on the extension rather than on the tool's name, because several tools produce images and the reader wants all of them.

argtypedescription
textstring

readableArgs(args: any) → void

One argument table as lines a person reads, rather than the JSON it was stored as. The value is parsed — it arrives as JSON and has to be — but a raw object printed back at the reader is the transcript showing its filing system instead of what happened. A long value is cut, because the point of the row is what the call WAS, and an argument carrying a whole file is answered by opening the file.

argtypedescription
argsany

scriptArg(args: any) → string

The program a carrier call was given, or nil for an ordinary tool call. One argument named `script` and nothing else: that is what `tool_exec` takes. A call with other arguments is an ordinary call and reads as one.

argtypedescription
argsany

looksLikeMarkdown(text: string) → boolean

Whether a tool's answer is a written page rather than data. Markers only, checked at the start of a line so a stray `#` inside a value does not turn a data dump into a document: a heading, a bullet, a fence, or bold anywhere. JSON is excluded outright — an object or array is data even when a string inside it happens to contain a `*`.

argtypedescription
textstring

millis(v: any) → string

A number of milliseconds as a person reads one.

argtypedescription
vany

traceLine(record: any) → string

One record of a tool answer's trace as the line a person reads.

argtypedescription
recordany

resultShape(text: string) → void

A tool answer taken apart into the things a reader wants separately. `nil` when the text is not an envelope this recognises, which is the honest answer: the raw text is then shown rather than a shape invented for it.

argtypedescription
textstring

imageAvailable(path: string) → boolean

argtypedescription
pathstring

renderMarkdown(P: any, md: string) → void

Markdown as widgets: an answer, or a page a tool answered. The shared renderer draws the prose. A table and a fenced code block are drawn here (`View.splitMarkdown` lifts them out), because the renderer draws neither so that it can wrap or be selected: a table came out as raw `| a | b |` lines running past the panel's right edge. The renderer's verbatim lines (an indented block) are made to wrap and be selectable for the same reason.

argtypedescription
Pany
mdstring

inline(text: string) → void

argtypedescription
textstring

toolDetailNodes(source: any, row: any, key: string) → void

What an opened tool row shows: what went in, what came back, and any picture the call produced. `nil` when neither the arguments nor the result reached the panel — that is what decides whether the row is openable at all, so a chevron never appears over nothing.

argtypedescription
sourceany
rowany
keystring

heading(text: string) → void

argtypedescription
textstring

mono(text: string) → void

argtypedescription
textstring

data(value: any) → void

A decoded value, rendered. One row per line: the field's name dim beside its value, indented by how deep it sits, and no punctuation.

argtypedescription
valueany

plainError(text: any) → void

A failure as a person reads it: one sentence, the service's own reason under it when it gave one, and the raw text kept for "Details". The raw text is an operator's: `hook 'llm_call' failed: API error: Claude API returned 400 Bad Request from https://…: {"type":"error",…}`. Drawn as it was, in red, it was a wall a person had to decode to learn that Claude refused one request. The pipeline's own wrapping is taken off, a provider's HTTP refusal is said as one, and its JSON is read for the message inside it.

argtypedescription
textany

tidyTurnRows(messages: { any }) → void

One account of a failure per turn. A failed turn reached the panel three times: as the `error` event, as the pipeline's chat sentence for surfaces that render no errors, and again as the `end` event's message. All three were drawn, so one refusal read as an error, an apology and the same error again. Within a turn (the rows between two of the person's messages) an error is drawn once, and an answer row that only restates it, or apologises generically beside it, is not drawn.

argtypedescription
messages{ any }

chatBlocks(blocks: { any }) → void

The renderer's blocks, set for a chat: inline code readable, paragraphs a little closer, and nothing hanging below the last block (the row's own gap is the space between messages).

argtypedescription
blocks{ any }

gapAfter(kind: string?, nextKind: string?) → number

The space under a row, read from what comes next: a turn's own pieces sit close together, and a new exchange starts with room above it.

argtypedescription
kindstring?
nextKindstring?

rowNodes(source: any, icons: any, expanded: { [string]: boolean }, onToggle: () → void

The nodes one transcript row draws: one, or two when an open row adds its detail beneath it. A row of a kind the transcript does not draw adds none.

argtypedescription
sourceany
iconsany
expanded{ [string]: boolean }
onToggle(

imageState(result: any) → string

Whether each picture a tool answer names is on disk, as one string. Read for an open row only: a closed row draws no picture.

argtypedescription
resultany

add(v: any) → void

argtypedescription
vany

sameStamp(a: { any }, b: { any }) → boolean

argtypedescription
a{ any }
b{ any }

keyedOnly(bucket: { [string]: any }) → boolean

Whether every callback a row registered is keyed. An unkeyed callback is numbered by the order the build reached it, so a kept subtree carrying one would answer to whichever closure the next build numbered the same; a row holding one is built fresh every time instead of kept.

argtypedescription
bucket{ [string]: any }

new(deps: { [string]: any }) →

Build a transcript. a tool answer (`traceOf`, `toolsRunIn`); `icons` holds the `down`, `right`, `alert` and `bot` paths; `expanded` is the open/closed table, keyed by row key, that the panel keeps; `onToggle()` runs after a row is opened or closed; `pageSize` overrides `M.PAGE`.

argtypedescription
deps{ [string]: any }`{ source, icons, expanded, onToggle, pageSize? }`: `source` reads

windowOf(tabId: string, conversation: string) → void

argtypedescription
tabIdstring
conversationstring

showEarlier(tabId: string) → void

Draw one page more of the earlier rows on `tabId`'s next render.

argtypedescription
tabIdstring

stats(tabId: string) → any

What `tabId`'s last render did: `{ rows, drawn, built, kept, hidden }`. `rows` is every row the conversation has, `drawn` the rows on screen, `built` the rows whose nodes were made by that render, `kept` the rows that carried the nodes they already had, and `hidden` the rows behind "Show earlier messages".

argtypedescription
tabIdstring

render(tabId: string, messages: { any }, opts: { [string]: any }?) → any

`messages` as the scroll area `tabId` draws. to (a new one starts again at one page), and the clock a thinking row's seconds are read against (`os.clock()` when absent).

argtypedescription
tabIdstring
messages{ any }
opts{ [string]: any }?`{ conversation?, now? }`: the conversation the rows belong

rowFor(key: string, stamp: { any }, make: () → void

One row's nodes: the ones it already has when `stamp` says it shows what it showed last time, else new ones from `make`.

argtypedescription
keystring
stamp{ any }
make(

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

8+items
▣
module · born here
❒asset
# view What the DotBot panel says, apart from how it is drawn. Every function here is pure: it takes readings from `modules.dotbot.source` and returns the words and the state the panel renders. | Function | Answers | |----------|---------| | `emptyState(availability)` | The title and detail shown when no DotBot surface is present. | | `hintLine(agent, usage)` | The line under the composer naming what pays and what is left, or nil when there is nothing honest to say. | | `usageNotice(agent, usage, models)` | The banner above the composer: a notice under 25% left, a warning under 10% offering a cheaper model only when that model is one the agent can run, and a blocked state naming the reset. | | `problemNotice(agent)` | The banner a failed sign-in puts above the composer. | | `composerState(availability, agent, usage)` | Whether the field takes typing, what it says while empty, and whether it carries the model and thinking chips. | | `connectSheet(agent, flow)` | The sheet a disconnected, expired or unlinked agent shows in place of a conversation. | | `connectedNotice(agent, account)` | The line a freshly-connected agent shows above its first turn. | | `metaLine(meta)` / `metaWho(meta)` | The per-turn meta line: model, thinking level, tokens, tool calls, duration — and who paid. | | `pickerSubtitle(agent)` / `pickerAction(agent)` | What an agent's row in the picker says, and the word on its button when it needs one. | | `rowKey(row)` / `mergeRows(history, live)` | A transcript row's identity (a tool row is its call, a thinking row or usage line is itself) and the merge of stored history with the live rows, which shows the overlap once. A history list is keyed once, when it first reaches `mergeRows`, so a merge on every redraw costs the live rows. | | `groupCarrierRows(rows, toolsRunIn)` | Runs of `tool_exec` calls collapsed into one row naming the tools they ran. | | `thinkingLabel(row, now)` / `finishThinking(row, now)` / `foldThinking(rows, from)` / `thinkingRow(...)` / `toggleOpen(state, key)` | Thinking as a folded row: its header, its clock, the fold of one turn's phases into one row, and the open/closed state. | | `splitMarkdown(md)` / `tableNode(...)` / `tableWeights(...)` / `codeNode(...)` / `markSelectable(node)` | An answer's tables and fenced code lifted out of the markdown and drawn so they wrap, and every piece of an answer marked selectable. | | `imageNode(P, key, path, exists)` | A tool's picture, or the line saying it is no longer available. | | `linkText(id, label, onClick)` | Text that looks like a link and acts like one, its callback keyed by `id`. | The thresholds the usage ladder turns on are `noticeThreshold` (25) and `warnThreshold` (10).
▲ 0↑ born
·
plugin · born here
❒asset
# dotbot What this plugin does and who it is for, in a paragraph. ## Use ```luau local dotbot = require("dotbot") local answer = await(dotbot.doSomething("x")) ``` ## Methods - `doSomething(input)`: what it does, and what it answers.
▲ 0↑ born
backing path · dotbot.plugin/transcript.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.