@springbrand/message-panel 0.1.3-alpha.0 → 0.1.3-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cloud-os/README.md +71 -0
- package/cloud-os/chat/cloud-os-chat-messages.tsx +606 -0
- package/cloud-os/chat/markdown-message.tsx +78 -0
- package/cloud-os/chat/rich-blocks.tsx +640 -0
- package/cloud-os/chat/tool-presentation.ts +542 -0
- package/cloud-os/chat/tool-rows.tsx +216 -0
- package/cloud-os/chat/transcript-model.ts +599 -0
- package/cloud-os/composer/cloud-os-chat-input.tsx +532 -0
- package/cloud-os/index.ts +103 -0
- package/cloud-os/internal/cn.ts +6 -0
- package/cloud-os/internal/theme-context.tsx +16 -0
- package/cloud-os/layout/cloud-os-root.tsx +34 -0
- package/cloud-os/layout/cloud-os-workspace-split.tsx +202 -0
- package/cloud-os/primitives/dropdown-menu.tsx +82 -0
- package/cloud-os/primitives/tooltip.tsx +68 -0
- package/cloud-os/primitives/workshop-controls.tsx +114 -0
- package/cloud-os/styles/cloud-os.css +559 -0
- package/cloud-os/workspace/cloud-os-workspace-panel.tsx +272 -0
- package/demo/camel-chat-showcase.tsx +13 -917
- package/demo/chat-scenarios.ts +926 -0
- package/demo/cloud-os-chat-showcase.tsx +470 -0
- package/demo/index.ts +2 -0
- package/package.json +16 -4
- package/src/camel/camel-chat-messages.tsx +50 -20
- package/src/camel/camel-turn.ts +21 -3
- package/src/message.tsx +0 -8
- package/src/parts/plan.ts +3 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { memo, useMemo, type ReactNode } from "react";
|
|
2
|
+
import ReactMarkdown, { type Components } from "react-markdown";
|
|
3
|
+
import remarkGfm from "remark-gfm";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 从 cloudflare-os-main 的 `MarkdownMessage` 拷来。
|
|
7
|
+
* 删掉的部分:capsule / format 提及的 remark 插件 —— 那是 gadgets 的资源体系,
|
|
8
|
+
* UIMessage 协议里没有对应概念,留着只会是死代码。
|
|
9
|
+
* 保留的部分:skipHtml、remark-gfm、表格外包一层横向滚动、外链白名单 + target=_blank。
|
|
10
|
+
* 加上的部分:resolveUrl —— 本仓的消息里会出现 `sandbox:/workspace/...` 这种
|
|
11
|
+
* 只有宿主知道怎么翻译成可访问 URL 的地址,原来由 camel 的同名钩子负责。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type CloudOsUrlResolver = (url: string) => string | null | undefined;
|
|
15
|
+
|
|
16
|
+
const ALLOWED_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
|
|
17
|
+
|
|
18
|
+
export function safeExternalUrl(href: string | undefined): string | undefined {
|
|
19
|
+
if (!href) return undefined;
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(href, "https://cloud-os.invalid");
|
|
22
|
+
return ALLOWED_PROTOCOLS.has(url.protocol) ? href : undefined;
|
|
23
|
+
} catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildComponents(resolveUrl?: CloudOsUrlResolver): Components {
|
|
29
|
+
const resolve = (url: string | undefined): string | undefined => {
|
|
30
|
+
if (!url) return undefined;
|
|
31
|
+
return resolveUrl?.(url) ?? url;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
table: ({ node: _node, children, ...props }) => (
|
|
35
|
+
<div style={{ maxWidth: "100%", overflowX: "auto" }}>
|
|
36
|
+
<table {...props}>{children}</table>
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
39
|
+
img: ({ node: _node, src, ...props }) => {
|
|
40
|
+
const resolved = resolve(typeof src === "string" ? src : undefined);
|
|
41
|
+
return resolved ? <img {...props} src={resolved} /> : null;
|
|
42
|
+
},
|
|
43
|
+
a: ({ node: _node, href, children, ...props }) => {
|
|
44
|
+
const safeHref = safeExternalUrl(resolve(href));
|
|
45
|
+
if (!safeHref) return <>{children}</>;
|
|
46
|
+
return (
|
|
47
|
+
<a {...props} href={safeHref} target="_blank" rel="noopener noreferrer">
|
|
48
|
+
{children}
|
|
49
|
+
</a>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const REMARK_PLUGINS = [remarkGfm];
|
|
56
|
+
const DEFAULT_COMPONENTS = buildComponents();
|
|
57
|
+
|
|
58
|
+
export const MarkdownMessage = memo(function MarkdownMessage({
|
|
59
|
+
message,
|
|
60
|
+
resolveUrl,
|
|
61
|
+
}: {
|
|
62
|
+
message: string;
|
|
63
|
+
resolveUrl?: CloudOsUrlResolver;
|
|
64
|
+
}): ReactNode {
|
|
65
|
+
const components = useMemo(
|
|
66
|
+
() => (resolveUrl ? buildComponents(resolveUrl) : DEFAULT_COMPONENTS),
|
|
67
|
+
[resolveUrl],
|
|
68
|
+
);
|
|
69
|
+
return (
|
|
70
|
+
<ReactMarkdown
|
|
71
|
+
skipHtml
|
|
72
|
+
remarkPlugins={REMARK_PLUGINS}
|
|
73
|
+
components={components}
|
|
74
|
+
>
|
|
75
|
+
{message}
|
|
76
|
+
</ReactMarkdown>
|
|
77
|
+
);
|
|
78
|
+
});
|