Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

44 lines
1.8 KiB
TypeScript

import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
import remarkGfm from "remark-gfm";
// react-markdown's default transform only keeps http(s)/irc(s)/mailto/xmpp
// URLs, which would strip the inline `data:image/...` sources the markdown
// serializer produces for attachments. Allow exactly those data images and
// defer everything else to the default sanitization. Raw HTML embedded in
// message text stays inert because react-markdown ignores html nodes.
//
// The allow-list is raster subtypes only, not a bare `data:image/` prefix:
// an image block's mimeType comes from unvalidated message content (tool
// output, a collaborator's message), so a `data:image/svg+xml` source could
// otherwise reach the text/html clipboard flavor verbatim — SVG is an XML
// document that can carry <script> and event handlers, unlike a raster
// image.
const SAFE_DATA_IMAGE_URL_PATTERN =
/^data:image\/(?:png|jpe?g|gif|webp|bmp|avif|x-icon|vnd\.microsoft\.icon);/i;
function conversationCopyUrlTransform(url: string): string {
return SAFE_DATA_IMAGE_URL_PATTERN.test(url) ? url : defaultUrlTransform(url);
}
/**
* Renders the clipboard markdown transcript to an HTML fragment for the
* `text/html` clipboard flavor. Rich-paste targets (Word, Feishu/Lark docs,
* Notion, mail clients) consume this flavor, so inline data-URI images paste
* as real images instead of a base64 wall of text; `text/plain` keeps the
* markdown source for plain editors.
*/
export function renderAgentConversationCopyHtml(markdown: string): string {
return renderToStaticMarkup(
createElement(
ReactMarkdown,
{
remarkPlugins: [remarkGfm],
urlTransform: conversationCopyUrlTransform
},
markdown
)
);
}