feat(llm): display message time

This commit is contained in:
Elian Doran
2026-03-30 18:08:20 +03:00
parent a860803cc4
commit 5e9fc614d7
4 changed files with 53 additions and 31 deletions

View File

@@ -922,6 +922,7 @@ export default {
parseDate,
formatDateISO,
formatDateTime,
formatTime,
formatTimeInterval,
formatSize,
localNowDateTime,

View File

@@ -36,8 +36,8 @@
font-size: 0.9rem;
}
/* Usage footer: pull it closer to the message above by collapsing the flex gap */
.sidebar-chat-messages .llm-chat-usage {
/* Footer: pull it closer to the message above by collapsing the flex gap */
.sidebar-chat-messages .llm-chat-footer {
margin-top: -0.35rem;
margin-bottom: 0;
}

View File

@@ -5,6 +5,7 @@ import { marked } from "marked";
import { useMemo } from "preact/hooks";
import { t } from "../../../services/i18n.js";
import utils from "../../../services/utils.js";
import type { ContentBlock, StoredMessage, ToolCall } from "./llm_chat_types.js";
import { getMessageText, getMessageToolCalls } from "./llm_chat_types.js";
@@ -205,30 +206,41 @@ export default function ChatMessage({ message, isStreaming }: Props) {
</div>
)}
</div>
{message.usage && typeof message.usage.promptTokens === "number" && (
<div className="llm-chat-usage">
{message.usage.model && (
<span className="llm-chat-usage-model">{message.usage.model}</span>
)}
<span className="llm-chat-usage-separator">·</span>
<span
className="llm-chat-usage-tokens"
title={t("llm_chat.tokens_detail", {
prompt: message.usage.promptTokens.toLocaleString(),
completion: message.usage.completionTokens.toLocaleString()
})}
>
<span className="bx bx-chip" />{" "}
{t("llm_chat.total_tokens", { total: shortenNumber(message.usage.totalTokens) })}
</span>
{message.usage.cost != null && (
<>
<span className="llm-chat-usage-separator">·</span>
<span className="llm-chat-usage-cost">~${message.usage.cost.toFixed(4)}</span>
</>
)}
</div>
)}
<div className={`llm-chat-footer llm-chat-footer-${message.role}`}>
<span
className="llm-chat-footer-time"
title={utils.formatDateTime(new Date(message.createdAt))}
>
{utils.formatTime(new Date(message.createdAt))}
</span>
{message.usage && typeof message.usage.promptTokens === "number" && (
<>
{message.usage.model && (
<>
<span className="llm-chat-usage-separator">·</span>
<span className="llm-chat-usage-model">{message.usage.model}</span>
</>
)}
<span className="llm-chat-usage-separator">·</span>
<span
className="llm-chat-usage-tokens"
title={t("llm_chat.tokens_detail", {
prompt: message.usage.promptTokens.toLocaleString(),
completion: message.usage.completionTokens.toLocaleString()
})}
>
<span className="bx bx-chip" />{" "}
{t("llm_chat.total_tokens", { total: shortenNumber(message.usage.totalTokens) })}
</span>
{message.usage.cost != null && (
<>
<span className="llm-chat-usage-separator">·</span>
<span className="llm-chat-usage-cost">~${message.usage.cost.toFixed(4)}</span>
</>
)}
</>
)}
</div>
</>
);
}

View File

@@ -24,8 +24,8 @@
margin-top: 0;
}
/* Collapse gap between the usage footer and the next message */
.llm-chat-usage + .llm-chat-message {
/* Collapse gap between the footer and the next message */
.llm-chat-footer + .llm-chat-message {
margin-top: 0.5rem;
}
@@ -618,8 +618,8 @@
color: var(--danger-color, #dc3545);
}
/* Token usage display (sits below the message bubble) */
.llm-chat-usage {
/* Message footer (timestamp + token usage, sits below the bubble) */
.llm-chat-footer {
display: flex;
align-items: center;
gap: 0.375rem;
@@ -630,10 +630,18 @@
cursor: default;
}
.llm-chat-usage .bx {
.llm-chat-footer-user {
justify-content: flex-end;
}
.llm-chat-footer .bx {
font-size: 0.875rem;
}
.llm-chat-footer-time {
cursor: help;
}
.llm-chat-usage-model {
font-weight: 500;
}
@@ -643,6 +651,7 @@
}
.llm-chat-usage-tokens {
cursor: help;
font-family: var(--monospace-font-family, monospace);
}