diff options
Diffstat (limited to 'packages/web/src/components/share')
| -rw-r--r-- | packages/web/src/components/share/common.tsx | 17 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.module.css | 6 | ||||
| -rw-r--r-- | packages/web/src/components/share/part.tsx | 190 |
3 files changed, 128 insertions, 85 deletions
diff --git a/packages/web/src/components/share/common.tsx b/packages/web/src/components/share/common.tsx index 9f5221de9..cab2dbdb0 100644 --- a/packages/web/src/components/share/common.tsx +++ b/packages/web/src/components/share/common.tsx @@ -58,3 +58,20 @@ export function createOverflow() { }, } } + +export function formatDuration(ms: number): string { + const ONE_SECOND = 1000 + const ONE_MINUTE = 60 * ONE_SECOND + + if (ms >= ONE_MINUTE) { + const minutes = Math.floor(ms / ONE_MINUTE) + return minutes === 1 ? `1min` : `${minutes}mins` + } + + if (ms >= ONE_SECOND) { + const seconds = Math.floor(ms / ONE_SECOND) + return `${seconds}s` + } + + return `${ms}ms` +} diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index df8102f5d..5ffb83f6b 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -101,7 +101,12 @@ } [data-component="content"] { + flex: 1 1 auto; min-width: 0; + padding: 0 0 0.375rem; + display: flex; + flex-direction: column; + gap: 1rem; } [data-component="spacer"] { @@ -209,7 +214,6 @@ flex-direction: column; align-items: flex-start; gap: 0.375rem; - padding-bottom: 1rem; &[data-tool="bash"] { max-width: var(--sm-tool-width); diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index e155332ad..443e4a795 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -20,6 +20,7 @@ import { IconDocumentMagnifyingGlass, } from "../icons" import { IconMeta, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom" +import { formatDuration } from "../share/common" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -31,6 +32,8 @@ import type { Diagnostic } from "vscode-languageserver-types" import styles from "./part.module.css" +const MIN_DURATION = 2 + export interface PartProps { index: number message: MessageV2.Info @@ -161,95 +164,104 @@ export function Part(props: PartProps) { {props.part.type === "tool" && props.part.state.status === "error" && ( <div data-component="tool" data-tool="error"> <ContentError>{formatErrorString(props.part.state.error)}</ContentError> + <Spacer /> </div> )} {props.part.type === "tool" && props.part.state.status === "completed" && props.message.role === "assistant" && ( - <div data-component="tool" data-tool={props.part.tool}> - <Switch> - <Match when={props.part.tool === "grep"}> - <GrepTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "glob"}> - <GlobTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "list"}> - <ListTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "read"}> - <ReadTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "write"}> - <WriteTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "edit"}> - <EditTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "bash"}> - <BashTool - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - message={props.message} - /> - </Match> - <Match when={props.part.tool === "todowrite"}> - <TodoWriteTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={props.part.tool === "webfetch"}> - <WebFetchTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - <Match when={true}> - <FallbackTool - message={props.message} - id={props.part.id} - tool={props.part.tool} - state={props.part.state} - /> - </Match> - </Switch> - </div> + <> + <div data-component="tool" data-tool={props.part.tool}> + <Switch> + <Match when={props.part.tool === "grep"}> + <GrepTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "glob"}> + <GlobTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "list"}> + <ListTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "read"}> + <ReadTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "write"}> + <WriteTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "edit"}> + <EditTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "bash"}> + <BashTool + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + message={props.message} + /> + </Match> + <Match when={props.part.tool === "todowrite"}> + <TodoWriteTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={props.part.tool === "webfetch"}> + <WebFetchTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + <Match when={true}> + <FallbackTool + message={props.message} + id={props.part.id} + tool={props.part.tool} + state={props.part.state} + /> + </Match> + </Switch> + </div> + <ToolFooter + time={ + DateTime.fromMillis(props.message.time.completed || 0) + .diff(DateTime.fromMillis(props.message.time.created || 0)) + .toMillis() + } /> + </> )} </div> </div> @@ -623,6 +635,16 @@ function Footer(props: ParentProps<{ title: string }>) { ) } +function ToolFooter(props: { time: number }) { + return props.time > MIN_DURATION ? ( + <Footer title={`${props.time}ms`}> + {formatDuration(props.time)} + </Footer> + ) : ( + <Spacer /> + ) +} + export function FallbackTool(props: ToolProps) { return ( <> |
