( topicGroups: TopicGroup[], currentPath: ChatMessage[] )
| 395 | * @returns OutlineNode 结构 |
| 396 | */ |
| 397 | export function computeOutline( |
| 398 | topicGroups: TopicGroup[], |
| 399 | currentPath: ChatMessage[] |
| 400 | ): OutlineNode[] { |
| 401 | const outline: OutlineNode[] = [] |
| 402 | let currentTopicNode: OutlineNode | null = null |
| 403 | |
| 404 | // 构建消息 ID 到消息的映射 |
| 405 | const messageMap = new Map<string, ChatMessage>() |
| 406 | for (const msg of currentPath) { |
| 407 | messageMap.set(msg.id, msg) |
| 408 | } |
| 409 | |
| 410 | // 构建 Topic startMessageId 到 TopicGroup 的映射 |
| 411 | const topicGroupMap = new Map<string, TopicGroup>() |
| 412 | for (const group of topicGroups) { |
| 413 | topicGroupMap.set(group.startMessageId, group) |
| 414 | } |
| 415 | |
| 416 | // 截取消息内容作为预览 |
| 417 | const getContentPreview = (content: string, maxLen = 30): string => { |
| 418 | const text = content.replace(/\n/g, ' ').trim() |
| 419 | return text.length > maxLen ? text.slice(0, maxLen) + '...' : text |
| 420 | } |
| 421 | |
| 422 | // 遍历当前路径 |
| 423 | for (const msg of currentPath) { |
| 424 | const topicGroup = topicGroupMap.get(msg.id) |
| 425 | |
| 426 | // 处理 Topic 节点 |
| 427 | if (topicGroup) { |
| 428 | const topicNode: OutlineNode = { |
| 429 | id: `topic-${topicGroup.topicId}`, |
| 430 | title: topicGroup.name, |
| 431 | type: 'topic', |
| 432 | messageId: msg.id, |
| 433 | topicId: topicGroup.topicId, |
| 434 | role: msg.role, |
| 435 | children: [], |
| 436 | collapsed: topicGroup.collapsed |
| 437 | } |
| 438 | |
| 439 | // 所有 Topic 都是顶级节点 |
| 440 | outline.push(topicNode) |
| 441 | currentTopicNode = topicNode |
| 442 | |
| 443 | // Topic 起始消息作为第一个子节点 |
| 444 | if (msg.title) { |
| 445 | const titleNode: OutlineNode = { |
| 446 | id: `title-${msg.id}`, |
| 447 | title: msg.title, |
| 448 | type: 'title', |
| 449 | messageId: msg.id, |
| 450 | role: msg.role |
| 451 | } |
| 452 | topicNode.children!.push(titleNode) |
| 453 | } else { |
| 454 | // 无标题的 Topic 起始消息 |
nothing calls this directly
no test coverage detected