@aura-react-comp/x 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +80 -2
- package/esm/actions/index.d.ts +28 -0
- package/esm/actions/index.js +37 -0
- package/esm/actions/index.less +59 -0
- package/esm/attachments/index.d.ts +41 -0
- package/esm/attachments/index.js +48 -0
- package/esm/attachments/index.less +22 -0
- package/esm/bubble/index.d.ts +42 -0
- package/esm/bubble/index.js +80 -0
- package/esm/bubble/index.less +164 -0
- package/esm/code-highlighter/index.d.ts +39 -0
- package/esm/code-highlighter/index.js +165 -0
- package/esm/code-highlighter/index.less +76 -0
- package/esm/code-highlighter/prism-theme.d.ts +13 -0
- package/esm/code-highlighter/prism-theme.js +75 -0
- package/esm/conversations/index.d.ts +35 -0
- package/esm/conversations/index.js +89 -0
- package/esm/conversations/index.less +138 -0
- package/esm/file-card/index.d.ts +40 -0
- package/esm/file-card/index.js +126 -0
- package/esm/file-card/index.less +161 -0
- package/esm/folder/index.d.ts +66 -0
- package/esm/folder/index.js +345 -0
- package/esm/folder/index.less +158 -0
- package/esm/folder/tree-utils.d.ts +30 -0
- package/esm/folder/tree-utils.js +147 -0
- package/esm/folder/types.d.ts +51 -0
- package/esm/folder/types.js +1 -0
- package/esm/index.d.ts +40 -0
- package/esm/index.js +20 -0
- package/esm/markdown-content/index.d.ts +34 -0
- package/esm/markdown-content/index.js +117 -0
- package/esm/markdown-content/index.less +124 -0
- package/esm/mermaid/index.d.ts +47 -0
- package/esm/mermaid/index.js +235 -0
- package/esm/mermaid/index.less +155 -0
- package/esm/notification/index.d.ts +59 -0
- package/esm/notification/index.js +148 -0
- package/esm/prompts/index.d.ts +27 -0
- package/esm/prompts/index.js +50 -0
- package/esm/prompts/index.less +95 -0
- package/esm/sender/index.d.ts +48 -0
- package/esm/sender/index.js +88 -0
- package/esm/sender/index.less +75 -0
- package/esm/sources/index.d.ts +40 -0
- package/esm/sources/index.js +154 -0
- package/esm/sources/index.less +219 -0
- package/esm/style.css +1565 -0
- package/esm/suggestion/index.d.ts +21 -0
- package/esm/suggestion/index.js +36 -0
- package/esm/suggestion/index.less +44 -0
- package/esm/think/index.d.ts +20 -0
- package/esm/think/index.js +48 -0
- package/esm/think/index.less +92 -0
- package/esm/thought-chain/index.d.ts +34 -0
- package/esm/thought-chain/index.js +105 -0
- package/esm/thought-chain/index.less +174 -0
- package/esm/use-x-chat/index.d.ts +85 -0
- package/esm/use-x-chat/index.js +178 -0
- package/esm/use-x-stream/index.d.ts +43 -0
- package/esm/use-x-stream/index.js +112 -0
- package/esm/use-x-stream/parse-sse.d.ts +19 -0
- package/esm/use-x-stream/parse-sse.js +91 -0
- package/esm/utils/color.d.ts +9 -0
- package/esm/utils/color.js +45 -0
- package/esm/welcome/index.d.ts +22 -0
- package/esm/welcome/index.js +36 -0
- package/esm/welcome/index.less +57 -0
- package/esm/x-provider/index.d.ts +48 -0
- package/esm/x-provider/index.js +66 -0
- package/package.json +70 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Aura Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @aura-react-comp/x
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Aura AI 组件库:对话式 AI 界面的原子组件与数据流 Hooks,对标 Ant Design X 的 Aura 实现。
|
|
4
|
+
|
|
5
|
+
覆盖从「输入 → 流式接收 → 渲染 → 推理过程 → 消息操作 → 会话管理」的完整链路,
|
|
6
|
+
并以 `XProvider` 与 Aura 生态共享同一套紫罗兰视觉语言。
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @aura-react-comp/x antd react react-dom
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`antd`(>=6)、`react` / `react-dom`(>=18)为 peerDependencies,需由使用方提供。
|
|
15
|
+
`mermaid`(>=10)为**可选** peerDependency,仅在使用 `Mermaid` 组件时才需要安装。
|
|
16
|
+
|
|
17
|
+
## 样式引入(重要)
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import '@aura-react-comp/ui/style.css'; // Aura 基础设计令牌(--aura-*)
|
|
21
|
+
import '@aura-react-comp/x/style.css'; // AI 组件样式
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
本包的 `.less` 直接使用 `var(--aura-*)` 且**不设 fallback**,基础令牌未加载时
|
|
25
|
+
颜色、圆角、字号、间距会整体失效。基础令牌由 `@aura-react-comp/ui` 提供
|
|
26
|
+
(已随本包依赖自动安装),因此需引入它的 `style.css` 一次。
|
|
27
|
+
|
|
28
|
+
品牌相关变量(渐变 / 光晕 / 软底色,`--aura-x-*`)由 `XProvider` 在运行期
|
|
29
|
+
从 `colorPrimary` 实时派生并注入子树作用域——**换主色即时全量生效**,无需重新构建样式。
|
|
30
|
+
|
|
31
|
+
## 使用
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { XProvider, Bubble, Sender, useXChat } from '@aura-react-comp/x';
|
|
35
|
+
import '@aura-react-comp/ui/style.css';
|
|
36
|
+
import '@aura-react-comp/x/style.css';
|
|
37
|
+
|
|
38
|
+
const App = () => {
|
|
39
|
+
const { messages, onRequest } = useXChat({ request: callModel });
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<XProvider>
|
|
43
|
+
<Bubble.List items={messages} />
|
|
44
|
+
<Sender onSubmit={({ message }) => onRequest({ message })} />
|
|
45
|
+
</XProvider>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
建议在应用根节点包裹 `XProvider`,使 antd 主题与 Aura 品牌色(默认主色 `#7c3aed`)保持一致。
|
|
51
|
+
|
|
52
|
+
## 组件总览(20 个导出)
|
|
53
|
+
|
|
54
|
+
| 分组 | 组件 | 说明 |
|
|
55
|
+
| -------- | ----------------------------------- | ------------------------------------------------------------------ |
|
|
56
|
+
| 主题桥接 | `XProvider` | 将 Aura 设计令牌映射到 antd 主题系统,支持暗色 / 紧凑 / 自定义主色 |
|
|
57
|
+
| 交互 | `Bubble` / `Bubble.List` | 消息气泡,支持头像、加载态、变体与列表分组 |
|
|
58
|
+
| 交互 | `Sender` | 对话输入框,支持提交类型、加载态与自定义操作区 |
|
|
59
|
+
| 交互 | `Attachments` | 附件条,支持上传、预览与状态展示 |
|
|
60
|
+
| 交互 | `FileCard` | 文件卡片,内置状态与文件大小 / 扩展名格式化 |
|
|
61
|
+
| 交互 | `Folder` | 文件树,支持预览、右键菜单与自定义目录图标 |
|
|
62
|
+
| 引导 | `Welcome` | 欢迎语,用于对话开始的引导区 |
|
|
63
|
+
| 引导 | `Prompts` | 提示集,支持横向 / 纵向排布 |
|
|
64
|
+
| 引导 | `Suggestion` | 建议项,用于输入框上方的快捷提问 |
|
|
65
|
+
| 推理 | `Think` | 单段思考过程展示 |
|
|
66
|
+
| 推理 | `ThoughtChain` | 多步思维链,支持状态与嵌套内容 |
|
|
67
|
+
| 反馈 | `Actions` | 消息操作组(复制 / 重试 / 点赞等) |
|
|
68
|
+
| 反馈 | `MarkdownContent` | Markdown 安全渲染,围栏代码块默认接入语法高亮 |
|
|
69
|
+
| 反馈 | `Sources` | 来源引用与跳转 |
|
|
70
|
+
| 反馈 | `CodeHighlighter` | 代码高亮,配色走 `var(--aura-*)` 以自动跟随亮暗主题 |
|
|
71
|
+
| 反馈 | `Mermaid` | Mermaid 图表渲染(`mermaid` 为可选依赖,按需动态加载) |
|
|
72
|
+
| 会话 | `Conversations` | 会话列表管理,支持分组与菜单 |
|
|
73
|
+
| 数据 | `useXStream` | SSE / 流式响应解析 |
|
|
74
|
+
| 数据 | `useXChat` | 对话数据流管理,支持多会话与请求中止 |
|
|
75
|
+
| 数据 | `XNotification` / `useNotification` | 系统级通知与权限管控 |
|
|
76
|
+
|
|
77
|
+
完整 API 与在线示例见文档站「AI 组件」。
|
|
78
|
+
|
|
79
|
+
## 许可证
|
|
80
|
+
|
|
81
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ActionItem {
|
|
3
|
+
key: React.Key;
|
|
4
|
+
icon?: React.ReactNode;
|
|
5
|
+
label?: React.ReactNode;
|
|
6
|
+
/** 危险操作(红色态) */
|
|
7
|
+
danger?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
/** 切换态(如点赞 / 收藏) */
|
|
10
|
+
active?: boolean;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface ActionsProps {
|
|
14
|
+
items: ActionItem[];
|
|
15
|
+
direction?: 'horizontal' | 'vertical';
|
|
16
|
+
/** 可访问名称(role=toolbar 时读屏依赖它) */
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Actions — 消息操作组。
|
|
23
|
+
*
|
|
24
|
+
* 常放在 [`Bubble`](/x-components/bubble) 的 `footer`:复制、重新生成、点赞、
|
|
25
|
+
* 切换收藏等。语义为工具栏(role=toolbar),按钮从 `label` / `icon`
|
|
26
|
+
* 生成可访问名称。
|
|
27
|
+
*/
|
|
28
|
+
export declare const Actions: React.FC<ActionsProps>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { classNames, prefixCls } from '@aura-react-comp/shared';
|
|
3
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
/**
|
|
6
|
+
* Actions — 消息操作组。
|
|
7
|
+
*
|
|
8
|
+
* 常放在 [`Bubble`](/x-components/bubble) 的 `footer`:复制、重新生成、点赞、
|
|
9
|
+
* 切换收藏等。语义为工具栏(role=toolbar),按钮从 `label` / `icon`
|
|
10
|
+
* 生成可访问名称。
|
|
11
|
+
*/
|
|
12
|
+
export const Actions = ({
|
|
13
|
+
items,
|
|
14
|
+
direction = 'horizontal',
|
|
15
|
+
ariaLabel = '操作',
|
|
16
|
+
className,
|
|
17
|
+
style
|
|
18
|
+
}) => /*#__PURE__*/_jsx("div", {
|
|
19
|
+
role: "toolbar",
|
|
20
|
+
"aria-label": ariaLabel,
|
|
21
|
+
"aria-orientation": direction,
|
|
22
|
+
className: classNames(prefixCls('x-actions'), prefixCls(`x-actions--${direction}`), className),
|
|
23
|
+
style: style,
|
|
24
|
+
children: items.map(item => /*#__PURE__*/_jsxs("button", {
|
|
25
|
+
type: "button",
|
|
26
|
+
className: classNames(prefixCls('x-actions-item'), item.danger && prefixCls('x-actions-item--danger'), item.active && prefixCls('x-actions-item--active')),
|
|
27
|
+
disabled: item.disabled,
|
|
28
|
+
"aria-label": typeof item.label === 'string' ? item.label : undefined,
|
|
29
|
+
title: typeof item.label === 'string' ? item.label : undefined,
|
|
30
|
+
onClick: item.onClick,
|
|
31
|
+
children: [item.icon, item.label ? /*#__PURE__*/_jsx("span", {
|
|
32
|
+
className: prefixCls('x-actions-item-label'),
|
|
33
|
+
children: item.label
|
|
34
|
+
}) : null]
|
|
35
|
+
}, item.key))
|
|
36
|
+
});
|
|
37
|
+
Actions.displayName = 'Actions';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Actions — 消息操作组 */
|
|
2
|
+
|
|
3
|
+
.aura-x-actions {
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: var(--aura-spacing-1);
|
|
6
|
+
align-items: center;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.aura-x-actions--vertical {
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: stretch;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.aura-x-actions-item {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
gap: var(--aura-spacing-1);
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
padding: var(--aura-spacing-1) var(--aura-spacing-2);
|
|
20
|
+
border: none;
|
|
21
|
+
border-radius: var(--aura-radius-sm);
|
|
22
|
+
background: transparent;
|
|
23
|
+
color: var(--aura-text-secondary);
|
|
24
|
+
font-size: var(--aura-font-size-xs);
|
|
25
|
+
line-height: 1.5;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
transition:
|
|
28
|
+
background var(--aura-duration-fast) var(--aura-easing),
|
|
29
|
+
color var(--aura-duration-fast) var(--aura-easing);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.aura-x-actions-item:not(:disabled):hover {
|
|
33
|
+
background: var(--aura-x-accent-soft);
|
|
34
|
+
color: var(--aura-x-accent);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.aura-x-actions-item:focus-visible {
|
|
38
|
+
outline: none;
|
|
39
|
+
box-shadow: var(--aura-x-focus-ring);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.aura-x-actions-item--active {
|
|
43
|
+
color: var(--aura-x-accent);
|
|
44
|
+
background: var(--aura-x-accent-soft);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.aura-x-actions-item--danger:not(:disabled):hover {
|
|
48
|
+
background: rgba(239, 68, 68, 0.12);
|
|
49
|
+
color: var(--aura-error);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.aura-x-actions-item:disabled {
|
|
53
|
+
opacity: 0.45;
|
|
54
|
+
cursor: not-allowed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.aura-x-actions--vertical .aura-x-actions-item {
|
|
58
|
+
justify-content: flex-start;
|
|
59
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FileCardStatus } from '../file-card';
|
|
3
|
+
export interface AttachmentItem {
|
|
4
|
+
/** 唯一键(同时作为 React key) */
|
|
5
|
+
id: React.Key;
|
|
6
|
+
name: string;
|
|
7
|
+
/** 字节数 */
|
|
8
|
+
size?: number;
|
|
9
|
+
status?: FileCardStatus;
|
|
10
|
+
/** `status="uploading"` 时的进度(0–100) */
|
|
11
|
+
percent?: number;
|
|
12
|
+
description?: React.ReactNode;
|
|
13
|
+
/** 失败原因 */
|
|
14
|
+
errorTip?: React.ReactNode;
|
|
15
|
+
/** 原始 File 对象,随 onRemove 回调透传 */
|
|
16
|
+
file?: File;
|
|
17
|
+
}
|
|
18
|
+
export interface AttachmentsProps {
|
|
19
|
+
items: AttachmentItem[];
|
|
20
|
+
/** 移除回调:回传被移除的项与其索引 */
|
|
21
|
+
onRemove?: (item: AttachmentItem, index: number) => void;
|
|
22
|
+
/**
|
|
23
|
+
* 溢出模式:
|
|
24
|
+
* - `wrap`:换行排列(默认)
|
|
25
|
+
* - `scrollX`:单行横向滚动,超出容器宽度可滑动
|
|
26
|
+
* @default 'wrap'
|
|
27
|
+
*/
|
|
28
|
+
overflow?: 'wrap' | 'scrollX';
|
|
29
|
+
/** 空列表占位;缺省渲染 null */
|
|
30
|
+
empty?: React.ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
style?: React.CSSProperties;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Attachments — 附件列表。
|
|
36
|
+
*
|
|
37
|
+
* 输入框(`Sender` 的 `header` 插槽)上方的附件条:把待发送的文件
|
|
38
|
+
* 以 [`FileCard`](/x-components/file-card) 形态横向排布,支持移除与
|
|
39
|
+
* 上传进度。语义为 `role="list"`,每张卡片是一个 `listitem`。
|
|
40
|
+
*/
|
|
41
|
+
export declare const Attachments: React.FC<AttachmentsProps>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { classNames, prefixCls } from '@aura-react-comp/shared';
|
|
3
|
+
import { FileCard } from "../file-card";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
/**
|
|
6
|
+
* Attachments — 附件列表。
|
|
7
|
+
*
|
|
8
|
+
* 输入框(`Sender` 的 `header` 插槽)上方的附件条:把待发送的文件
|
|
9
|
+
* 以 [`FileCard`](/x-components/file-card) 形态横向排布,支持移除与
|
|
10
|
+
* 上传进度。语义为 `role="list"`,每张卡片是一个 `listitem`。
|
|
11
|
+
*/
|
|
12
|
+
export const Attachments = ({
|
|
13
|
+
items,
|
|
14
|
+
onRemove,
|
|
15
|
+
overflow = 'wrap',
|
|
16
|
+
empty,
|
|
17
|
+
className,
|
|
18
|
+
style
|
|
19
|
+
}) => {
|
|
20
|
+
if (items.length === 0) {
|
|
21
|
+
if (!empty) return null;
|
|
22
|
+
return /*#__PURE__*/_jsx("div", {
|
|
23
|
+
className: classNames(prefixCls('x-attachments'), prefixCls('x-attachments--empty'), className),
|
|
24
|
+
style: style,
|
|
25
|
+
children: empty
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return /*#__PURE__*/_jsx("div", {
|
|
29
|
+
role: "list",
|
|
30
|
+
"aria-label": "\u9644\u4EF6\u5217\u8868",
|
|
31
|
+
className: classNames(prefixCls('x-attachments'), overflow === 'scrollX' && prefixCls('x-attachments--scroll-x'), className),
|
|
32
|
+
style: style,
|
|
33
|
+
children: items.map((item, index) => /*#__PURE__*/_jsx("div", {
|
|
34
|
+
role: "listitem",
|
|
35
|
+
className: prefixCls('x-attachments-item'),
|
|
36
|
+
children: /*#__PURE__*/_jsx(FileCard, {
|
|
37
|
+
name: item.name,
|
|
38
|
+
size: item.size,
|
|
39
|
+
status: item.status,
|
|
40
|
+
percent: item.percent,
|
|
41
|
+
description: item.description,
|
|
42
|
+
errorTip: item.errorTip,
|
|
43
|
+
onRemove: onRemove ? () => onRemove(item, index) : undefined
|
|
44
|
+
})
|
|
45
|
+
}, item.id))
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
Attachments.displayName = 'Attachments';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* Attachments — 附件列表(输入框上方的附件条) */
|
|
2
|
+
|
|
3
|
+
.aura-x-attachments {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-wrap: wrap;
|
|
6
|
+
gap: var(--aura-spacing-2);
|
|
7
|
+
align-items: stretch;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.aura-x-attachments--scroll-x {
|
|
11
|
+
flex-wrap: nowrap;
|
|
12
|
+
padding-bottom: var(--aura-spacing-1);
|
|
13
|
+
overflow-x: auto;
|
|
14
|
+
/* 横滑条与卡片间距,避免遮挡最后一张卡片 */
|
|
15
|
+
scrollbar-width: thin;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.aura-x-attachments--empty {
|
|
19
|
+
color: var(--aura-text-tertiary);
|
|
20
|
+
font-size: var(--aura-font-size-xs);
|
|
21
|
+
line-height: 1.5;
|
|
22
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface BubbleProps {
|
|
3
|
+
/** 消息角色:user 渲染在右侧,assistant / system 渲染在左侧 */
|
|
4
|
+
role?: 'user' | 'assistant' | 'system';
|
|
5
|
+
/** 消息内容(纯文本);需要 Markdown 时传 contentRender */
|
|
6
|
+
content?: string;
|
|
7
|
+
/** 自定义内容渲染扩展点(如传 MarkdownContent) */
|
|
8
|
+
contentRender?: (content: string) => React.ReactNode;
|
|
9
|
+
/** 头像(放任意节点,如 @aura-react-comp/ui 的 Avatar) */
|
|
10
|
+
avatar?: React.ReactNode;
|
|
11
|
+
/** 气泡上方的说明区(如消息来源、时间) */
|
|
12
|
+
header?: React.ReactNode;
|
|
13
|
+
/** 气泡下方的操作区(如复制 / 重新生成按钮) */
|
|
14
|
+
footer?: React.ReactNode;
|
|
15
|
+
/** 生成中:内容区显示三点打字动画,aria-busy 标记 */
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
/** 视觉变体 */
|
|
18
|
+
variant?: 'filled' | 'outlined' | 'shadow';
|
|
19
|
+
className?: string;
|
|
20
|
+
style?: React.CSSProperties;
|
|
21
|
+
}
|
|
22
|
+
export declare const BubbleBase: React.FC<BubbleProps>;
|
|
23
|
+
export interface BubbleListItem extends BubbleProps {
|
|
24
|
+
/** 列表内唯一键 */
|
|
25
|
+
key: React.Key;
|
|
26
|
+
}
|
|
27
|
+
export interface BubbleListProps {
|
|
28
|
+
items: BubbleListItem[];
|
|
29
|
+
/**
|
|
30
|
+
* items 变化时自动滚动到底部(打字机场景)。
|
|
31
|
+
* 容器高度由使用方控制(如 style={{ maxHeight: 320 }}),超出部分内部滚动。
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
autoScroll?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
style?: React.CSSProperties;
|
|
37
|
+
}
|
|
38
|
+
export declare const BubbleList: React.FC<BubbleListProps>;
|
|
39
|
+
/** Bubble(含 Bubble.List 复合导出,与 antd X 的用法对齐) */
|
|
40
|
+
export declare const Bubble: React.FC<BubbleProps> & {
|
|
41
|
+
List: typeof BubbleList;
|
|
42
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { classNames, prefixCls } from '@aura-react-comp/shared';
|
|
3
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
export const BubbleBase = ({
|
|
6
|
+
role = 'assistant',
|
|
7
|
+
content = '',
|
|
8
|
+
contentRender,
|
|
9
|
+
avatar,
|
|
10
|
+
header,
|
|
11
|
+
footer,
|
|
12
|
+
loading = false,
|
|
13
|
+
variant = 'filled',
|
|
14
|
+
className,
|
|
15
|
+
style
|
|
16
|
+
}) => {
|
|
17
|
+
// user 在右(end),assistant / system 在左(start)
|
|
18
|
+
const placement = role === 'user' ? 'end' : 'start';
|
|
19
|
+
const rootClass = classNames(prefixCls('x-bubble'), prefixCls(`x-bubble--${placement}`), prefixCls(`x-bubble--${variant}`), className);
|
|
20
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
21
|
+
className: rootClass,
|
|
22
|
+
"data-role": role,
|
|
23
|
+
style: style,
|
|
24
|
+
"aria-busy": loading || undefined,
|
|
25
|
+
children: [avatar ? /*#__PURE__*/_jsx("div", {
|
|
26
|
+
className: prefixCls('x-bubble-avatar'),
|
|
27
|
+
children: avatar
|
|
28
|
+
}) : null, /*#__PURE__*/_jsxs("div", {
|
|
29
|
+
className: prefixCls('x-bubble-main'),
|
|
30
|
+
children: [header ? /*#__PURE__*/_jsx("div", {
|
|
31
|
+
className: prefixCls('x-bubble-header'),
|
|
32
|
+
children: header
|
|
33
|
+
}) : null, /*#__PURE__*/_jsx("div", {
|
|
34
|
+
className: classNames(prefixCls('x-bubble-content'), loading && prefixCls('x-bubble-content-loading')),
|
|
35
|
+
children: loading ? /*#__PURE__*/_jsxs("span", {
|
|
36
|
+
className: prefixCls('x-bubble-dots'),
|
|
37
|
+
"aria-hidden": "true",
|
|
38
|
+
children: [/*#__PURE__*/_jsx("i", {}), /*#__PURE__*/_jsx("i", {}), /*#__PURE__*/_jsx("i", {})]
|
|
39
|
+
}) : contentRender ? contentRender(content) : /*#__PURE__*/_jsx("span", {
|
|
40
|
+
className: prefixCls('x-bubble-text'),
|
|
41
|
+
children: content
|
|
42
|
+
})
|
|
43
|
+
}), footer ? /*#__PURE__*/_jsx("div", {
|
|
44
|
+
className: prefixCls('x-bubble-footer'),
|
|
45
|
+
children: footer
|
|
46
|
+
}) : null]
|
|
47
|
+
})]
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
BubbleBase.displayName = 'Bubble';
|
|
51
|
+
export const BubbleList = ({
|
|
52
|
+
items,
|
|
53
|
+
autoScroll = true,
|
|
54
|
+
className,
|
|
55
|
+
style
|
|
56
|
+
}) => {
|
|
57
|
+
const listRef = useRef(null);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!autoScroll) return;
|
|
60
|
+
const el = listRef.current;
|
|
61
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
62
|
+
}, [items, autoScroll]);
|
|
63
|
+
return /*#__PURE__*/_jsx("div", {
|
|
64
|
+
ref: listRef,
|
|
65
|
+
className: classNames(prefixCls('x-bubble-list'), className),
|
|
66
|
+
style: style,
|
|
67
|
+
children: items.map(({
|
|
68
|
+
key,
|
|
69
|
+
...rest
|
|
70
|
+
}) => /*#__PURE__*/_jsx(BubbleBase, {
|
|
71
|
+
...rest
|
|
72
|
+
}, key))
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
BubbleList.displayName = 'BubbleList';
|
|
76
|
+
|
|
77
|
+
/** Bubble(含 Bubble.List 复合导出,与 antd X 的用法对齐) */
|
|
78
|
+
export const Bubble = Object.assign(BubbleBase, {
|
|
79
|
+
List: BubbleList
|
|
80
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* Bubble — AI 对话气泡(玻璃拟态 + 品牌渐变) */
|
|
2
|
+
|
|
3
|
+
.aura-x-bubble {
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: var(--aura-spacing-3);
|
|
6
|
+
align-items: flex-start;
|
|
7
|
+
animation: aura-x-bubble-in var(--aura-duration-normal) var(--aura-easing);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@keyframes aura-x-bubble-in {
|
|
11
|
+
from {
|
|
12
|
+
opacity: 0;
|
|
13
|
+
transform: translateY(6px);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
to {
|
|
17
|
+
opacity: 1;
|
|
18
|
+
transform: none;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* 用户消息在右侧 */
|
|
23
|
+
.aura-x-bubble--end {
|
|
24
|
+
flex-direction: row-reverse;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.aura-x-bubble-avatar {
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
display: grid;
|
|
30
|
+
place-items: center;
|
|
31
|
+
width: 36px;
|
|
32
|
+
height: 36px;
|
|
33
|
+
border-radius: var(--aura-radius-full);
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
border: 1px solid var(--aura-x-bubble-border);
|
|
36
|
+
background: var(--aura-x-stage-bg);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.aura-x-bubble-main {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: var(--aura-spacing-1);
|
|
43
|
+
min-width: 0;
|
|
44
|
+
max-width: 100%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.aura-x-bubble--end .aura-x-bubble-main {
|
|
48
|
+
align-items: flex-end;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.aura-x-bubble-header,
|
|
52
|
+
.aura-x-bubble-footer {
|
|
53
|
+
font-size: var(--aura-font-size-xs);
|
|
54
|
+
color: var(--aura-text-tertiary);
|
|
55
|
+
padding: 0 var(--aura-spacing-1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.aura-x-bubble-content {
|
|
59
|
+
padding: var(--aura-spacing-3) var(--aura-spacing-4);
|
|
60
|
+
border-radius: var(--aura-radius-lg);
|
|
61
|
+
font-size: var(--aura-font-size-md);
|
|
62
|
+
line-height: 1.65;
|
|
63
|
+
word-break: break-word;
|
|
64
|
+
white-space: pre-wrap;
|
|
65
|
+
transition:
|
|
66
|
+
background var(--aura-duration-normal) var(--aura-easing),
|
|
67
|
+
box-shadow var(--aura-duration-normal) var(--aura-easing);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* 气泡尖角朝向说话者(现代聊天形态) */
|
|
71
|
+
.aura-x-bubble--start .aura-x-bubble-content {
|
|
72
|
+
border-top-left-radius: var(--aura-radius-sm);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.aura-x-bubble--end .aura-x-bubble-content {
|
|
76
|
+
border-top-right-radius: var(--aura-radius-sm);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* AI:玻璃拟态 */
|
|
80
|
+
.aura-x-bubble--start.aura-x-bubble--filled .aura-x-bubble-content {
|
|
81
|
+
background: var(--aura-x-bubble-ai-bg);
|
|
82
|
+
border: 1px solid var(--aura-x-bubble-border);
|
|
83
|
+
backdrop-filter: var(--aura-x-glass-blur);
|
|
84
|
+
-webkit-backdrop-filter: var(--aura-x-glass-blur);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* 用户:品牌渐变 + 光晕投影 */
|
|
88
|
+
.aura-x-bubble--end.aura-x-bubble--filled .aura-x-bubble-content {
|
|
89
|
+
background: var(--aura-x-bubble-user-bg);
|
|
90
|
+
color: var(--aura-x-bubble-user-text);
|
|
91
|
+
box-shadow: 0 6px 18px -6px rgba(124, 58, 237, 0.55);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* outlined:细边框玻璃 */
|
|
95
|
+
.aura-x-bubble--outlined .aura-x-bubble-content {
|
|
96
|
+
background: var(--aura-x-bubble-ai-bg);
|
|
97
|
+
border: 1px solid var(--aura-x-bubble-border);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* shadow:玻璃 + 光晕 */
|
|
101
|
+
.aura-x-bubble--shadow .aura-x-bubble-content {
|
|
102
|
+
background: var(--aura-x-bubble-ai-bg);
|
|
103
|
+
backdrop-filter: var(--aura-x-glass-blur);
|
|
104
|
+
-webkit-backdrop-filter: var(--aura-x-glass-blur);
|
|
105
|
+
box-shadow: var(--aura-x-glow);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* 生成中:品牌色光点 */
|
|
109
|
+
.aura-x-bubble-dots {
|
|
110
|
+
display: inline-flex;
|
|
111
|
+
gap: 4px;
|
|
112
|
+
align-items: center;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.aura-x-bubble-dots i {
|
|
116
|
+
width: 6px;
|
|
117
|
+
height: 6px;
|
|
118
|
+
border-radius: var(--aura-radius-full);
|
|
119
|
+
background: var(--aura-x-accent);
|
|
120
|
+
opacity: 0.35;
|
|
121
|
+
animation: aura-x-bubble-blink 1.2s infinite ease-in-out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.aura-x-bubble-dots i:nth-child(2) {
|
|
125
|
+
animation-delay: 0.2s;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.aura-x-bubble-dots i:nth-child(3) {
|
|
129
|
+
animation-delay: 0.4s;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@keyframes aura-x-bubble-blink {
|
|
133
|
+
0%,
|
|
134
|
+
80%,
|
|
135
|
+
100% {
|
|
136
|
+
opacity: 0.2;
|
|
137
|
+
transform: scale(0.85);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
40% {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
transform: scale(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* 无障碍:偏好减少动效时关闭入场与打点动画 */
|
|
147
|
+
@media (prefers-reduced-motion: reduce) {
|
|
148
|
+
.aura-x-bubble {
|
|
149
|
+
animation: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.aura-x-bubble-dots i {
|
|
153
|
+
animation: none;
|
|
154
|
+
opacity: 0.6;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* 列表 */
|
|
159
|
+
.aura-x-bubble-list {
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
gap: var(--aura-spacing-4);
|
|
163
|
+
overflow-y: auto;
|
|
164
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface CodeHighlighterProps {
|
|
3
|
+
/**
|
|
4
|
+
* 代码语言。prism-react-renderer 内置语言(js / ts / tsx / jsx / json /
|
|
5
|
+
* bash / css / html / python / go / sql / yaml 等)会着色;
|
|
6
|
+
* 未内置的语言安全回退为纯文本,不会报错。
|
|
7
|
+
* @default 'text'
|
|
8
|
+
*/
|
|
9
|
+
lang?: string;
|
|
10
|
+
/** 代码内容 */
|
|
11
|
+
children?: string;
|
|
12
|
+
/**
|
|
13
|
+
* 头部内容:
|
|
14
|
+
* - 不传:渲染默认头部(语言名 + 复制按钮)
|
|
15
|
+
* - `false`:不渲染头部
|
|
16
|
+
* - 函数:`({ lang, code }) => ReactNode | false`,返回 `false` 同样不渲染
|
|
17
|
+
*/
|
|
18
|
+
header?: React.ReactNode | ((info: {
|
|
19
|
+
lang: string;
|
|
20
|
+
code: string;
|
|
21
|
+
}) => React.ReactNode | false) | false;
|
|
22
|
+
/** 复制成功后的提示文案 @default '已复制' */
|
|
23
|
+
copiedText?: string;
|
|
24
|
+
/** 复制成功回调 */
|
|
25
|
+
onCopy?: (code: string) => void;
|
|
26
|
+
className?: string;
|
|
27
|
+
style?: React.CSSProperties;
|
|
28
|
+
}
|
|
29
|
+
export interface CodeHighlighterRef {
|
|
30
|
+
/** 根节点 DOM */
|
|
31
|
+
nativeElement: HTMLDivElement | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* CodeHighlighter — 代码高亮。
|
|
35
|
+
*
|
|
36
|
+
* 基于 `prism-react-renderer`:等宽字体 + 语法着色 + 头部语言标识与一键复制。
|
|
37
|
+
* 配色走 `var(--aura-*)` 令牌,亮暗主题自动跟随,无需传入主题对象。
|
|
38
|
+
*/
|
|
39
|
+
export declare const CodeHighlighter: React.ForwardRefExoticComponent<CodeHighlighterProps & React.RefAttributes<CodeHighlighterRef>>;
|