mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 04:38:31 +00:00
feat(app): update message part ui to v2 (#34394)
This commit is contained in:
parent
b5f92c9f48
commit
0a5e617da8
2 changed files with 92 additions and 44 deletions
|
|
@ -1013,6 +1013,7 @@ export function MessageTimeline(props: {
|
|||
message={message()}
|
||||
showAssistantCopyPartID={assistantCopyPartID(row().userMessageID)}
|
||||
turnDurationMs={turnDurationMs(row().userMessageID)}
|
||||
useV2Actions={settings.general.newLayoutDesigns()}
|
||||
defaultOpen={defaultOpen()}
|
||||
toolOpen={toolOpen[part().id] ?? defaultOpen()}
|
||||
onToolOpenChange={(open) => setToolOpen(part().id, open)}
|
||||
|
|
@ -1120,6 +1121,7 @@ export function MessageTimeline(props: {
|
|||
message={message()}
|
||||
parts={getMsgParts(userMessageRow().userMessageID)}
|
||||
actions={props.actions}
|
||||
useV2Actions={settings.general.newLayoutDesigns()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
onCleanup,
|
||||
Index,
|
||||
type JSX,
|
||||
type ComponentProps,
|
||||
} from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import stripAnsi from "strip-ansi"
|
||||
|
|
@ -49,6 +50,8 @@ import { getDirectory as _getDirectory, getFilename } from "@opencode-ai/core/ut
|
|||
import { checksum } from "@opencode-ai/core/util/encode"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||
import { TextShimmer } from "@opencode-ai/ui/text-shimmer"
|
||||
import { AnimatedCountList } from "./tool-count-summary"
|
||||
|
|
@ -161,6 +164,7 @@ export interface MessageProps {
|
|||
actions?: UserActions
|
||||
showAssistantCopyPartID?: string | null
|
||||
showReasoningSummaries?: boolean
|
||||
useV2Actions?: boolean
|
||||
}
|
||||
|
||||
export type SessionAction = (input: { sessionID: string; messageID: string }) => Promise<void> | void
|
||||
|
|
@ -182,6 +186,46 @@ export interface MessagePartProps {
|
|||
onContentRendered?: () => void
|
||||
showAssistantCopyPartID?: string | null
|
||||
turnDurationMs?: number
|
||||
useV2Actions?: boolean
|
||||
}
|
||||
|
||||
function MessageActionButton(
|
||||
props: Pick<ComponentProps<"button">, "disabled" | "onMouseDown" | "onClick" | "aria-label"> & {
|
||||
icon: "check" | "copy" | "reset"
|
||||
label: JSX.Element
|
||||
useV2?: boolean
|
||||
},
|
||||
) {
|
||||
return (
|
||||
<Show
|
||||
when={props.useV2}
|
||||
fallback={
|
||||
<Tooltip value={props.label} placement="top" gutter={4}>
|
||||
<IconButton
|
||||
icon={props.icon}
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
disabled={props.disabled}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
aria-label={props["aria-label"]}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<TooltipV2 value={props.label} placement="top" gutter={4}>
|
||||
<IconButtonV2
|
||||
icon={<Icon name={props.icon} size="small" />}
|
||||
size="normal"
|
||||
variant="ghost-muted"
|
||||
disabled={props.disabled}
|
||||
onMouseDown={props.onMouseDown}
|
||||
onClick={props.onClick}
|
||||
aria-label={props["aria-label"]}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
||||
export type PartComponent = Component<MessagePartProps>
|
||||
|
|
@ -640,6 +684,7 @@ export function AssistantParts(props: {
|
|||
messages: AssistantMessage[]
|
||||
showAssistantCopyPartID?: string | null
|
||||
turnDurationMs?: number
|
||||
useV2Actions?: boolean
|
||||
working?: boolean
|
||||
showReasoningSummaries?: boolean
|
||||
shellToolDefaultOpen?: boolean
|
||||
|
|
@ -724,6 +769,7 @@ export function AssistantParts(props: {
|
|||
message={message()!}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
turnDurationMs={props.turnDurationMs}
|
||||
useV2Actions={props.useV2Actions}
|
||||
defaultOpen={partDefaultOpen(item()!, props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
||||
/>
|
||||
</Show>
|
||||
|
|
@ -851,7 +897,12 @@ export function Message(props: MessageProps) {
|
|||
<Switch>
|
||||
<Match when={props.message.role === "user" && props.message}>
|
||||
{(userMessage) => (
|
||||
<UserMessageDisplay message={userMessage() as UserMessage} parts={props.parts} actions={props.actions} />
|
||||
<UserMessageDisplay
|
||||
message={userMessage() as UserMessage}
|
||||
parts={props.parts}
|
||||
actions={props.actions}
|
||||
useV2Actions={props.useV2Actions}
|
||||
/>
|
||||
)}
|
||||
</Match>
|
||||
<Match when={props.message.role === "assistant" && props.message}>
|
||||
|
|
@ -861,6 +912,7 @@ export function Message(props: MessageProps) {
|
|||
parts={props.parts}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
showReasoningSummaries={props.showReasoningSummaries}
|
||||
useV2Actions={props.useV2Actions}
|
||||
/>
|
||||
)}
|
||||
</Match>
|
||||
|
|
@ -873,6 +925,7 @@ export function AssistantMessageDisplay(props: {
|
|||
parts: PartType[]
|
||||
showAssistantCopyPartID?: string | null
|
||||
showReasoningSummaries?: boolean
|
||||
useV2Actions?: boolean
|
||||
}) {
|
||||
const emptyTools: ToolPart[] = []
|
||||
const part = createMemo(() => index(props.parts))
|
||||
|
|
@ -932,6 +985,7 @@ export function AssistantMessageDisplay(props: {
|
|||
part={item()!}
|
||||
message={props.message}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
useV2Actions={props.useV2Actions}
|
||||
/>
|
||||
</Show>
|
||||
)
|
||||
|
|
@ -1052,7 +1106,12 @@ export function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean; onS
|
|||
)
|
||||
}
|
||||
|
||||
export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[]; actions?: UserActions }) {
|
||||
export function UserMessageDisplay(props: {
|
||||
message: UserMessage
|
||||
parts: PartType[]
|
||||
actions?: UserActions
|
||||
useV2Actions?: boolean
|
||||
}) {
|
||||
const data = useData()
|
||||
const dialog = useDialog()
|
||||
const i18n = useI18n()
|
||||
|
|
@ -1191,38 +1250,30 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
|
|||
</span>
|
||||
</Show>
|
||||
<Show when={props.actions?.revert}>
|
||||
<Tooltip value={i18n.t("ui.message.revertMessage")} placement="top" gutter={4}>
|
||||
<IconButton
|
||||
icon="reset"
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
disabled={!!busy()}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
revert()
|
||||
}}
|
||||
aria-label={i18n.t("ui.message.revertMessage")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<Tooltip
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyMessage")}
|
||||
placement="top"
|
||||
gutter={4}
|
||||
>
|
||||
<IconButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
<MessageActionButton
|
||||
icon="reset"
|
||||
label={i18n.t("ui.message.revertMessage")}
|
||||
useV2={props.useV2Actions}
|
||||
disabled={!!busy()}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
void handleCopy()
|
||||
revert()
|
||||
}}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyMessage")}
|
||||
aria-label={i18n.t("ui.message.revertMessage")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Show>
|
||||
<MessageActionButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyMessage")}
|
||||
useV2={props.useV2Actions}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
void handleCopy()
|
||||
}}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyMessage")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Show>
|
||||
|
|
@ -1286,6 +1337,7 @@ export function Part(props: MessagePartProps) {
|
|||
onContentRendered={props.onContentRendered}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
turnDurationMs={props.turnDurationMs}
|
||||
useV2Actions={props.useV2Actions}
|
||||
/>
|
||||
</Show>
|
||||
)
|
||||
|
|
@ -1566,20 +1618,14 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
|
|||
</div>
|
||||
<Show when={showCopy()}>
|
||||
<div data-slot="text-part-copy-wrapper" data-interrupted={interrupted() ? "" : undefined}>
|
||||
<Tooltip
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyResponse")}
|
||||
placement="top"
|
||||
gutter={4}
|
||||
>
|
||||
<IconButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={handleCopy}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyResponse")}
|
||||
/>
|
||||
</Tooltip>
|
||||
<MessageActionButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyResponse")}
|
||||
useV2={props.useV2Actions}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={handleCopy}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copyResponse")}
|
||||
/>
|
||||
<Show when={meta()}>
|
||||
<span data-slot="text-part-meta" class="text-12-regular text-text-weak cursor-default">
|
||||
{meta()}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue