enhance assistant batch responses
This commit is contained in:
1 parent
edf3f6cd84
commit
8d06201b41
10 files changed
+207
-38
No files matched your search
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { getSafeAssistantMarkdownImageSource } from "./assistant-markdown-image";
|
||||
|
||||
describe("getSafeAssistantMarkdownImageSource", () => {
|
||||
test("allows Homarr-relative and ordinary remote images", () => {
|
||||
expect(getSafeAssistantMarkdownImageSource("/logo/logo.png")).toBe("/logo/logo.png");
|
||||
expect(getSafeAssistantMarkdownImageSource("https://cdn.example.com/icons/discord.svg")).toBe(
|
||||
"https://cdn.example.com/icons/discord.svg",
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects unsafe protocols, protocol-relative URLs, and embedded credentials", () => {
|
||||
expect(getSafeAssistantMarkdownImageSource("javascript:alert(1)")).toBeNull();
|
||||
expect(getSafeAssistantMarkdownImageSource("data:image/svg+xml;base64,PHN2Zy8+")).toBeNull();
|
||||
expect(getSafeAssistantMarkdownImageSource("//example.com/icon.svg")).toBeNull();
|
||||
expect(getSafeAssistantMarkdownImageSource("https://user:secret@example.com/icon.svg")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
export const getSafeAssistantMarkdownImageSource = (source: string | Blob | undefined) => {
|
||||
if (typeof source !== "string" || !source) return null;
|
||||
if (source.startsWith("/") && !source.startsWith("//")) return source;
|
||||
|
||||
try {
|
||||
const url = new URL(source);
|
||||
if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username || url.password) return null;
|
||||
return url.toString();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -466,11 +466,13 @@
|
||||
border-top: 1px solid var(--mantine-color-default-border);
|
||||
}
|
||||
|
||||
.messageMarkdown img {
|
||||
.markdownImage {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 24rem;
|
||||
height: auto;
|
||||
margin-block: 0.65rem;
|
||||
object-fit: contain;
|
||||
border-radius: var(--mantine-radius-md);
|
||||
}
|
||||
|
||||
@@ -511,6 +513,13 @@
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.markdownTableWrap .markdownImage {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
margin: 0 auto;
|
||||
border-radius: var(--mantine-radius-sm);
|
||||
}
|
||||
|
||||
.messageQuote {
|
||||
margin: 0 0 0.55rem;
|
||||
padding-inline-start: 0.65rem;
|
||||
|
||||
@@ -104,6 +104,7 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
import classes from "./assistant-panel.module.css";
|
||||
import { useAssistantAutoApproval, useAssistantAutomaticAction } from "./assistant-auto-approval";
|
||||
import { normalizeAssistantMarkdown } from "./assistant-markdown";
|
||||
import { getSafeAssistantMarkdownImageSource } from "./assistant-markdown-image";
|
||||
import { getAssistantTelemetry, getAssistantUsage } from "./assistant-message-metadata";
|
||||
import type { AssistantPendingAction } from "./assistant-pending-action";
|
||||
import { AssistantQuestionPortalProvider } from "./assistant-question-portal";
|
||||
@@ -153,12 +154,29 @@ const MarkdownTable = ({ children, ...props }: ComponentPropsWithoutRef<"table">
|
||||
</div>
|
||||
);
|
||||
|
||||
const MarkdownImage = ({ src, alt = "", ...props }: ComponentPropsWithoutRef<"img">) => {
|
||||
const safeSource = getSafeAssistantMarkdownImageSource(src);
|
||||
if (!safeSource) return null;
|
||||
|
||||
return (
|
||||
<img
|
||||
{...props}
|
||||
src={safeSource}
|
||||
alt={alt}
|
||||
className={classes.markdownImage}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AssistantTextPart = () => (
|
||||
<MarkdownTextPrimitive
|
||||
className={classes.messageMarkdown}
|
||||
preprocess={normalizeAssistantMarkdown}
|
||||
remarkPlugins={markdownRemarkPlugins}
|
||||
components={{ a: MarkdownLink, table: MarkdownTable }}
|
||||
components={{ a: MarkdownLink, img: MarkdownImage, table: MarkdownTable }}
|
||||
defer
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user