Compare commits
2 Commits
ff4217c72a
...
7125753ca2
| Author | SHA1 | Date | |
|---|---|---|---|
| 7125753ca2 | |||
| 9dd2d4a1fc |
89
openspec/specs/ai-message-blocks/spec.md
Normal file
89
openspec/specs/ai-message-blocks/spec.md
Normal file
@@ -0,0 +1,89 @@
|
||||
## Purpose
|
||||
|
||||
定义AI消息中思考过程、工具调用和对话正文的多Block结构规范。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: AI消息Block结构
|
||||
AI消息气泡内 SHALL 支持多个Block的灵活组织,Block类型包括thinking、tool、content三种。
|
||||
|
||||
#### Scenario: 单个Thinking Block
|
||||
- **WHEN** AI消息包含thinking过程
|
||||
- **THEN** 在消息气泡内显示thinking Block,包含标题、执行时间、可展开的详细内容
|
||||
|
||||
#### Scenario: 单个Tool Block
|
||||
- **WHEN** AI消息包含工具调用
|
||||
- **THEN** 在消息气泡内显示tool Block,包含工具名称、状态(input/output)
|
||||
|
||||
#### Scenario: 多个Block组合
|
||||
- **WHEN** AI消息包含多个thinking和tool
|
||||
- **THEN** 按声明顺序依次显示,最后显示content
|
||||
- **AND** 所有thinking和tool必须在content之前
|
||||
|
||||
### Requirement: 可折叠容器
|
||||
Thinking和Tool Block SHALL 使用统一的可折叠容器样式。
|
||||
|
||||
#### Scenario: 展开状态
|
||||
- **WHEN** 用户点击Block头部
|
||||
- **THEN** 显示详细内容区域
|
||||
|
||||
#### Scenario: 收起状态
|
||||
- **WHEN** Block初始状态为收起
|
||||
- **THEN** 只显示头部标题和状态,点击后展开
|
||||
|
||||
#### Scenario: Thinking默认展开
|
||||
- **WHEN** 渲染thinking Block
|
||||
- **THEN** 默认展开显示内容
|
||||
|
||||
#### Scenario: Tool默认收起
|
||||
- **WHEN** 渲染tool Block
|
||||
- **THEN** 默认收起,只显示头部
|
||||
|
||||
### Requirement: 工具调用显示
|
||||
Tool Block SHALL 清晰展示input和output内容。
|
||||
|
||||
#### Scenario: 显示输入参数
|
||||
- **WHEN** tool Block展开
|
||||
- **THEN** 显示INPUT区域,包含JSON格式化的调用参数
|
||||
|
||||
#### Scenario: 显示返回结果
|
||||
- **WHEN** tool执行完成
|
||||
- **THEN** 显示OUTPUT区域,包含JSON格式化的返回值
|
||||
|
||||
#### Scenario: 执行状态
|
||||
- **WHEN** 渲染tool Block
|
||||
- **THEN** 显示执行状态:running(运行中)/completed(完成)/error(失败)
|
||||
- **AND** running状态带脉冲动画
|
||||
|
||||
### Requirement: 时间显示
|
||||
时间显示在Block头部的status区域中。
|
||||
|
||||
#### Scenario: Block执行时间
|
||||
- **WHEN** 渲染thinking或tool Block
|
||||
- **THEN** 在头部status区域显示执行耗时,如"2.1s"
|
||||
|
||||
#### Scenario: Content时间
|
||||
- **WHEN** 渲染content Block
|
||||
- **THEN** 在Block底部显示消息发送时间,如"14:31"
|
||||
|
||||
### Requirement: 消息气泡样式
|
||||
AI消息气泡 SHALL 使用毛玻璃效果和精细阴影。
|
||||
|
||||
#### Scenario: 毛玻璃效果
|
||||
- **WHEN** 渲染AI消息气泡
|
||||
- **THEN** 使用backdrop-filter: blur(12px)实现毛玻璃效果
|
||||
|
||||
#### Scenario: 精细阴影
|
||||
- **WHEN** 渲染AI消息气泡
|
||||
- **THEN** 使用多层阴影实现精细立体效果
|
||||
|
||||
### Requirement: 场景数据
|
||||
消息场景数据 SHALL 符合新的Block结构。
|
||||
|
||||
#### Scenario: 删除File场景
|
||||
- **WHEN** 系统加载场景列表
|
||||
- **THEN** 不包含file场景
|
||||
|
||||
#### Scenario: 场景消息结构
|
||||
- **WHEN** 渲染场景消息
|
||||
- **THEN** 使用blocks结构组织thinking、tool、content
|
||||
@@ -82,7 +82,8 @@ function WorkspaceSidebar({ isOpen, onClose }) {
|
||||
if (isResizingSidebarRef.current) {
|
||||
setSidebarWidth(prev => {
|
||||
const newWidth = prev - deltaX; // 注意:向左拖动是负数
|
||||
return Math.max(400, Math.min(800, newWidth));
|
||||
const maxWidth = Math.min(1200, window.innerWidth - 480);
|
||||
return Math.max(400, Math.min(maxWidth, newWidth));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,7 +91,9 @@ function WorkspaceSidebar({ isOpen, onClose }) {
|
||||
if (isResizingTreeRef.current) {
|
||||
setFileTreeWidth(prev => {
|
||||
const newWidth = prev + deltaX;
|
||||
return Math.max(180, Math.min(300, newWidth));
|
||||
const minWidth = 300;
|
||||
const maxWidth = previewFile ? sidebarWidth - 32 : sidebarWidth;
|
||||
return Math.max(minWidth, Math.min(maxWidth, newWidth));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -368,7 +371,10 @@ function WorkspaceSidebar({ isOpen, onClose }) {
|
||||
{/* FileTree */}
|
||||
<div
|
||||
className="workspace-sidebar__file-tree"
|
||||
style={{ width: `${fileTreeWidth}px` }}
|
||||
style={{
|
||||
width: previewFile ? `${fileTreeWidth}px` : 'auto',
|
||||
flex: previewFile ? 'none' : '1'
|
||||
}}
|
||||
>
|
||||
<FileTree
|
||||
onFileClick={handleFileClick}
|
||||
@@ -377,10 +383,12 @@ function WorkspaceSidebar({ isOpen, onClose }) {
|
||||
</div>
|
||||
|
||||
{/* 分隔线(可拖动调整) */}
|
||||
<div
|
||||
className="workspace-sidebar__divider"
|
||||
onMouseDown={handleDividerMouseDown}
|
||||
/>
|
||||
{previewFile && (
|
||||
<div
|
||||
className="workspace-sidebar__divider"
|
||||
onMouseDown={handleDividerMouseDown}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Preview Panel */}
|
||||
{previewFile && (
|
||||
@@ -397,13 +405,7 @@ function WorkspaceSidebar({ isOpen, onClose }) {
|
||||
<div
|
||||
className="workspace-sidebar__resize-handle"
|
||||
onMouseDown={handleSidebarMouseDown}
|
||||
>
|
||||
<div className="workspace-sidebar__resize-lines">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 右键菜单 */}
|
||||
|
||||
@@ -4,7 +4,6 @@ export const conversations = [
|
||||
{ id: 'welcome', title: '新对话', time: '欢迎页', scene: 'welcome', status: 'running' },
|
||||
{ id: 'text', title: '代码重构方案讨论', time: '普通对话', scene: 'text', status: 'running' },
|
||||
{ id: 'skill', title: '查询客户数据', time: '调用 Skill', scene: 'skill', status: 'running' },
|
||||
{ id: 'file', title: '分析上传的报表', time: '上传文件', scene: 'file', status: 'running' },
|
||||
{ id: 'code', title: '生成 Python 函数', time: '代码展示', scene: 'code', status: 'running' },
|
||||
{ id: 'table', title: '查询销售报表', time: '表格数据', scene: 'table', status: 'running' },
|
||||
{ id: 'multiTurn', title: '产品方案讨论', time: '多轮对话', scene: 'multiTurn', status: 'running' },
|
||||
@@ -74,12 +73,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">2.1s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>让我分析一下代码重构的思路:</p>
|
||||
<ul>
|
||||
<li>首先识别代码中的重复模式和冗余逻辑</li>
|
||||
@@ -90,6 +90,7 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p>好的,我来帮你重构这段代码。首先让我分析一下现有代码的问题,然后提供优化方案。</p>
|
||||
<p style="margin-top: 12px;"><strong>优化建议:</strong></p>
|
||||
@@ -114,23 +115,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p style="display: flex; align-items: center; gap: 8px; color: #3B82F6;">
|
||||
🧩 已加载CRM客户查询技能
|
||||
</p>
|
||||
</div>
|
||||
<div class="message-time">10:15</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">1.8s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在调用CRM客户查询技能...</p>
|
||||
<ul>
|
||||
<li>识别用户意图:查询客户"张三"的订单信息</li>
|
||||
@@ -141,6 +132,56 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-block message-tool">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">🧩 使用工具: CRM客户查询</span>
|
||||
<span class="message-block-header-status message-tool-completed">✓ 0.4s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">INPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "name": "张三", "timeRange": "1year" }</pre></div>
|
||||
</div>
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">OUTPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "customerId": "C001", "orders": 3, "totalAmount": 28560 }</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-block message-tool">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">🧩 使用工具: 订单数据分析</span>
|
||||
<span class="message-block-header-status message-tool-completed">✓ 0.3s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">INPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "orders": [...], "timeRange": "1year" }</pre></div>
|
||||
</div>
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">OUTPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "totalAmount": 28560, "orderCount": 3, "lastOrderDate": "2026-03-10" }</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">0.6s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<p>根据分析结果,用户可能还想知道:</p>
|
||||
<ul>
|
||||
<li>订单详情列表</li>
|
||||
<li>客户等级权益</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p><strong>客户信息查询结果:</strong></p>
|
||||
<div style="margin-top: 12px; padding: 12px; background: #F8FAFC; border-radius: 8px;">
|
||||
@@ -154,70 +195,6 @@ export function getChatScenes() {
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
file: `
|
||||
<div class="message user">
|
||||
<div class="message-avatar user">张</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p>帮我分析一下这个 Excel 文件里的销售数据</p>
|
||||
<div style="margin-top: 12px; padding: 10px 14px; background: #F8FAFC; border-radius: 8px; display: inline-flex; align-items: center; gap: 10px; color: #475569;">
|
||||
<span style="font-size: 20px;">📊</span>
|
||||
<div>
|
||||
<div style="font-weight: 600;">Q1销售数据.xlsx</div>
|
||||
<div style="font-size: 12px; color: #94A3B8;">2.4 MB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-time">16:20</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p>文件已接收!正在分析数据...</p>
|
||||
<div style="margin-top: 14px;">
|
||||
<div style="height: 6px; background: #E2E8F0; border-radius: 3px; overflow: hidden;">
|
||||
<div style="width: 65%; height: 100%; background: linear-gradient(90deg, #3B82F6, #8B5CF6); border-radius: 3px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-time">16:20</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<p>正在分析 Excel 文件中的销售数据...</p>
|
||||
<ul>
|
||||
<li>文件格式识别:Excel (.xlsx),大小 2.4MB</li>
|
||||
<li>数据结构解析:包含日期、产品、销售额等字段</li>
|
||||
<li>时间范围:2026年 Q1(1-3月)</li>
|
||||
<li>计算指标:总销售额、同比增长、月度趋势、产品占比</li>
|
||||
<li>异常检测:无异常值或缺失数据</li>
|
||||
<li>关键洞察提取:3月表现突出,产品C增长势头强劲</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-bubble">
|
||||
<p><strong>数据分析完成!以下是关键发现:</strong></p>
|
||||
<ul style="margin-top: 12px; padding-left: 20px;">
|
||||
<li>Q1 总销售额:<strong>¥128.5 万</strong>,同比增长 18%</li>
|
||||
<li>3 月份表现最佳,单月突破 50 万</li>
|
||||
<li>产品 A 占比最高(42%),产品 C 增长最快</li>
|
||||
</ul>
|
||||
<p style="margin-top: 12px;">需要生成可视化图表吗?</p>
|
||||
</div>
|
||||
<div class="message-time">16:22</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
code: `
|
||||
<div class="message user">
|
||||
<div class="message-avatar user">张</div>
|
||||
@@ -229,12 +206,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">0.8s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>分析需求:列表去重并保持顺序</p>
|
||||
<ul>
|
||||
<li>使用字典键的有序性(Python 3.7+)</li>
|
||||
@@ -243,6 +221,7 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p>好的,这是一个常用的工具函数。以下提供两种实现方式:</p>
|
||||
<pre style="margin-top: 12px; padding: 16px; background: #1E293B; border-radius: 8px; overflow-x: auto;"><code style="color: #E2E8F0; font-family: 'Fira Code', monospace; font-size: 13px; line-height: 1.6;"><span style="color: #60A5FA;">def</span> <span style="color: #34D399;">dedupe_ordered</span>(lst):
|
||||
@@ -274,12 +253,13 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">1.2s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在查询销售数据...</p>
|
||||
<ul>
|
||||
<li>时间范围:2026年3月1日 - 3月19日</li>
|
||||
@@ -288,6 +268,7 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p><strong>本月各部门销售业绩汇总:</strong></p>
|
||||
<table class="msg-table">
|
||||
@@ -423,12 +404,13 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">30.2s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在连接日志数据库...</p>
|
||||
<ul>
|
||||
<li>目标数据库:log-db-prod-01</li>
|
||||
@@ -437,6 +419,7 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble" style="border-left: 3px solid #EF4444; background: #FEF2F2;">
|
||||
<p style="display: flex; align-items: center; gap: 8px; color: #EF4444; font-weight: 600;">
|
||||
❌ 请求失败
|
||||
|
||||
@@ -155,20 +155,20 @@ function ChatPage() {
|
||||
useEffect(() => {
|
||||
if (!chatMessagesRef.current) return;
|
||||
|
||||
const thinkingElements = chatMessagesRef.current.querySelectorAll('.message-thinking');
|
||||
const blockElements = chatMessagesRef.current.querySelectorAll('.message-block');
|
||||
|
||||
const handleClick = (event) => {
|
||||
const thinkingElement = event.currentTarget;
|
||||
thinkingElement.classList.toggle('expanded');
|
||||
const blockElement = event.currentTarget;
|
||||
blockElement.classList.toggle('expanded');
|
||||
};
|
||||
|
||||
thinkingElements.forEach(el => {
|
||||
blockElements.forEach(el => {
|
||||
el.addEventListener('click', handleClick);
|
||||
el.style.cursor = 'pointer';
|
||||
});
|
||||
|
||||
return () => {
|
||||
thinkingElements.forEach(el => {
|
||||
blockElements.forEach(el => {
|
||||
el.removeEventListener('click', handleClick);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
.workspace-sidebar {
|
||||
width: 500px;
|
||||
min-width: 400px;
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
background: var(--color-bg-2);
|
||||
border-left: 1px solid var(--color-border-2);
|
||||
display: flex;
|
||||
@@ -131,8 +131,8 @@
|
||||
|
||||
// ========== FileTree ==========
|
||||
&__file-tree {
|
||||
min-width: 180px;
|
||||
max-width: 300px;
|
||||
min-width: 300px;
|
||||
max-width: 100%;
|
||||
flex-shrink: 0;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
@@ -177,39 +177,28 @@
|
||||
&__resize-handle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 12px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background: var(--color-border-2);
|
||||
cursor: ew-resize;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
transition: background 0.2s;
|
||||
z-index: 10;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
bottom: 0;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&__resize-lines {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
div {
|
||||
width: 4px;
|
||||
height: 2px;
|
||||
background: var(--color-text-3);
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&__resize-handle:hover &__resize-lines div {
|
||||
background: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
// 工作空间展开按钮
|
||||
|
||||
@@ -105,7 +105,13 @@
|
||||
}
|
||||
|
||||
.message.assistant .message-bubble {
|
||||
background: var(--color-bg-2);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.04),
|
||||
0 4px 12px rgba(0, 0, 0, 0.06);
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -122,17 +128,27 @@
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
// AI 思考过程
|
||||
.message-thinking {
|
||||
// AI 消息Block容器(thinking和tool通用)
|
||||
.message-block {
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--color-border-3);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #FFFBEB;
|
||||
cursor: pointer;
|
||||
|
||||
&.expanded {
|
||||
.message-block-header-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.message-block-content {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-thinking-header {
|
||||
.message-block-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -150,26 +166,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.message-thinking-icon {
|
||||
.message-block-header-icon {
|
||||
transition: transform 0.2s ease;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.message-thinking.expanded .message-thinking-icon {
|
||||
transform: rotate(90deg);
|
||||
.message-block-header-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.message-thinking-content {
|
||||
.message-block-header-status {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.message-block-content {
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #78350F;
|
||||
display: none;
|
||||
|
||||
.message-thinking.expanded & {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 8px 0 0 0;
|
||||
padding-left: 20px;
|
||||
@@ -180,6 +201,95 @@
|
||||
}
|
||||
}
|
||||
|
||||
.message-block-time {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-4);
|
||||
padding: 8px 14px 12px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
// 工具调用Block
|
||||
.message-tool {
|
||||
background: #F0F9FF;
|
||||
border-color: #BFDBFE;
|
||||
|
||||
.message-block-header {
|
||||
color: #1E40AF;
|
||||
background: rgba(219, 234, 254, 0.5);
|
||||
}
|
||||
|
||||
.message-block-header-status {
|
||||
color: #1D4ED8;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-running {
|
||||
color: #3B82F6;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-completed {
|
||||
color: #10B981;
|
||||
}
|
||||
|
||||
.message-tool-error {
|
||||
color: #EF4444;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具调用的input/output分隔
|
||||
.message-tool-io {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
padding-top: 12px;
|
||||
margin-top: 12px;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-io-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748B;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.message-tool-io-content {
|
||||
background: #1E293B;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
overflow-x: auto;
|
||||
|
||||
pre, code {
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 13px;
|
||||
color: #E2E8F0;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
// 输入区
|
||||
.chat-input-wrapper {
|
||||
padding: 16px 24px 24px;
|
||||
|
||||
Reference in New Issue
Block a user