docs: trim the run card's comments to one rationale each, four lines

This commit is contained in:
AlexRV12
2026-09-03 16:31:55 +02:00
parent 87afd0d191
commit c8d4afcc72
5 changed files with 37 additions and 54 deletions
@@ -4776,19 +4776,14 @@ export class AIChatManager {
}
/** What the transcript would be if the turn stopped here — for the writes that fire
* mid-turn without ending it. Loading is a property of this page: reloading resolves
* no card, so one stored still pending comes back asking for input nothing can
* deliver. Settles the stored copy only; the live turn keeps its cards.
* mid-turn without ending it. Loading is a property of this page: reloading resolves no
* card, so one stored still pending comes back asking for input nothing can deliver.
* Settles the stored copy only; the live turn keeps its cards.
*
* Except a card the poller resolves after a reload: settling that one stores an
* "Interrupted" error, and the patch a completed job merges in carries no error to
* clear it with. Which cards those are is loadPastChat's question, so ask it the same
* way — a job still running inline is detached by the restore and polled like any
* other.
*
* That exemption is a bet on the poller, and the poller only knows the jobs stored
* in the same record — registering one does not write it. So the jobs come back
* with the transcript that depends on them, and both go into the same saveChat. */
* Except a card the poller will resolve after a reload: settling that one stores an
* "Interrupted" error the patch a completed job merges in carries nothing to clear.
* Which cards those are is loadPastChat's question, asked the same way — and the poller
* only knows the jobs stored in the same record, so both go into the same saveChat. */
#interruptedSnapshot = (): { display: DisplayMessage[]; jobs: ChatJob[] } => {
const polled = this.#pollableToolCalls()
return {
@@ -31,11 +31,10 @@
// variables have to resolve there too.
const workspace = $derived(aiChatManager.operatingWorkspace)
// The manager's draft, not a copy of its own: this form is mounted either in the chat
// card or in the preview pane, and moving between the two has to keep what was typed.
// It is also where the deep copy off displayMessages happens — see runFormDraft.
// untrack: taken once, on purpose. The message is replaced on every patch to the card,
// and re-seeding from a later copy of it would throw away what has been typed.
// The manager's draft, not a copy of its own: the chat card and the preview pane are two
// views of one form, and moving between them has to keep what was typed. untrack because
// the message is replaced on every patch to the card, and re-seeding from a later copy of
// it would discard those edits.
const draft = untrack(() => aiChatManager.runFormDraft(toolCallId, runForm))
const properties = $derived(draft.schema?.properties ?? {})
@@ -194,11 +193,10 @@
<p class="text-2xs text-secondary">{PLAN_MODE_MESSAGES.runFormRefused}</p>
{/if}
<!-- Reject then confirm at the end of the row, as ToolConfirmationFooter puts them:
this is a tool call being validated. Both rest while a submit is in flight, since
the ephemeral variables exist by then and cancelling would settle the call as
declined on a run already starting. Escape stops the turn from here and nowhere
else in the form. -->
<!-- Reject then confirm at the end of the row, as ToolConfirmationFooter puts them: this
is a tool call being validated. Both rest while a submit is in flight — the ephemeral
variables exist by then, and cancelling would settle the call as declined on a run
already starting. Escape stops the turn from here and nowhere else in the form. -->
<div class="flex items-center justify-end gap-2" data-run-form-actions>
<Button
variant="default"
@@ -70,7 +70,7 @@
// The card stores its result as text (see formatResult), so read it back into a
// value DisplayResult can render: a markdown, table or image result is what the
// pretty view buys. A string that happens to be JSON parses back as JSON, and the
// text it was stored as is one click away under { }.
// text it was stored as is one toggle away in the raw view.
const resultValue = $derived.by(() => {
if (message.result === undefined) return undefined
if (typeof message.result !== 'string') return message.result
@@ -235,8 +235,7 @@
<!-- The run page's own status badge, so a run reads the same wherever it is met, with the
time beside it: the badge says how it went, the number how long it took, and while it
runs that number is still moving. Ahead of the label, so the chevron stays next to what
it opens and the preview chip keeps the other end of the row to itself. -->
runs that number is still moving. -->
{#snippet status()}
{#if !pending}
<span class="inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap text-2xs text-hint">
@@ -280,12 +279,10 @@
would not do it — the body is a scroll region, and a max-height silently beats
flex-grow. -->
<div class="relative flex h-[20rem] flex-col">
<!-- The tabs go in raw view: they name the parts of the body, and the raw call is not
one of them. The strip stays because the switch out of raw lives there — the run
page's own JSON toggle, which carries its label and so does not read as a fourth
tab. -->
<!-- The strip's own height, not one its contents happen to add up to: the tabs leave
in raw view, and a row sized by what is in it would step every time they do. -->
<!-- The tabs go in raw view they name the parts of the body, and the raw call is not
one of them — while the strip stays, since the JSON toggle lives there. Hence its
fixed height: a row sized by its contents would step every time the tabs leave, and
the tighter Tab padding below is what fits a label inside that height. -->
<Tabs
selected={activeTab}
on:selected={(e) => (userTab = e.detail)}
@@ -294,9 +291,6 @@
>
{#if !jsonView}
{#each tabs as tab (tab.value)}
<!-- leading-4 and the tighter padding are the strip's height: text-2xs
inherits a 22px line box, which with Tab's own padding puts 12px of air
above and below a 11px label. -->
<Tab
value={tab.value}
label={tab.label}
@@ -349,9 +343,9 @@
/>
</div>
{:else if activeTab === 'input'}
<!-- What the runs page shows a finished job's arguments as, for the same reason the
Result tab is DisplayResult: the operator has already read this table. The job id
is what lets it fetch arguments too big to have been persisted with the card. -->
<!-- What the runs page shows a finished job's arguments as: the operator has already
read this table. The job id is what lets it fetch arguments too big to have been
persisted with the card. -->
<JobArgs
args={parameters}
id={chatJob?.jobId}
@@ -394,13 +388,11 @@
hideAsJson
/>
{:else if resultValue !== undefined}
<!-- The run page's own renderer, not a second one invented for the chat: it is
what the result already looks like everywhere else, and it is the only thing
that handles markdown, tables, images, S3 files and deep nesting without the
card guessing at the shape. `disableExpand` drops its whole toolbar and
`hideAsJson` its Pretty/JSON switch: the row already owns both, opening it
bigger and reading it raw. jobId and workspace still let it reach the job for
an S3 preview. -->
<!-- The run page's own renderer, not a second one invented for the chat: it handles
markdown, tables, images and deep nesting without the card guessing at the
shape, and reaches the job through jobId/workspace for an S3 preview.
`disableExpand` drops its toolbar and `hideAsJson` its Pretty/JSON switch,
which the row already owns. -->
<DisplayResult
result={resultValue}
jobId={chatJob?.jobId}
@@ -1630,12 +1630,10 @@ export type BackgroundJobFormatter = (job: CompletedJob) => {
card: Partial<ToolDisplayMessage>
}
/** Reads a running job's output incrementally through `getJobUpdates`, which is the
* only endpoint carrying `new_result_stream`: `getJob` returns logs but never the
* partial result, so a script that streams would show nothing until it landed. Both
* the inline wait and the background poller drive one of these, so a run that detaches
* keeps streaming; each reader accumulates its own copy, so a poller that starts over
* (after a reload) refetches from offset 0 rather than appending to what it cannot see. */
/** Reads a running job's output incrementally through `getJobUpdates`, the only endpoint
* carrying `new_result_stream`: `getJob` returns logs but never the partial result. Both the
* inline wait and the background poller drive one, so a detached run keeps streaming, and
* each keeps its own offsets so one starting over refetches from zero. */
export function createJobUpdateReader(jobId: string, workspace: string) {
let logs = ''
let resultStream = ''
@@ -1853,7 +1851,7 @@ export function completedJobToolStatus(job: CompletedJob): Partial<ToolDisplayMe
content: `Background job ${job.success ? 'completed successfully' : 'failed'}`,
result: formatResult(job.result),
logs: formatLogs(job.logs),
// The partial is the result now — see the inline terminal branch.
// The partial is the result now, so nothing streamed is kept beside it.
resultStream: undefined,
...(job.success ? {} : { error: getErrorMessage(job.result) })
}
@@ -519,9 +519,9 @@ function createRuntime(session: Session): SessionRuntime {
})
}
// And let a run card move its pending form here. Not a page: the tab mounts the chat's
// own form on the same tool call, so Run in the panel is Run in the chat, and the two
// share one draft rather than being two forms proposing two jobs.
// Not a page: the tab mounts the chat's own form on the same tool call, so Run in the
// panel is Run in the chat, and the two share one draft rather than being two forms
// proposing two jobs.
manager.openRunForm = ({ toolCallId, label }) => {
previewTabs.open({ type: 'runform', toolCallId, label })
}