Files
beaver_project/app-instance/frontend/components/office/OfficeShared.tsx
steven_li 30ab74ffb2 feat(engine): 添加MCP连接管理和工具集成功能
- 集成MCP连接管理器,支持MCP服务器连接
- 添加多种内置工具:ClarifyTool、CronTool、DelegateTool、ExecuteCodeTool、
  PatchFileTool、ProcessTool、SendMessageTool、SpawnTool、TerminalTool、
  TodoTool、WebFetchTool、WebSearchTool、WriteFileTool等
- 实现工具注册和装配功能
- 添加技能选择上下文参数
- 支持思考模式控制参数thinking_enabled

feat(coordinator): 重构任务执行计划器参数命名

- 将learning_candidate_enabled重命名为allow_candidate_generation
- 更新TeamGraphScheduler中的参数传递
- 修改LocalAgentRunner中的相关参数处理
- 更新README文档中的相应描述

refactor(context): 标准化工具调用参数格式

- 添加_json导入用于参数序列化
- 实现_provider_tool_calls方法标准化OpenAI兼容的工具调用载荷
- 修复工具调用中参数非字符串类型的序列化问题

refactor(session): 优化消息历史记录过滤逻辑

- 修改get_messages_as_conversation为基于运行状态过滤消息
- 排除未完成、失败或错误结束的运行记录
- 改进对话历史的可见性控制机制

fix(store): 修复FTS索引重建逻辑

- 添加异常处理防止FTS索引创建失败
- 实现_rebuild_fts_index方法重新构建全文搜索索引
- 优化索引触发器和表的维护流程
2026-05-14 09:43:48 +08:00

80 lines
3.1 KiB
TypeScript

'use client';
import { useAppI18n } from '@/lib/i18n/provider';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import type { OfficeTaskStatus, OfficeZoneView } from '@/lib/office';
import { officeTaskStatusLabel } from '@/lib/office';
export function OfficeStatusBadge({
status,
className,
}: {
status: OfficeTaskStatus;
className?: string;
}) {
const { locale } = useAppI18n();
return (
<Badge
variant="outline"
className={cn(
'border text-[11px]',
status === 'done' && 'border-[#B7C2B5] bg-[#E3E8E2] text-[#657162]',
status === 'running' && 'border-[#BCC4CE] bg-[#E4E7EB] text-[#697281]',
status === 'waiting' && 'border-[#B8AEA8] bg-[#E7E2DE] text-[#5F5550]',
status === 'blocked' && 'border-[#B8AEA8] bg-[#E7E2DE] text-[#5F5550]',
status === 'queued' && 'border-[#D8D2CE] bg-[#ECE8E5] text-[#4F4642]',
status === 'error' && 'border-[#B8AEA8] bg-[#E7E2DE] text-[#342E2B]',
status === 'cancelled' && 'border-[#D8D2CE] bg-[#ECE8E5] text-[#6A5E58]',
className
)}
>
{officeTaskStatusLabel(status, locale)}
</Badge>
);
}
export function formatOfficeTime(value?: string | null, locale: 'zh-CN' | 'en-US' = 'zh-CN'): string {
if (!value) return '-';
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat(locale, {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
}).format(date);
}
export function formatOfficeDuration(durationMs: number | null, locale: 'zh-CN' | 'en-US' = 'zh-CN'): string {
if (durationMs === null || durationMs < 0) return '-';
if (durationMs < 1000) return locale === 'en-US' ? '<1s' : '<1秒';
const seconds = Math.floor(durationMs / 1000);
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
if (hours > 0) return locale === 'en-US' ? `${hours}h ${minutes}m` : `${hours}小时 ${minutes}`;
if (minutes > 0) return locale === 'en-US' ? `${minutes}m ${remainingSeconds}s` : `${minutes}${remainingSeconds}`;
return locale === 'en-US' ? `${remainingSeconds}s` : `${remainingSeconds}`;
}
export function progressPercent(value: number | null, max: number | null): number {
if (value === null || max === null || max <= 0) return 0;
return Math.max(0, Math.min(100, Math.round((value / max) * 100)));
}
export function zonePanelClassName(zone: OfficeZoneView): string {
return cn(
'relative min-h-[220px] overflow-hidden rounded-2xl border p-4 shadow-sm',
'before:pointer-events-none before:absolute before:inset-0 before:bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.9),transparent_40%)]',
zone.tone === 'info' && 'border-[#BCC4CE] bg-[#E4E7EB]/70',
zone.tone === 'warn' && 'border-[#B8AEA8] bg-[#E7E2DE]/70',
zone.tone === 'danger' && 'border-[#B8AEA8] bg-[#E7E2DE]/80',
zone.tone === 'success' && 'border-[#B7C2B5] bg-[#E3E8E2]/75',
zone.tone === 'neutral' && 'border-border bg-card'
);
}