|
3 | 3 | // also shows the reply being written. |
4 | 4 |
|
5 | 5 | import { CircleAlert } from "lucide-react"; |
6 | | -import type { ReactNode } from "react"; |
| 6 | +import { memo, type ReactNode } from "react"; |
7 | 7 | import { useT } from "../../i18n"; |
8 | 8 | import { duration, tokens, usedTokens, when } from "../../lib/format"; |
9 | 9 | import type { Bot, ChatItem, TurnItem } from "../../lib/protocol.gen"; |
10 | 10 | import { useArrival } from "../../ui/motion"; |
11 | 11 | import { BotAvatar, moodOf } from "../bots/BotAvatar"; |
12 | 12 | import { ApprovalCard } from "./ApprovalCard"; |
13 | 13 | import { Markdown } from "./Markdown"; |
14 | | -import { runParts } from "./rows"; |
| 14 | +import { runParts, splitDraft } from "./rows"; |
15 | 15 | import { ToolLines } from "./ToolLines"; |
16 | 16 |
|
17 | 17 | export interface Live { |
@@ -70,7 +70,33 @@ function Working() { |
70 | 70 | ); |
71 | 71 | } |
72 | 72 |
|
73 | | -export function BotRun({ |
| 73 | +function Draft({ text }: { text: string }) { |
| 74 | + const [done, writing] = splitDraft(text); |
| 75 | + return ( |
| 76 | + <div aria-live="off" className="flex animate-fade flex-col gap-[0.6em] opacity-90"> |
| 77 | + {done && <Markdown text={done} />} |
| 78 | + <Markdown text={writing} streaming /> |
| 79 | + </div> |
| 80 | + ); |
| 81 | +} |
| 82 | + |
| 83 | +const sameItems = (a: ChatItem[], b: ChatItem[]) => |
| 84 | + a.length === b.length && a.every((item, index) => item === b[index]); |
| 85 | + |
| 86 | +/** |
| 87 | + * Re-renders only when something it shows changed: finished runs stay put |
| 88 | + * while the last one streams. |
| 89 | + */ |
| 90 | +export const BotRun = memo( |
| 91 | + BotRunView, |
| 92 | + (before, after) => |
| 93 | + before.bot === after.bot && |
| 94 | + before.live?.draft === after.live?.draft && |
| 95 | + before.live?.working === after.live?.working && |
| 96 | + sameItems(before.items, after.items), |
| 97 | +); |
| 98 | + |
| 99 | +function BotRunView({ |
74 | 100 | items, |
75 | 101 | bot, |
76 | 102 | live, |
@@ -119,11 +145,7 @@ export function BotRun({ |
119 | 145 | return null; |
120 | 146 | } |
121 | 147 | })} |
122 | | - {live?.draft && ( |
123 | | - <div aria-live="off" className="animate-fade opacity-90"> |
124 | | - <Markdown text={live.draft} streaming /> |
125 | | - </div> |
126 | | - )} |
| 148 | + {live?.draft && <Draft text={live.draft} />} |
127 | 149 | {live && !live.draft && live.working && <Working />} |
128 | 150 | </div> |
129 | 151 | </li> |
|
0 commit comments