Spaces:
Runtime error
Runtime error
Đào Minh Tú
feat(wave5): mind-map, templates, personas marketplace, meeting mode, pinned, notes, link-preview
26c925a | import type { BranchTree, ChatSession } from "@/hooks/use-chat-history" | |
| export const MIND_MAP_NODE_WIDTH = 168 | |
| export const MIND_MAP_NODE_HEIGHT = 60 | |
| export const MIND_MAP_LEVEL_GAP = 200 | |
| export const MIND_MAP_ROW_GAP = 20 | |
| export interface MindMapNode { | |
| id: string | |
| title: string | |
| depth: number | |
| x: number | |
| y: number | |
| session: ChatSession | |
| isVirtual?: boolean | |
| } | |
| export interface MindMapEdge { | |
| id: string | |
| from: string | |
| to: string | |
| fromX: number | |
| fromY: number | |
| toX: number | |
| toY: number | |
| } | |
| export interface MindMapLayout { | |
| nodes: MindMapNode[] | |
| edges: MindMapEdge[] | |
| width: number | |
| height: number | |
| } | |
| const ROW_SPACING = MIND_MAP_NODE_HEIGHT + MIND_MAP_ROW_GAP | |
| const PADDING = 48 | |
| export function truncateTitle(title: string, max = 24) { | |
| const normalized = (title || "Cuộc trò chuyện mới").trim() || "Cuộc trò chuyện mới" | |
| return normalized.length > max ? `${normalized.slice(0, max)}…` : normalized | |
| } | |
| export function layoutMindMap(tree: BranchTree | null): MindMapLayout { | |
| if (!tree) return { nodes: [], edges: [], width: 0, height: 0 } | |
| const nodes: MindMapNode[] = [] | |
| const edges: MindMapEdge[] = [] | |
| let leafIndex = 0 | |
| let maxDepth = 0 | |
| function walk(current: BranchTree, depth: number): number { | |
| maxDepth = Math.max(maxDepth, depth) | |
| const children = current.children ?? [] | |
| const childYs = children.map((child) => walk(child, depth + 1)) | |
| const y = childYs.length | |
| ? (Math.min(...childYs) + Math.max(...childYs)) / 2 | |
| : leafIndex++ * ROW_SPACING | |
| const x = depth * MIND_MAP_LEVEL_GAP | |
| const isVirtual = current.session.id === "__all__" | |
| nodes.push({ | |
| id: current.session.id, | |
| title: truncateTitle(current.session.title), | |
| depth, | |
| x, | |
| y, | |
| session: current.session, | |
| isVirtual, | |
| }) | |
| for (const child of children) { | |
| const childNodeY = nodes.find((node) => node.id === child.session.id)?.y ?? y | |
| edges.push({ | |
| id: `${current.session.id}->${child.session.id}`, | |
| from: current.session.id, | |
| to: child.session.id, | |
| fromX: x + MIND_MAP_NODE_WIDTH, | |
| fromY: y + MIND_MAP_NODE_HEIGHT / 2, | |
| toX: (depth + 1) * MIND_MAP_LEVEL_GAP, | |
| toY: childNodeY + MIND_MAP_NODE_HEIGHT / 2, | |
| }) | |
| } | |
| return y | |
| } | |
| walk(tree, 0) | |
| return { | |
| nodes, | |
| edges, | |
| width: maxDepth * MIND_MAP_LEVEL_GAP + MIND_MAP_NODE_WIDTH + PADDING * 2, | |
| height: Math.max(MIND_MAP_NODE_HEIGHT + PADDING * 2, leafIndex * ROW_SPACING + PADDING * 2), | |
| } | |
| } | |