@@ -3248,8 +3248,8 @@ ${task.assignee ? `<div class="row"><b>Worker</b><span>${escape(task.assignee)}<
32483248
32493249 // ───── Stage 2 DECOMPOSE (1.2s 后) — 建 task 节点 ─────
32503250 await sleep ( 1200 )
3251- const COL_W = 260
3252- const ROW_H = 220
3251+ const COL_W = 280 // task 列宽 (ontologyNode ~220 + 60 gap)
3252+ const ROW_H = 240 // 行高 (ontologyNode ~140 + label/desc 60 + 40 gap)
32533253 const GROUP_CENTER_X = 800 // root 居中在 group 中心
32543254 const ROOT_Y = 60
32553255 const TASK_Y = ROOT_Y + ROW_H
@@ -3316,26 +3316,59 @@ ${task.assignee ? `<div class="row"><b>Worker</b><span>${escape(task.assignee)}<
33163316 } )
33173317 } )
33183318
3319+ // 改进的 agent 布局: 不按 anchor task 找 x (会重叠), 改全部 agent 一行排开,
3320+ // 按 (stage_index, 同 stage 内顺序) 全局排 — 这样:
3321+ // - 同 stage 的 agent 视觉上仍然相邻 (染色一致)
3322+ // - 不会因为多 role 同 task 而横向只错开 60px (旧逻辑实测重叠严重)
3323+ // - X 间距 = AGENT_COL_W (300), 对 agentRoleNode 宽度 220-260 安全
3324+ const AGENT_COL_W = 300
3325+ const orderedRoles = [ ]
3326+ ; [ ...structure . execution_topology . stages ]
3327+ . sort ( ( a , b ) => a . stage_index - b . stage_index )
3328+ . forEach ( ( s ) => {
3329+ s . role_ids . forEach ( ( rid ) => {
3330+ const role = structure . roles . find ( ( r ) => r . id === rid )
3331+ if ( role ) orderedRoles . push ( { role, stageIndex : s . stage_index , stageInfo : s } )
3332+ } )
3333+ } )
3334+ // 兜底: 如果有 role 没在 topology 里, 也排进去
3335+ structure . roles . forEach ( ( role ) => {
3336+ if ( ! orderedRoles . find ( ( x ) => x . role . id === role . id ) ) {
3337+ orderedRoles . push ( { role, stageIndex : 1 , stageInfo : { kind : 'parallel' } } )
3338+ }
3339+ } )
3340+ const totalAgentCount = orderedRoles . length
3341+ const stageMaxForLayout = Math . max ( 1 , ...orderedRoles . map ( ( r ) => r . stageIndex ) )
3342+
3343+ // 动态扩 projectGroup 宽高 — agent 多 / stage 深时不要溢出 group 边界
3344+ const requiredAgentRowWidth = totalAgentCount * AGENT_COL_W + 200
3345+ const requiredTaskRowWidth = taskCount * COL_W + 200
3346+ const requiredGroupWidth = Math . max ( 1600 , requiredAgentRowWidth , requiredTaskRowWidth )
3347+ // height = root (60+220) + task row (240) + agent_y_offset + conclusion 间距 + 底 padding
3348+ // agent_y_max = TASK_Y + ROW_H + 20 + (stageMax-1)*100 + 140 (agent height)
3349+ // conclusion_y = agent_y_max + 100 (gap) + 140 (conclusion height) + 60 (bottom pad)
3350+ const requiredGroupHeight = TASK_Y + ROW_H + 20 + ( stageMaxForLayout - 1 ) * 100 + 240 + 140 + 60
3351+ const finalGroupHeight = Math . max ( 1100 , requiredGroupHeight )
3352+ if ( requiredGroupWidth > 1600 || finalGroupHeight > 1100 ) {
3353+ set ( ( state ) => {
3354+ const g = state . nodes . find ( ( n ) => n . id === projectGroupId )
3355+ if ( g ) {
3356+ g . style = { ...g . style , width : requiredGroupWidth , height : finalGroupHeight }
3357+ }
3358+ } )
3359+ }
3360+ const agentStartX = GROUP_CENTER_X - ( ( totalAgentCount - 1 ) * AGENT_COL_W ) / 2
3361+
33193362 const roleIdMap = { }
3320- // role 的 y 按它所在 stage_index 错位排, x 跟随 assigned_tasks 第一个 task
3321- for ( let i = 0 ; i < structure . roles . length ; i ++ ) {
3322- const role = structure . roles [ i ]
3323- const firstTaskId = role . assigned_tasks [ 0 ]
3324- const anchorTaskNode = firstTaskId ? taskNodes . find ( ( n ) => n . data . projectTaskId === firstTaskId ) : null
3325- const stageInfo = roleStageMap [ role . id ]
3326- const stageIndex = stageInfo ?. stage_index || 1
3327- // 同 stage 多 role 横向错开 — 计算这个 stage 里它的位置
3328- const sameStageRoles = structure . execution_topology . stages . find ( ( s ) => s . stage_index === stageIndex ) ?. role_ids || [ role . id ]
3329- const sameStageIdx = sameStageRoles . indexOf ( role . id )
3330- const sameStageCount = sameStageRoles . length
3331-
3332- // anchorTaskNode.position 已经是相对 projectGroup 的偏移
3333- const anchorX = anchorTaskNode ? anchorTaskNode . position . x : taskOffsetX0 + i * COL_W
3334- const xOffset = sameStageCount > 1 ? ( sameStageIdx - ( sameStageCount - 1 ) / 2 ) * 60 : 0
3335- const x = anchorX + xOffset
3336- // agent 作为 projectGroup 子, y = task 行下面 + stage 错开
3337- const AGENT_Y_BASE = TASK_Y + ROW_H
3338- const y = AGENT_Y_BASE + ( stageIndex - 1 ) * 40
3363+ for ( let i = 0 ; i < orderedRoles . length ; i ++ ) {
3364+ const { role, stageIndex, stageInfo } = orderedRoles [ i ]
3365+
3366+ // x: 全局横向均分, 各 agent 一格 AGENT_COL_W
3367+ const x = agentStartX + i * AGENT_COL_W
3368+ // y: task 行下方 + 按 stage_index 阶梯下沉 (同 stage 同 y, 视觉成行)
3369+ // stage_index 1 → 紧贴 task 行下面; 后续 stage 再下沉一行
3370+ const AGENT_Y_BASE = TASK_Y + ROW_H + 20 // +20 让 agent 离 task 行远一点
3371+ const y = AGENT_Y_BASE + ( stageIndex - 1 ) * 100
33393372
33403373 const nid = `agent-${ rootId } -${ role . id } `
33413374 roleIdMap [ role . id ] = nid
@@ -3545,11 +3578,13 @@ ${task.assignee ? `<div class="row"><b>Worker</b><span>${escape(task.assignee)}<
35453578 if ( decision ) {
35463579 const stageMaxIdx = Math . max ( 1 , ...Object . values ( roleStageMap ) . map ( ( s ) => s . stage_index || 1 ) )
35473580 const conclusionId = `conclusion-${ rootId } `
3581+ // y = agent 最底行 (TASK_Y+ROW_H+20 + (stageMax-1)*100) + agent 高 ~140 + 间距 100
3582+ const conclusionY = TASK_Y + ROW_H + 20 + ( stageMaxIdx - 1 ) * 100 + 240
35483583 const conclusionNode = {
35493584 id : conclusionId ,
35503585 type : 'ontologyNode' ,
3551- // 相对 projectGroup: 居中横向, 纵向在所有 agent 之下
3552- position : { x : GROUP_CENTER_X , y : TASK_Y + ROW_H + ( stageMaxIdx - 1 ) * 40 + 220 } ,
3586+ // 相对 projectGroup: 居中横向 (GROUP_CENTER_X) , 纵向在所有 agent 之下
3587+ position : { x : GROUP_CENTER_X , y : conclusionY } ,
35533588 parentNode : projectGroupId ,
35543589 data : {
35553590 variant : 'goal' , // 深色显示
0 commit comments