mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 16:46:58 +10:00
243 lines
27 KiB
TypeScript
243 lines
27 KiB
TypeScript
|
|
import { c as _c } from "react/compiler-runtime";
|
||
|
|
import React, { useRef } from 'react';
|
||
|
|
import type { RemoteAgentTaskState } from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js';
|
||
|
|
import type { DeepImmutable } from 'src/types/utils.js';
|
||
|
|
import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js';
|
||
|
|
import { useSettings } from '../../hooks/useSettings.js';
|
||
|
|
import { Text, useAnimationFrame } from '../../ink.js';
|
||
|
|
import { count } from '../../utils/array.js';
|
||
|
|
import { getRainbowColor } from '../../utils/thinking.js';
|
||
|
|
const TICK_MS = 80;
|
||
|
|
type ReviewStage = NonNullable<NonNullable<RemoteAgentTaskState['reviewProgress']>['stage']>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stage-appropriate counts line for a running review. Shared between the
|
||
|
|
* one-line pill (below) and RemoteSessionDetailDialog's reviewCountsLine so
|
||
|
|
* the two can't drift — they have historically disagreed on whether to show
|
||
|
|
* refuted counts and what to call the synthesizing stage.
|
||
|
|
*
|
||
|
|
* Canonical behavior: word labels (not ✓/✗), hide refuted when 0, "deduping"
|
||
|
|
* for the synthesizing stage (matches STAGE_LABELS in the detail dialog).
|
||
|
|
*/
|
||
|
|
export function formatReviewStageCounts(stage: ReviewStage | undefined, found: number, verified: number, refuted: number): string {
|
||
|
|
// Pre-stage orchestrator images don't write the stage field.
|
||
|
|
if (!stage) return `${found} found · ${verified} verified`;
|
||
|
|
if (stage === 'synthesizing') {
|
||
|
|
const parts = [`${verified} verified`];
|
||
|
|
if (refuted > 0) parts.push(`${refuted} refuted`);
|
||
|
|
parts.push('deduping');
|
||
|
|
return parts.join(' · ');
|
||
|
|
}
|
||
|
|
if (stage === 'verifying') {
|
||
|
|
const parts = [`${found} found`, `${verified} verified`];
|
||
|
|
if (refuted > 0) parts.push(`${refuted} refuted`);
|
||
|
|
return parts.join(' · ');
|
||
|
|
}
|
||
|
|
// stage === 'finding'
|
||
|
|
return found > 0 ? `${found} found` : 'finding';
|
||
|
|
}
|
||
|
|
|
||
|
|
// Per-character rainbow gradient, same treatment as the ultraplan keyword.
|
||
|
|
// The phase offset lets the gradient cycle — so the colors sweep along the
|
||
|
|
// text on each animation frame instead of being static.
|
||
|
|
function RainbowText(t0) {
|
||
|
|
const $ = _c(5);
|
||
|
|
const {
|
||
|
|
text,
|
||
|
|
phase: t1
|
||
|
|
} = t0;
|
||
|
|
const phase = t1 === undefined ? 0 : t1;
|
||
|
|
let t2;
|
||
|
|
if ($[0] !== text) {
|
||
|
|
t2 = [...text];
|
||
|
|
$[0] = text;
|
||
|
|
$[1] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[1];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[2] !== phase || $[3] !== t2) {
|
||
|
|
t3 = <>{t2.map((ch, i) => <Text key={i} color={getRainbowColor(i + phase)}>{ch}</Text>)}</>;
|
||
|
|
$[2] = phase;
|
||
|
|
$[3] = t2;
|
||
|
|
$[4] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[4];
|
||
|
|
}
|
||
|
|
return t3;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Smooth-tick a count toward target, +1 per frame. Same pattern as the
|
||
|
|
// token counter in SpinnerAnimationRow — the ref survives re-renders and
|
||
|
|
// the animation clock drives the tick. Target jumps (2→5) display as
|
||
|
|
// 2→3→4→5 instead of snapping. When `snap` is set (reduced motion, or
|
||
|
|
// the clock is frozen), bypass the tick and jump straight to target —
|
||
|
|
// otherwise a frozen `time` would leave the ref stuck at its init value.
|
||
|
|
function useSmoothCount(target: number, time: number, snap: boolean): number {
|
||
|
|
const displayed = useRef(target);
|
||
|
|
const lastTick = useRef(time);
|
||
|
|
if (snap || target < displayed.current) {
|
||
|
|
displayed.current = target;
|
||
|
|
} else if (target > displayed.current && time !== lastTick.current) {
|
||
|
|
displayed.current += 1;
|
||
|
|
lastTick.current = time;
|
||
|
|
}
|
||
|
|
return displayed.current;
|
||
|
|
}
|
||
|
|
function ReviewRainbowLine(t0) {
|
||
|
|
const $ = _c(15);
|
||
|
|
const {
|
||
|
|
session
|
||
|
|
} = t0;
|
||
|
|
const settings = useSettings();
|
||
|
|
const reducedMotion = settings.prefersReducedMotion ?? false;
|
||
|
|
const p = session.reviewProgress;
|
||
|
|
const running = session.status === "running";
|
||
|
|
const [, time] = useAnimationFrame(running && !reducedMotion ? TICK_MS : null);
|
||
|
|
const targetFound = p?.bugsFound ?? 0;
|
||
|
|
const targetVerified = p?.bugsVerified ?? 0;
|
||
|
|
const targetRefuted = p?.bugsRefuted ?? 0;
|
||
|
|
const snap = reducedMotion || !running;
|
||
|
|
const found = useSmoothCount(targetFound, time, snap);
|
||
|
|
const verified = useSmoothCount(targetVerified, time, snap);
|
||
|
|
const refuted = useSmoothCount(targetRefuted, time, snap);
|
||
|
|
const phase = Math.floor(time / (TICK_MS * 3)) % 7;
|
||
|
|
if (session.status === "completed") {
|
||
|
|
let t1;
|
||
|
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = <><Text color="background">{DIAMOND_FILLED} </Text><RainbowText text="ultrareview" phase={0} /><Text dimColor={true}> ready · shift+↓ to view</Text></>;
|
||
|
|
$[0] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[0];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
if (session.status === "failed") {
|
||
|
|
let t1;
|
||
|
|
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = <><Text color="background">{DIAMOND_FILLED} </Text><RainbowText text="ultrareview" phase={0} /><Text color="error" dimColor={true}>{" \xB7 "}error</Text></>;
|
||
|
|
$[1] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[1];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
let t1;
|
||
|
|
if ($[2] !== found || $[3] !== p || $[4] !== refuted || $[5] !== verified) {
|
||
|
|
t1 = !p ? "setting up" : formatReviewStageCounts(p.stage, found, verified, refuted);
|
||
|
|
$[2] = found;
|
||
|
|
$[3] = p;
|
||
|
|
$[4] = refuted;
|
||
|
|
$[5] = verified;
|
||
|
|
$[6] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[6];
|
||
|
|
}
|
||
|
|
const tail = t1;
|
||
|
|
let t2;
|
||
|
|
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t2 = <Text color="background">{DIAMOND_OPEN} </Text>;
|
||
|
|
$[7] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[7];
|
||
|
|
}
|
||
|
|
const t3 = running ? phase : 0;
|
||
|
|
let t4;
|
||
|
|
if ($[8] !== t3) {
|
||
|
|
t4 = <RainbowText text="ultrareview" phase={t3} />;
|
||
|
|
$[8] = t3;
|
||
|
|
$[9] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[9];
|
||
|
|
}
|
||
|
|
let t5;
|
||
|
|
if ($[10] !== tail) {
|
||
|
|
t5 = <Text dimColor={true}> · {tail}</Text>;
|
||
|
|
$[10] = tail;
|
||
|
|
$[11] = t5;
|
||
|
|
} else {
|
||
|
|
t5 = $[11];
|
||
|
|
}
|
||
|
|
let t6;
|
||
|
|
if ($[12] !== t4 || $[13] !== t5) {
|
||
|
|
t6 = <>{t2}{t4}{t5}</>;
|
||
|
|
$[12] = t4;
|
||
|
|
$[13] = t5;
|
||
|
|
$[14] = t6;
|
||
|
|
} else {
|
||
|
|
t6 = $[14];
|
||
|
|
}
|
||
|
|
return t6;
|
||
|
|
}
|
||
|
|
export function RemoteSessionProgress(t0) {
|
||
|
|
const $ = _c(11);
|
||
|
|
const {
|
||
|
|
session
|
||
|
|
} = t0;
|
||
|
|
if (session.isRemoteReview) {
|
||
|
|
let t1;
|
||
|
|
if ($[0] !== session) {
|
||
|
|
t1 = <ReviewRainbowLine session={session} />;
|
||
|
|
$[0] = session;
|
||
|
|
$[1] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[1];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
if (session.status === "completed") {
|
||
|
|
let t1;
|
||
|
|
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = <Text bold={true} color="success" dimColor={true}>done</Text>;
|
||
|
|
$[2] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[2];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
if (session.status === "failed") {
|
||
|
|
let t1;
|
||
|
|
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = <Text bold={true} color="error" dimColor={true}>error</Text>;
|
||
|
|
$[3] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[3];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
if (!session.todoList.length) {
|
||
|
|
let t1;
|
||
|
|
if ($[4] !== session.status) {
|
||
|
|
t1 = <Text dimColor={true}>{session.status}…</Text>;
|
||
|
|
$[4] = session.status;
|
||
|
|
$[5] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[5];
|
||
|
|
}
|
||
|
|
return t1;
|
||
|
|
}
|
||
|
|
let t1;
|
||
|
|
if ($[6] !== session.todoList) {
|
||
|
|
t1 = count(session.todoList, _temp);
|
||
|
|
$[6] = session.todoList;
|
||
|
|
$[7] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[7];
|
||
|
|
}
|
||
|
|
const completed = t1;
|
||
|
|
const total = session.todoList.length;
|
||
|
|
let t2;
|
||
|
|
if ($[8] !== completed || $[9] !== total) {
|
||
|
|
t2 = <Text dimColor={true}>{completed}/{total}</Text>;
|
||
|
|
$[8] = completed;
|
||
|
|
$[9] = total;
|
||
|
|
$[10] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[10];
|
||
|
|
}
|
||
|
|
return t2;
|
||
|
|
}
|
||
|
|
function _temp(_) {
|
||
|
|
return _.status === "completed";
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVJlZiIsIlJlbW90ZUFnZW50VGFza1N0YXRlIiwiRGVlcEltbXV0YWJsZSIsIkRJQU1PTkRfRklMTEVEIiwiRElBTU9ORF9PUEVOIiwidXNlU2V0dGluZ3MiLCJUZXh0IiwidXNlQW5pbWF0aW9uRnJhbWUiLCJjb3VudCIsImdldFJhaW5ib3dDb2xvciIsIlRJQ0tfTVMiLCJSZXZpZXdTdGFnZSIsIk5vbk51bGxhYmxlIiwiZm9ybWF0UmV2aWV3U3RhZ2VDb3VudHMiLCJzdGFnZSIsImZvdW5kIiwidmVyaWZpZWQiLCJyZWZ1dGVkIiwicGFydHMiLCJwdXNoIiwiam9pbiIsIlJhaW5ib3dUZXh0IiwidDAiLCIkIiwiX2MiLCJ0ZXh0IiwicGhhc2UiLCJ0MSIsInVuZGVmaW5lZCIsInQyIiwidDMiLCJtYXAiLCJjaCIsImkiLCJ1c2VTbW9vdGhDb3VudCIsInRhcmdldCIsInRpbWUiLCJzbmFwIiwiZGlzcGxheWVkIiwibGFzdFRpY2siLCJjdXJyZW50IiwiUmV2aWV3UmFpbmJvd0xpbmUiLCJzZXNzaW9uIiwic2V0dGluZ3MiLCJyZWR1Y2VkTW90aW9uIiwicHJlZmVyc1JlZHVjZWRNb3Rpb24iLCJwIiwicmV2aWV3UHJvZ3Jlc3MiLCJydW5uaW5nIiwic3RhdHVzIiwidGFyZ2V0Rm91bmQiLCJidWdzRm91bmQiLCJ0YXJnZXRWZXJpZmllZCIsImJ1Z3NWZXJpZmllZCIsInRhcmdldFJlZnV0ZWQiLCJidWdzUmVmdXRlZCIsIk1hdGgiLCJmbG9vciIsIlN5bWJvbCIsImZvciIsInRhaWwiLCJ0NCIsInQ1IiwidDYiLCJSZW1vdGVTZXNzaW9uUHJvZ3Jlc3MiLCJpc1JlbW90ZVJldmlldyIsInRvZG9MaXN0IiwibGVuZ3RoIiwiX3RlbXAiLCJjb21wbGV0ZWQiLCJ0b3RhbCIsIl8iXSwic291cmNlcyI6WyJSZW1vdGVTZXNzaW9uUHJvZ3Jlc3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgUmVtb3RlQWdlbnRUYXNrU3RhdGUgfSBmcm9tICdzcmMvdGFza3MvUmVtb3RlQWdlbnRUYXNrL1JlbW90ZUFnZW50VGFzay5qcydcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcbmltcG9ydCB7IERJQU1PTkRfRklMTEVELCBESUFNT05EX09QRU4gfSBmcm9tICcuLi8uLi9jb25zdGFudHMvZmlndXJlcy5qcydcbmltcG9ydCB7IHVzZVNldHRpbmdzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlU2V0dGluZ3MuanMnXG5pbXBvcnQgeyBUZXh0LCB1c2VBbmltYXRpb25GcmFtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgeyBnZXRSYWluYm93Q29sb3IgfSBmcm9tICcuLi8uLi91dGlscy90aGlua2luZy5qcydcblxuY29uc3QgVElDS19NUyA9IDgwXG5cbnR5cGUgUmV2aWV3U3RhZ2UgPSBOb25OdWxsYWJsZTxcbiAgTm9uTnVsbGFibGU8UmVtb3RlQWdlbnRUYXNrU3RhdGVbJ3Jldmlld1Byb2dyZXNzJ10+WydzdGFnZSddXG4+XG5cbi8qKlxuICogU3RhZ2UtYXBwcm9wcmlhdGUgY291bnRzIGxpbmUgZm9yIGEgcnVubmluZyByZXZpZXcuIFNoYXJlZCBiZXR3ZWVuIHRoZVxuICogb25lLWxpbmUgcGlsbCAoYmVsb3cpIGFuZCBSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nJ3MgcmV2aWV3Q291bnRzTGluZSBzb1xuICogdGhlIHR3byBjYW4ndCBkcmlmdCDigJQgdGhleSBoYXZlIGhpc3RvcmljYWxseSBkaXNhZ3JlZWQgb24gd2hldGhlciB0byBzaG93XG4gKiByZWZ1dGVkIGNvdW50cyBhbmQgd2hhdCB0byBjYWxsIHRoZSBzeW50aGVzaXppbmcgc3RhZ2UuXG4gKlxuICogQ2Fub25pY2FsIGJlaGF2aW9yOiB3b3JkIGxhYmVscyAobm90IOKcky/inJcpLCBoaWRlIHJlZnV0ZWQgd2hlbiAwLCBcImRlZHVwaW5nXCJcbiAqIGZvciB0aGUgc3ludGhlc2l6aW5nIHN0YWdlIChtYXRjaGVzIFNUQUdFX0xBQkVMUyBpbiB0aGUgZGV0YWlsIGRpYWxvZykuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRSZXZpZXdTdGFnZUNvdW50cyhcbiAgc3RhZ2U6IFJldmlld1N0YWdlIHwgdW5kZWZpbmVkLFxuICBmb3VuZDogbnVtYmVyLFxuICB2ZXJpZmllZDogbnVtYmVyLFxuICByZWZ1dGVkOiBudW1iZXIsXG4pOiBzdHJpbmcge1xuICAvLyBQcmUtc3RhZ2Ugb3JjaGVzdHJhdG9yIGltYWdlcyBkb24ndCB3cml0ZSB0aGUgc3RhZ2UgZmllbGQuXG4gIGlmICghc3RhZ2UpIHJldHVybiBgJHtmb3VuZH0gZm91bmQgwrcgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXG4gIGlmIChzdGFnZSA9PT0gJ3N5bnRoZXNpemluZycpIHtcbiAgICBjb25zdCBwYXJ0cyA9IFtgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXVxuICAgIGlmIChyZWZ1dGVkID4gMCkgcGFydHMucHVzaChgJHtyZWZ1dGVkfSByZWZ1dGVkYClcbiAgICBwYXJ0cy5wdXNoKCdkZWR1cGluZycpXG4gICAgcmV0dXJuIHBhcnRzLmpvaW4oJyDCtyAnKVxuICB9XG4gIGlmIChzdGFnZSA9PT0gJ3ZlcmlmeWluZycpIHtcbiAgICBjb25zdCBwYXJ0cyA9IFtgJHtmb3VuZH0gZm91bmRgLCBgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXVxuICAgIGlmIChyZWZ1dGVkID4gMCkgcGFydHMucHVzaChgJHtyZWZ1dGVkfSByZWZ1dGVkYClcbiAgICByZXR1cm4gcGFydHMuam9pbignIMK3ICcpXG4gIH1cbiAgLy8gc3RhZ2UgPT09ICdmaW5kaW5nJ1xuICByZXR1cm4gZm91bmQgPiAwID8gYCR7Zm91bmR9IGZvdW5kYCA6ICdmaW5kaW5nJ1xufVxuXG4vLyBQZXItY2hhcmFjdGVyIHJhaW5ib3cgZ3JhZGllbnQsIHNhbWUgdHJlYXRtZW50IGFzIHRoZSB1bHRyYXBsYW4ga2V5d29yZC5cbi8vIFRoZSBwaGFzZSBvZmZzZXQgbGV0cyB0aGUgZ3JhZGllbnQgY3ljbGUg4oCUIHNvIHRoZSBjb2xvcnMgc3dlZXAgYWxvbmcgdGhlXG4vLyB0ZXh0IG9uIGVhY2ggYW5pbWF0aW9uIGZyYW1lIGluc3RlYWQgb2YgYmVpbmcgc3RhdGljLlxuZnVuY3Rpb24gUmFpbmJvd1RleHQoe1xuICB0ZXh0LFxuICBwaGFzZSA9IDAsXG59OiB7XG4gIHRleHQ6IHN0cmluZ1xuICBwaGFzZT86IG51bWJlclxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4
|