mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-14 06:43:53 +00:00
fix(webui): polish links and thought timing
This commit is contained in:
parent
d5692bf94c
commit
e8d4aff5be
@ -272,7 +272,7 @@ function InlineLinkPreviewRow({ link }: { link: InlineLinkPreview }) {
|
||||
aria-label={`Open link: ${label}`}
|
||||
className={cn(
|
||||
"not-prose inline-flex max-w-full items-center gap-2 align-baseline",
|
||||
"text-primary no-underline underline-offset-2 hover:underline",
|
||||
"text-blue-600 no-underline underline-offset-2 hover:underline dark:text-blue-300",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
@ -410,7 +410,7 @@ export default function MarkdownTextRenderer({
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="text-primary underline underline-offset-2 hover:opacity-80"
|
||||
className="text-blue-600 underline underline-offset-2 hover:text-blue-700 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
{...props}
|
||||
>
|
||||
{markdownChildren}
|
||||
@ -508,7 +508,7 @@ export default function MarkdownTextRenderer({
|
||||
"prose-ul:my-2 prose-ol:my-2 prose-li:my-0.5",
|
||||
"prose-blockquote:my-3 prose-blockquote:border-l-2 prose-blockquote:font-normal",
|
||||
"prose-blockquote:not-italic prose-blockquote:text-foreground/80",
|
||||
"prose-a:text-primary prose-a:underline-offset-2 hover:prose-a:opacity-80",
|
||||
"prose-a:text-blue-600 prose-a:underline-offset-2 hover:prose-a:text-blue-700 dark:prose-a:text-blue-300 dark:hover:prose-a:text-blue-200",
|
||||
"prose-hr:my-6",
|
||||
"prose-pre:my-0 prose-pre:bg-transparent prose-pre:p-0",
|
||||
"prose-code:before:content-none prose-code:after:content-none prose-code:font-normal",
|
||||
|
||||
@ -573,7 +573,7 @@ export function ReasoningBubble({
|
||||
"prose-headings:mt-2 prose-headings:mb-1 prose-headings:font-medium",
|
||||
"prose-headings:text-muted-foreground/92 prose-strong:text-muted-foreground",
|
||||
"prose-h1:text-[15px] prose-h2:text-[13.5px] prose-h3:text-[12.5px] prose-h4:text-[12px]",
|
||||
"prose-a:text-muted-foreground/95 prose-a:underline hover:prose-a:opacity-90",
|
||||
"prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 dark:prose-a:text-blue-300 dark:hover:prose-a:text-blue-200",
|
||||
"prose-code:text-[0.92em]",
|
||||
)}
|
||||
>
|
||||
|
||||
@ -36,7 +36,7 @@ export function ReasoningRow({
|
||||
"prose-headings:mt-2 prose-headings:mb-1 prose-headings:font-medium",
|
||||
"prose-headings:text-muted-foreground/88 prose-strong:text-muted-foreground",
|
||||
"prose-h1:text-[15px] prose-h2:text-[13.5px] prose-h3:text-[12.5px] prose-h4:text-[12px]",
|
||||
"prose-a:text-muted-foreground/95 prose-a:underline hover:prose-a:opacity-90",
|
||||
"prose-a:text-blue-600 prose-a:underline hover:prose-a:text-blue-700 dark:prose-a:text-blue-300 dark:hover:prose-a:text-blue-200",
|
||||
"prose-code:text-[0.92em]",
|
||||
)}
|
||||
>
|
||||
|
||||
@ -145,7 +145,17 @@ function closeReasoningStream(prev: UIMessage[]): UIMessage[] {
|
||||
for (let i = prev.length - 1; i >= 0; i -= 1) {
|
||||
const candidate = prev[i];
|
||||
if (!candidate.reasoningStreaming) continue;
|
||||
const merged: UIMessage = { ...candidate, reasoningStreaming: false };
|
||||
const latencyMs =
|
||||
candidate.latencyMs === undefined
|
||||
&& Number.isFinite(candidate.createdAt)
|
||||
&& candidate.createdAt > 1_000_000_000_000
|
||||
? Math.max(0, Math.round(Date.now() - candidate.createdAt))
|
||||
: candidate.latencyMs;
|
||||
const merged: UIMessage = {
|
||||
...candidate,
|
||||
reasoningStreaming: false,
|
||||
...(latencyMs !== undefined ? { latencyMs } : {}),
|
||||
};
|
||||
return [...prev.slice(0, i), merged, ...prev.slice(i + 1)];
|
||||
}
|
||||
return prev;
|
||||
|
||||
@ -4,6 +4,14 @@ import { describe, expect, it } from "vitest";
|
||||
import MarkdownTextRenderer from "@/components/MarkdownTextRenderer";
|
||||
|
||||
describe("MarkdownTextRenderer", () => {
|
||||
it("renders clickable markdown links in blue", () => {
|
||||
render(<MarkdownTextRenderer>[local server](http://127.0.0.1:7891/)</MarkdownTextRenderer>);
|
||||
|
||||
const link = screen.getByRole("link", { name: "local server" });
|
||||
expect(link).toHaveAttribute("href", "http://127.0.0.1:7891/");
|
||||
expect(link).toHaveClass("text-blue-600", "dark:text-blue-300");
|
||||
});
|
||||
|
||||
it("does not wrap complete fenced code blocks in an extra pre", () => {
|
||||
const { container } = render(
|
||||
<MarkdownTextRenderer highlightCode={false}>
|
||||
|
||||
@ -1039,6 +1039,41 @@ describe("useNanobotStream", () => {
|
||||
expect(result.current.messages[1].reasoningStreaming).toBe(false);
|
||||
});
|
||||
|
||||
it("stamps completed live Thought blocks with their own latency", async () => {
|
||||
const dateNow = vi.spyOn(Date, "now");
|
||||
let now = Date.UTC(2026, 5, 1, 0, 0, 0);
|
||||
dateNow.mockImplementation(() => now);
|
||||
try {
|
||||
const fake = fakeClient();
|
||||
const { result } = renderHook(() => useNanobotStream("chat-r5-lat", EMPTY_MESSAGES), {
|
||||
wrapper: wrap(fake.client),
|
||||
});
|
||||
await act(async () => {});
|
||||
|
||||
act(() => {
|
||||
fake.emit("chat-r5-lat", {
|
||||
event: "reasoning_delta",
|
||||
chat_id: "chat-r5-lat",
|
||||
text: "Thinking through the tests.",
|
||||
});
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise<void>((resolve) => window.requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
|
||||
expect(result.current.messages[0].createdAt).toBe(now);
|
||||
now += 2100;
|
||||
act(() => {
|
||||
fake.emit("chat-r5-lat", { event: "reasoning_end", chat_id: "chat-r5-lat" });
|
||||
});
|
||||
|
||||
expect(result.current.messages[0].reasoningStreaming).toBe(false);
|
||||
expect(result.current.messages[0].latencyMs).toBe(2100);
|
||||
} finally {
|
||||
dateNow.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps alternating reasoning and answer deltas in separate ordered blocks", async () => {
|
||||
const fake = fakeClient();
|
||||
const { result } = renderHook(() => useNanobotStream("chat-r5b", EMPTY_MESSAGES), {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user