mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12: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 remarkMath from "remark-math";
|
||||||
|
|
||||||
import { CodeBlock } from "@/components/CodeBlock";
|
import { CodeBlock } from "@/components/CodeBlock";
|
||||||
|
import { FileReferenceChip, isLikelyFilePath } from "@/components/FileReferenceChip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
import "katex/dist/katex.min.css";
|
import "katex/dist/katex.min.css";
|
||||||
@ -44,6 +45,9 @@ export default function MarkdownTextRenderer({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const raw = String(kids).replace(/\n$/, "");
|
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. */
|
/** Plain fenced ``` blocks (no language) & wide one-liners: block monospace, not inline pill. */
|
||||||
const widePlainBlock = raw.includes("\n") || raw.length > 120;
|
const widePlainBlock = raw.includes("\n") || raw.length > 120;
|
||||||
if (widePlainBlock) {
|
if (widePlainBlock) {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ const TooltipContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
<TooltipPrimitive.Portal>
|
||||||
<TooltipPrimitive.Content
|
<TooltipPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
@ -20,6 +21,7 @@ const TooltipContent = React.forwardRef<
|
|||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
</TooltipPrimitive.Portal>
|
||||||
));
|
));
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
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 { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { MessageBubble } from "@/components/MessageBubble";
|
import { MessageBubble } from "@/components/MessageBubble";
|
||||||
@ -179,6 +179,47 @@ describe("MessageBubble", () => {
|
|||||||
expect(screen.getByText("Body line.")).toBeInTheDocument();
|
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", () => {
|
it("renders assistant image media as a larger generated result", () => {
|
||||||
const message: UIMessage = {
|
const message: UIMessage = {
|
||||||
id: "a-image",
|
id: "a-image",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user