mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-08 21:38:40 +03:00
fix(webui): avoid preview chips for glob references
This commit is contained in:
@@ -127,6 +127,7 @@ export function isLikelyFilePath(value: string): boolean {
|
|||||||
const raw = value.trim();
|
const raw = value.trim();
|
||||||
if (!raw || raw.includes("\n")) return false;
|
if (!raw || raw.includes("\n")) return false;
|
||||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return false;
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) return false;
|
||||||
|
if (isFilePatternReference(raw)) return false;
|
||||||
if (!/[\\/]/.test(raw) && !/^(dockerfile|makefile|readme|package-lock\.json)$/i.test(raw)) {
|
if (!/[\\/]/.test(raw) && !/^(dockerfile|makefile|readme|package-lock\.json)$/i.test(raw)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -137,6 +138,10 @@ export function isLikelyFilePath(value: string): boolean {
|
|||||||
return /\.[a-z0-9][a-z0-9_-]{0,12}$/i.test(name);
|
return /\.[a-z0-9][a-z0-9_-]{0,12}$/i.test(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFilePatternReference(value: string): boolean {
|
||||||
|
return /[*?[\]{}]/.test(value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
export function splitFilePath(path: string): { directory: string; name: string } {
|
export function splitFilePath(path: string): { directory: string; name: string } {
|
||||||
const normalized = path.replace(/\\/g, "/");
|
const normalized = path.replace(/\\/g, "/");
|
||||||
const slash = normalized.lastIndexOf("/");
|
const slash = normalized.lastIndexOf("/");
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ import remarkMath from "remark-math";
|
|||||||
|
|
||||||
import { AttachmentTile } from "@/components/AttachmentTile";
|
import { AttachmentTile } from "@/components/AttachmentTile";
|
||||||
import { CodeBlock } from "@/components/CodeBlock";
|
import { CodeBlock } from "@/components/CodeBlock";
|
||||||
import { FileReferenceChip, isLikelyFilePath } from "@/components/FileReferenceChip";
|
import {
|
||||||
|
FileReferenceChip,
|
||||||
|
isFilePatternReference,
|
||||||
|
isLikelyFilePath,
|
||||||
|
} from "@/components/FileReferenceChip";
|
||||||
import { inferMediaKind } from "@/lib/media";
|
import { inferMediaKind } from "@/lib/media";
|
||||||
import { faviconUrls } from "@/lib/provider-brand";
|
import { faviconUrls } from "@/lib/provider-brand";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -216,12 +220,19 @@ function cleanFileReferenceTarget(value: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isPreviewableFileTarget(value: string): boolean {
|
function isPreviewableFileTarget(value: string): boolean {
|
||||||
|
if (isFilePatternReference(value)) return false;
|
||||||
if (isLikelyFilePath(value)) return true;
|
if (isLikelyFilePath(value)) return true;
|
||||||
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) return false;
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) return false;
|
||||||
if (/[\\/]/.test(value)) return false;
|
if (/[\\/]/.test(value)) return false;
|
||||||
return /^[^?#]+\.[a-z0-9][a-z0-9_-]{0,12}$/i.test(value);
|
return /^[^?#]+\.[a-z0-9][a-z0-9_-]{0,12}$/i.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNonNavigableFilePatternLink(href: string | undefined): boolean {
|
||||||
|
if (!href || /^https?:\/\//i.test(href) || href.startsWith("#")) return false;
|
||||||
|
const target = cleanFileReferenceTarget(href);
|
||||||
|
return Boolean(target && isFilePatternReference(target));
|
||||||
|
}
|
||||||
|
|
||||||
function fileReferenceFromLink(href: string | undefined): string | null {
|
function fileReferenceFromLink(href: string | undefined): string | null {
|
||||||
if (!href || /^https?:\/\//i.test(href) || href.startsWith("#")) return null;
|
if (!href || /^https?:\/\//i.test(href) || href.startsWith("#")) return null;
|
||||||
const target = cleanFileReferenceTarget(href);
|
const target = cleanFileReferenceTarget(href);
|
||||||
@@ -469,6 +480,9 @@ export default function MarkdownTextRenderer({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (isNonNavigableFilePatternLink(href)) {
|
||||||
|
return <>{markdownChildren}</>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
|
|||||||
@@ -49,6 +49,30 @@ describe("MarkdownTextRenderer", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders glob file links as plain text instead of preview targets", () => {
|
||||||
|
const onOpenFilePreview = vi.fn();
|
||||||
|
const { container } = render(
|
||||||
|
<MarkdownTextRenderer onOpenFilePreview={onOpenFilePreview}>
|
||||||
|
{"原始对话通常还在 [*.json](*.json)。"}
|
||||||
|
</MarkdownTextRenderer>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId("inline-file-path")).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("link", { name: "*.json" })).not.toBeInTheDocument();
|
||||||
|
expect(container).toHaveTextContent("*.json");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps glob inline code as code instead of a file preview chip", () => {
|
||||||
|
render(
|
||||||
|
<MarkdownTextRenderer>
|
||||||
|
{"检查 `src/**/*.json`。"}
|
||||||
|
</MarkdownTextRenderer>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId("inline-file-path")).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByText("src/**/*.json").tagName).toBe("CODE");
|
||||||
|
});
|
||||||
|
|
||||||
it("does not wrap complete fenced code blocks in an extra pre", () => {
|
it("does not wrap complete fenced code blocks in an extra pre", () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<MarkdownTextRenderer highlightCode={false}>
|
<MarkdownTextRenderer highlightCode={false}>
|
||||||
|
|||||||
Reference in New Issue
Block a user