+
import { computed, ref, watch } from 'vue';
-import SbaAlert from '@/components/sba-alert.vue';
-import SbaFormattedObj from '@/components/sba-formatted-obj.vue';
+import SbaKeyValueTable from '@/components/sba-key-value-table.vue';
import Instance from '@/services/instance';
import SbaAccordion from '@/views/instances/details/sba-accordion.vue';
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/health-details.vue b/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/health-details.vue
index 44ecda2eeb2..80fcc19c67f 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/health-details.vue
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/health-details.vue
@@ -56,7 +56,7 @@
v-else
:aria-labelledby="`health-detail-${id}__${detail.name}`"
class="break-words whitespace-pre-wrap"
- v-text="detail.value"
+ v-html="autolink(detail.value)"
/>
@@ -78,6 +78,8 @@ import { computed, useId } from 'vue';
import SbaFormattedObj from '@/components/sba-formatted-obj.vue';
+import autolink from '@/utils/autolink';
+
const id = useId();
const isChildHealth = (value) => {
@@ -112,3 +114,9 @@ const childHealth = computed(() => {
return [];
});
+
+
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/env/refresh.vue b/spring-boot-admin-server-ui/src/main/frontend/views/instances/env/refresh.vue
index 30d100a8e0b..5e5f82d32a1 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/env/refresh.vue
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/env/refresh.vue
@@ -3,18 +3,16 @@
:instance-count="instanceCount"
:action-fn="refreshContext"
:show-info="false"
+ :label="$t('instances.env.context_refresh')"
>
-
-
-
-
+
+ {{ $t('instances.env.context_refresh') }}
+
+
+
+
+
+
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/dependencyTree.ts b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/dependencyTree.ts
index d4be56c37b3..2bd95d2967d 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/dependencyTree.ts
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/dependencyTree.ts
@@ -73,11 +73,15 @@ export type D3DependencyTree = {
};
// Utility Functions
-const linkNodesHorizontal = (hierarchyLink: MyHierarchyLink) =>
- d3
- .linkHorizontal()
- .x((d) => d.y)
- .y((d) => d.x)(hierarchyLink);
+const linkNodesHorizontal = (d) => {
+ const sx = d.source.x;
+ const sy = d.source.y + (d.source.nodeWidth || 0); // right edge of source
+ const tx = d.target.x;
+ const ty = d.target.y; // left edge of target
+ const mx = (sy + ty) / 2; // horizontal midpoint for smooth curve
+ // Horizontal link: M sx at sy, with control points at horizontal midpoint
+ return `M${sy},${sx}C${mx},${sx} ${mx},${tx} ${ty},${tx}`;
+};
const createGlobalLinkAndNode = (
svg: Selection,
@@ -118,7 +122,7 @@ const updateDependencyTree = async (
source: MyHierarchyNode,
removeNodes = false,
): Promise => {
- const { root, treeLayout, svg, gNode, gLink } = dependencyTree;
+ const { root, treeLayout, svg, gNode, gLink, nodeWidth } = dependencyTree;
const nodes = root.descendants().reverse();
const links = root.links();
@@ -135,13 +139,9 @@ const updateDependencyTree = async (
);
const height = right.x - left.x + MARGIN.top + MARGIN.bottom;
+ const width = rightWidth.y - leftWidth.y + MARGIN.left + nodeWidth;
const treeContainerWidth =
dependencyTree.treeContainer.getBoundingClientRect().width;
- const width =
- rightWidth.y -
- leftWidth.y +
- MARGIN.left +
- treeContainerWidth / MAX_ITEMS_IN_FRAME;
svg
.transition()
@@ -189,12 +189,7 @@ const updateDependencyTree = async (
.attr('ry', 6)
.attr('stroke-width', 1)
.attr('fill-opacity', 0.8)
- .style('fill', (d) => (d._children ? '#91E8E0' : '#d0f7df'));
-
- nodeEnter
- .append('circle')
- .attr('r', 3.5)
- .attr('fill', (d) => (d._children ? '#48c78e' : '#999999'));
+ .attr('class', (d) => `node ${d._children ? 'node-with-children' : ''}`);
nodeEnter
.append('text')
@@ -225,6 +220,10 @@ const updateDependencyTree = async (
.style('top', `${event.layerY + 10}px`);
});
+ nodeEnter.each((d) => {
+ d.nodeWidth = nodeWidth;
+ });
+
subGNodeSelection
.merge(nodeEnter)
.transition()
@@ -240,11 +239,13 @@ const updateDependencyTree = async (
.attr('fill-opacity', 0)
.attr('stroke-opacity', 0);
- const link = gLink
- .selectAll('path')
- .data(links, (d: MyHierarchyLink) => d.target.id);
+ const link = gLink.selectAll('path').data(links);
- const linkEnter = link.enter().append('path').attr('d', linkNodesHorizontal);
+ const linkEnter = link
+ .enter()
+ .append('path')
+ .attr('class', 'edge')
+ .attr('d', linkNodesHorizontal);
link.merge(linkEnter).transition().attr('d', linkNodesHorizontal);
@@ -268,6 +269,7 @@ export const createDependencyTree = async (
const elementsWidth = treeContainer.getBoundingClientRect().width;
const dx = 48;
const dy = elementsWidth / MAX_ITEMS_IN_FRAME;
+ const nodeWidth = elementsWidth / (MAX_ITEMS_IN_FRAME + 1);
const root = d3.hierarchy(treeData) as MyHierarchyNode;
const treeLayout = d3.tree().nodeSize([dx, dy]);
@@ -286,7 +288,7 @@ export const createDependencyTree = async (
d3.select(treeContainer)
.append('div')
.attr('id', 'tooltip')
- .attr('class', 'bg-sba-100 rounded')
+ .attr('class', 'border bg-white rounded px-2 shadow')
.attr('style', 'position: absolute; opacity: 0; font-size: 0.85rem;');
const { gLink, gNode } = createGlobalLinkAndNode(svg);
@@ -298,6 +300,7 @@ export const createDependencyTree = async (
svg,
treeLayout,
gLink,
+ nodeWidth,
};
initRootAndDescendants(d3DependencyTree, initFolding);
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/i18n.en.json b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/i18n.en.json
index f1582a14297..3d75f5c0e31 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/i18n.en.json
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/i18n.en.json
@@ -1,7 +1,12 @@
{
"instances": {
"sbom": {
- "label": "Dependency trees"
+ "label": "Dependency trees",
+ "legend": {
+ "title": "Legend",
+ "node": "Dependency with no further dependencies",
+ "node_with_children": "Dependency with other dependencies"
+ }
}
}
}
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/tree.vue b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/tree.vue
index c85db092a7c..c2d6be52f2e 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/tree.vue
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/sbomdependencytrees/tree.vue
@@ -19,12 +19,29 @@
+
+
+
+ {{ t('instances.sbom.legend.title') }}
+
+
+
+
{{ t('instances.sbom.legend.node') }}
+
+
+
+
{{ t('instances.sbom.legend.node_with_children') }}
+
+
-
diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/threads-list.vue b/spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/threads-list.vue
index acd80b3aee9..c76cea6df8d 100644
--- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/threads-list.vue
+++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/threads-list.vue
@@ -15,7 +15,7 @@
-->
-
+
|
@@ -202,6 +202,7 @@ export default {