mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 08:02:30 +00:00
fix(webui): use portal file reference tooltips
This commit is contained in:
parent
945f208d38
commit
361f31c0e4
@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
|
||||
import { CodeBlock } from "@/components/CodeBlock";
|
||||
import { FileReferenceChip, isLikelyFilePath } from "@/components/FileReferenceChip";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import "katex/dist/katex.min.css";
|
||||
@ -44,6 +45,9 @@ export default function MarkdownTextRenderer({
|
||||
);
|
||||
}
|
||||
const raw = String(kids).replace(/\n$/, "");
|
||||
if (isLikelyFilePath(raw)) {
|
||||
return <FileReferenceChip path={raw} />;
|
||||
}
|
||||
/** Plain fenced ``` blocks (no language) & wide one-liners: block monospace, not inline pill. */
|
||||
const widePlainBlock = raw.includes("\n") || raw.length > 120;
|
||||
if (widePlainBlock) {
|
||||
|
||||
@ -11,15 +11,17 @@ const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { MessageBubble } from "@/components/MessageBubble";
|
||||
@ -179,6 +179,47 @@ describe("MessageBubble", () => {
|
||||
expect(screen.getByText("Body line.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders inline file paths as compact file references", async () => {
|
||||
await import("@/components/MarkdownTextRenderer");
|
||||
const message: UIMessage = {
|
||||
id: "a-file-path",
|
||||
role: "assistant",
|
||||
content:
|
||||
"改动在 `webui/src/components/MarkdownTextRenderer.tsx` 和 `/Users/renxubin/.nanobot/workspace/minecraft-fps/index.html`。",
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
render(<MessageBubble message={message} />);
|
||||
|
||||
const references = await screen.findAllByTestId("inline-file-path");
|
||||
expect(references).toHaveLength(2);
|
||||
expect(references[0].parentElement).not.toHaveClass("translate-y-[0.08em]");
|
||||
expect(references[0].parentElement).toHaveClass("align-[0.14em]");
|
||||
expect(references[0]).toHaveTextContent("MarkdownTextRenderer.tsx");
|
||||
expect(references[0]).not.toHaveTextContent("webui/src/components");
|
||||
expect(screen.getByText("index.html")).toBeInTheDocument();
|
||||
expect(references[1]).not.toHaveTextContent("/Users/renxubin");
|
||||
expect(references[1]).not.toHaveAttribute("title");
|
||||
expect(references[1]).toHaveAttribute(
|
||||
"aria-label",
|
||||
"/Users/renxubin/.nanobot/workspace/minecraft-fps/index.html",
|
||||
);
|
||||
|
||||
vi.useFakeTimers();
|
||||
fireEvent.pointerMove(references[1].parentElement!);
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(500);
|
||||
});
|
||||
const tooltip = screen.getByRole("tooltip");
|
||||
expect(tooltip).toHaveTextContent(
|
||||
"/Users/renxubin/.nanobot/workspace/minecraft-fps/index.html",
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("renders assistant image media as a larger generated result", () => {
|
||||
const message: UIMessage = {
|
||||
id: "a-image",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user