tweak(ui): show assistant response meta on hover
Adds hover-only metadata after the assistant copy icon showing agent, provider, model, and response duration.
This commit is contained in:
@@ -168,6 +168,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.15s ease;
|
||||
@@ -179,6 +180,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="text-part-meta"] {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="text-part-copy-wrapper"][data-interrupted] {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -902,6 +902,47 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
() =>
|
||||
props.message.role === "assistant" && (props.message as AssistantMessage).error?.name === "MessageAbortedError",
|
||||
)
|
||||
|
||||
const provider = createMemo(() => {
|
||||
if (props.message.role !== "assistant") return ""
|
||||
const id = (props.message as AssistantMessage).providerID
|
||||
const match = data.store.provider?.all?.find((p) => p.id === id)
|
||||
return match?.name ?? id
|
||||
})
|
||||
|
||||
const model = createMemo(() => {
|
||||
if (props.message.role !== "assistant") return ""
|
||||
const message = props.message as AssistantMessage
|
||||
const match = data.store.provider?.all?.find((p) => p.id === message.providerID)
|
||||
return match?.models?.[message.modelID]?.name ?? message.modelID
|
||||
})
|
||||
|
||||
const duration = createMemo(() => {
|
||||
if (props.message.role !== "assistant") return ""
|
||||
const message = props.message as AssistantMessage
|
||||
const completed = message.time.completed
|
||||
if (typeof completed !== "number") return ""
|
||||
const ms = completed - message.time.created
|
||||
if (!(ms >= 0)) return ""
|
||||
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`
|
||||
const minutes = Math.floor(ms / 60_000)
|
||||
const seconds = Math.round((ms - minutes * 60_000) / 1000)
|
||||
return `${minutes}m ${seconds}s`
|
||||
})
|
||||
|
||||
const meta = createMemo(() => {
|
||||
if (props.message.role !== "assistant") return ""
|
||||
const agent = (props.message as AssistantMessage).agent
|
||||
const items = [
|
||||
agent ? agent[0]?.toUpperCase() + agent.slice(1) : "",
|
||||
provider(),
|
||||
model(),
|
||||
duration(),
|
||||
interrupted() ? i18n.t("ui.message.interrupted") : "",
|
||||
]
|
||||
return items.filter((x) => !!x).join(" \u00B7 ")
|
||||
})
|
||||
|
||||
const displayText = () => relativizeProjectPaths((part.text ?? "").trim(), data.directory)
|
||||
const throttledText = createThrottledValue(displayText)
|
||||
const isLastTextPart = createMemo(() => {
|
||||
@@ -934,11 +975,6 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
</div>
|
||||
<Show when={showCopy()}>
|
||||
<div data-slot="text-part-copy-wrapper" data-interrupted={interrupted() ? "" : undefined}>
|
||||
<Show when={interrupted()}>
|
||||
<span data-slot="text-part-interrupted" class="text-13-regular text-text-weak cursor-default">
|
||||
{i18n.t("ui.message.interrupted")}
|
||||
</span>
|
||||
</Show>
|
||||
<Tooltip
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
|
||||
placement="top"
|
||||
@@ -953,6 +989,11 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.message.copy")}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Show when={meta()}>
|
||||
<span data-slot="text-part-meta" class="text-12-regular text-text-weak cursor-default">
|
||||
{meta()}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user