From 62091c858ec5e0cb0e219987f5db7ae803354248 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Jul 2026 10:52:41 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B0=86=20AgentRun=20=E6=A0=91?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=80=9D=E7=BB=B4=E5=AF=BC=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/hwlab-cloud-web/bun.lock | 26 ++++ web/hwlab-cloud-web/package.json | 2 + .../src/components/agents/AgentRunMindMap.vue | 146 ++++++++++++++++++ .../src/stores/agent-observer.ts | 2 +- .../src/styles/agent-observer.css | 44 ++++-- .../src/views/agents/AgentRunsView.vue | 63 +------- 6 files changed, 208 insertions(+), 75 deletions(-) create mode 100644 web/hwlab-cloud-web/src/components/agents/AgentRunMindMap.vue diff --git a/web/hwlab-cloud-web/bun.lock b/web/hwlab-cloud-web/bun.lock index 75076d0e..721b2409 100644 --- a/web/hwlab-cloud-web/bun.lock +++ b/web/hwlab-cloud-web/bun.lock @@ -5,7 +5,9 @@ "": { "name": "hwlab-cloud-web", "dependencies": { + "@dagrejs/dagre": "3.0.0", "@pinia/colada": "0.15.3", + "@vue-flow/core": "1.48.2", "@vueuse/core": "^10.7.0", "axios": "^1.16.0", "dompurify": "^3.3.1", @@ -57,6 +59,10 @@ "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + "@dagrejs/dagre": ["@dagrejs/dagre@3.0.0", "", { "dependencies": { "@dagrejs/graphlib": "4.0.1" } }, "sha512-ZzhnTy1rfuoew9Ez3EIw4L2znPGnYYhfn8vc9c4oB8iw6QAsszbiU0vRhlxWPFnmmNSFAkrYeF1PhM5m4lAN0Q=="], + + "@dagrejs/graphlib": ["@dagrejs/graphlib@4.0.1", "", {}, "sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], @@ -217,6 +223,8 @@ "@volar/typescript": ["@volar/typescript@2.4.15", "", { "dependencies": { "@volar/language-core": "2.4.15", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg=="], + "@vue-flow/core": ["@vue-flow/core@1.48.2", "", { "dependencies": { "@vueuse/core": "^10.5.0", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" }, "peerDependencies": { "vue": "^3.3.0" } }, "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.38", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/shared": "3.5.38", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ=="], "@vue/compiler-dom": ["@vue/compiler-dom@3.5.38", "", { "dependencies": { "@vue/compiler-core": "3.5.38", "@vue/shared": "3.5.38" } }, "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw=="], @@ -327,6 +335,24 @@ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], diff --git a/web/hwlab-cloud-web/package.json b/web/hwlab-cloud-web/package.json index b7a86872..85c6b041 100644 --- a/web/hwlab-cloud-web/package.json +++ b/web/hwlab-cloud-web/package.json @@ -16,7 +16,9 @@ "e2e:workbench:report": "bun run deps --quiet && bunx playwright show-report .state/workbench-e2e/html-report" }, "dependencies": { + "@dagrejs/dagre": "3.0.0", "@pinia/colada": "0.15.3", + "@vue-flow/core": "1.48.2", "@vueuse/core": "^10.7.0", "axios": "^1.16.0", "dompurify": "^3.3.1", diff --git a/web/hwlab-cloud-web/src/components/agents/AgentRunMindMap.vue b/web/hwlab-cloud-web/src/components/agents/AgentRunMindMap.vue new file mode 100644 index 00000000..3c0a20fd --- /dev/null +++ b/web/hwlab-cloud-web/src/components/agents/AgentRunMindMap.vue @@ -0,0 +1,146 @@ + + + diff --git a/web/hwlab-cloud-web/src/stores/agent-observer.ts b/web/hwlab-cloud-web/src/stores/agent-observer.ts index 6c846803..54245523 100644 --- a/web/hwlab-cloud-web/src/stores/agent-observer.ts +++ b/web/hwlab-cloud-web/src/stores/agent-observer.ts @@ -687,7 +687,7 @@ export function buildRelationWindow( const append = (id: string, depth: number, position: number, setSize: number): void => { const node = nodes.get(id); if (!node) return; - const defaultExpanded = depth === 0 || selectedPath.has(id) || activePath.has(id); + const defaultExpanded = depth <= 2 || selectedPath.has(id) || activePath.has(id); const expanded = node.children.length > 0 && !collapsedIds.has(id) && (expandedIds.has(id) || defaultExpanded); rows.push({ id: node.id, diff --git a/web/hwlab-cloud-web/src/styles/agent-observer.css b/web/hwlab-cloud-web/src/styles/agent-observer.css index cb213a66..90474532 100644 --- a/web/hwlab-cloud-web/src/styles/agent-observer.css +++ b/web/hwlab-cloud-web/src/styles/agent-observer.css @@ -96,19 +96,33 @@ .agent-observer-card footer strong { color: var(--console-text); } .agent-observer-card footer span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.agent-observer-tree { height: 100%; min-width: 0; min-height: 0; padding-block: 8px; } -.agent-observer-tree .console-virtual-list { overflow: auto; } -.agent-observer-tree-row { position: relative; height: calc(100% - 2px); min-width: 720px; margin-inline: 10px; padding-inline: calc(10px + (var(--tree-depth, 0) * 18px)) 10px; display: grid; grid-template-columns: 18px 110px minmax(220px, 1fr) minmax(180px, .8fr); align-items: center; gap: 9px; border-bottom: 1px solid var(--console-border); outline: 0; font-size: 12px; } -.agent-observer-tree-row:hover, -.agent-observer-tree-row[aria-selected="true"] { background: var(--console-accent-soft); } -.agent-observer-tree-row:focus-visible { box-shadow: inset 0 0 0 2px var(--console-accent); } -.agent-observer-tree-toggle { width: 18px; height: 18px; padding: 0; display: inline-grid; place-items: center; border: 0; background: transparent; color: var(--console-muted); } -.agent-observer-tree-toggle svg { transition: transform .14s ease; } -.agent-observer-tree-toggle svg[data-expanded="true"] { transform: rotate(90deg); } -.agent-observer-tree-row strong, -.agent-observer-tree-row small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.agent-observer-tree-row small, -.agent-observer-relation-kind { color: var(--console-muted); } +.agent-observer-tree { height: 100%; min-width: 0; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); } +.agent-mind-map { position: relative; min-width: 0; min-height: 0; overflow: hidden; background: var(--console-surface-muted); } +.agent-mind-map-flow { width: 100%; height: 100%; } +.agent-mind-map-flow .vue-flow__pane { cursor: grab; } +.agent-mind-map-flow .vue-flow__pane.dragging { cursor: grabbing; } +.agent-mind-map-flow .vue-flow__edge-path { stroke: color-mix(in srgb, var(--console-muted) 55%, var(--console-border)); stroke-width: 1.4; } +.agent-mind-map-flow .vue-flow__arrowhead { fill: var(--console-muted); } +.agent-mind-map-node { width: 220px; height: 70px; min-width: 0; display: grid; grid-template-rows: 19px 1fr 16px; gap: 2px; padding: 7px 9px; border: 1px solid var(--console-border); border-left: 3px solid var(--console-muted); border-radius: 5px; background: var(--console-surface); color: var(--console-text); outline: 0; box-shadow: 0 2px 7px rgb(15 23 42 / 8%); cursor: pointer; } +.agent-mind-map-node[data-kind="scope"] { width: 260px; } +.agent-mind-map-node[data-kind="run"] { width: 244px; } +.agent-mind-map-node[data-status="running"] { border-left-color: var(--console-accent); } +.agent-mind-map-node[data-status="failed"] { border-left-color: var(--console-danger); } +.agent-mind-map-node[data-status="completed"] { border-left-color: var(--console-success); } +.agent-mind-map-node[data-selected="true"] { box-shadow: inset 0 0 0 2px var(--console-accent), 0 2px 7px rgb(15 23 42 / 8%); } +.agent-mind-map-node:focus-visible { outline: 2px solid var(--console-accent); outline-offset: 2px; } +.agent-mind-map-node header { min-width: 0; display: flex; align-items: center; gap: 6px; color: var(--console-muted); font-size: 10px; } +.agent-mind-map-node header > span:first-child { font-weight: 700; } +.agent-mind-map-node header .status-badge { margin-left: auto; } +.agent-mind-map-node strong, +.agent-mind-map-node small { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.agent-mind-map-node strong { font-size: 12px; } +.agent-mind-map-node small { color: var(--console-muted); font-size: 10px; } +.agent-mind-map-toggle { width: 18px; height: 18px; padding: 0; display: inline-grid; place-items: center; border: 1px solid var(--console-border); border-radius: 3px; background: var(--console-surface); color: var(--console-muted); font-size: 14px; line-height: 1; } +.agent-mind-map-controls { position: absolute; right: 10px; bottom: 10px; z-index: 5; display: inline-flex; border: 1px solid var(--console-border); border-radius: 5px; overflow: hidden; background: var(--console-surface); box-shadow: 0 2px 8px rgb(15 23 42 / 10%); } +.agent-mind-map-controls button { width: 34px; height: 34px; padding: 0; display: inline-grid; place-items: center; border: 0; border-right: 1px solid var(--console-border); background: transparent; color: var(--console-text); } +.agent-mind-map-controls button:last-child { border-right: 0; } +.agent-mind-map-controls button:hover { background: var(--console-accent-soft); color: var(--console-accent); } .agent-observer-inspector { min-width: 0; min-height: 0; overflow: auto; border-left: 1px solid var(--console-border); background: var(--console-surface); } .agent-observer-inspector-section { display: grid; gap: 14px; } @@ -190,7 +204,7 @@ .agent-observer-table-row > :nth-child(7) { display: none; } .agent-observer-card-grid { padding: 7px; } .agent-observer-card dl { grid-template-columns: 1fr; gap: 6px; } - .agent-observer-tree-row { min-width: 560px; grid-template-columns: 18px 96px minmax(180px, 1fr) minmax(150px, .8fr); } + .agent-mind-map-controls { right: 7px; bottom: 7px; } .agent-observer-inspector-grid { grid-template-columns: 1fr; } .agent-observer-inspector-wide { grid-column: auto; } .agent-observer-event-tail li { grid-template-columns: 1fr; } @@ -199,5 +213,5 @@ @media (prefers-reduced-motion: reduce) { .agent-observer-table-row[data-recent="true"] { animation: none; } - .agent-observer-tree-toggle svg { transition: none; } + .agent-mind-map-flow .vue-flow__viewport { transition: none !important; } } diff --git a/web/hwlab-cloud-web/src/views/agents/AgentRunsView.vue b/web/hwlab-cloud-web/src/views/agents/AgentRunsView.vue index 0bdf50e1..3857f303 100644 --- a/web/hwlab-cloud-web/src/views/agents/AgentRunsView.vue +++ b/web/hwlab-cloud-web/src/views/agents/AgentRunsView.vue @@ -2,11 +2,10 @@