Skip to content

Commit 099420b

Browse files
committed
fix: avoid graph label overlap
1 parent 46e8675 commit 099420b

3 files changed

Lines changed: 66 additions & 20 deletions

File tree

console/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>MathClaw Console</title>
7-
<link rel="stylesheet" href="./styles.css?v=20260404a" />
7+
<link rel="stylesheet" href="./styles.css?v=20260404b" />
88
</head>
99
<body>
1010
<div id="app"></div>
11-
<script type="module" src="./main.js?v=20260404a"></script>
11+
<script type="module" src="./main.js?v=20260404b"></script>
1212
</body>
1313
</html>

console/main.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3090,6 +3090,15 @@ function visualNodeSize(node) {
30903090
return clamp(Math.round(normalized || 32), 24, 46);
30913091
}
30923092

3093+
function stablePlacementSeed(seed) {
3094+
let hash = 0;
3095+
const text = String(seed || "");
3096+
for (let index = 0; index < text.length; index += 1) {
3097+
hash = (hash * 31 + text.charCodeAt(index)) % 9973;
3098+
}
3099+
return hash;
3100+
}
3101+
30933102
function graphNodeAnchor(node) {
30943103
if (node.x > GRAPH_STAGE.width - 220) {
30953104
return "left";
@@ -3107,12 +3116,28 @@ function graphNodeAnchor(node) {
31073116
const centerY = GRAPH_STAGE.height / 2;
31083117
const dx = node.x - centerX;
31093118
const dy = node.y - centerY;
3110-
if (Math.abs(dx) > Math.abs(dy) + 48) {
3119+
const horizontalWeight = Math.abs(dx) * 1.18;
3120+
const verticalWeight = Math.abs(dy);
3121+
if (horizontalWeight > verticalWeight + 20) {
31113122
return dx > 0 ? "left" : "right";
31123123
}
3124+
if (Math.abs(horizontalWeight - verticalWeight) < 42) {
3125+
return stablePlacementSeed(node.id) % 2 === 0 ? "left" : "right";
3126+
}
31133127
return dy > 0 ? "top" : "bottom";
31143128
}
31153129

3130+
function graphBadgeAnchor(node, titleAnchor) {
3131+
if (titleAnchor === "bottom") {
3132+
return "top";
3133+
}
3134+
if (titleAnchor === "top") {
3135+
return "bottom";
3136+
}
3137+
const centerY = GRAPH_STAGE.height / 2;
3138+
return node.y >= centerY ? "top" : "bottom";
3139+
}
3140+
31163141
function stableEdgeCurve(sourceId, targetId, relation) {
31173142
const seed = `${sourceId}|${targetId}|${relation || ""}`;
31183143
let hash = 0;
@@ -3509,17 +3534,18 @@ function renderGraphNodes(graph, highlightNode, selectedNode) {
35093534
const isLinked = activeNodeIds && !isActive && !isMuted;
35103535
const coreSize = visualNodeSize(node);
35113536
const anchor = graphNodeAnchor(node);
3537+
const badgeAnchor = graphBadgeAnchor(node, anchor);
35123538

35133539
return `
35143540
<button
3515-
class="graph-node graph-node--${graph.accent} graph-node--anchor-${anchor}${isActive ? " is-active" : ""}${isSelected ? " is-selected" : ""}${isLinked ? " is-linked" : ""}${isMuted ? " is-muted" : ""}${node.status === "candidate" ? " is-candidate" : ""}${node.isFocus ? " is-focus" : ""}"
3541+
class="graph-node graph-node--${graph.accent} graph-node--anchor-${anchor} graph-node--badge-${badgeAnchor}${isActive ? " is-active" : ""}${isSelected ? " is-selected" : ""}${isLinked ? " is-linked" : ""}${isMuted ? " is-muted" : ""}${node.status === "candidate" ? " is-candidate" : ""}${node.isFocus ? " is-focus" : ""}"
35163542
type="button"
35173543
data-node-id="${node.id}"
35183544
style="left:${node.x}px; top:${node.y}px; width:${coreSize}px; height:${coreSize}px;"
35193545
>
35203546
<span class="graph-node__core" aria-hidden="true"></span>
35213547
<span class="graph-node__title">${node.label}</span>
3522-
<span class="graph-node__badge">${node.badge}</span>
3548+
${node.badge ? `<span class="graph-node__badge">${node.badge}</span>` : ""}
35233549
</button>
35243550
`;
35253551
})

console/styles.css

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,42 +3014,50 @@ button {
30143014
.graph-node__title {
30153015
position: absolute;
30163016
z-index: 2;
3017-
width: 136px;
3018-
max-width: 136px;
3017+
width: 120px;
3018+
max-width: 120px;
3019+
display: -webkit-box;
30193020
font-size: 12px;
30203021
font-weight: 800;
3021-
line-height: 1.2;
3022+
line-height: 1.16;
30223023
letter-spacing: -0.01em;
30233024
white-space: normal;
3025+
-webkit-box-orient: vertical;
3026+
-webkit-line-clamp: 2;
3027+
overflow: hidden;
3028+
max-height: calc(2 * 1.16em);
30243029
color: rgba(74, 84, 108, 0.96);
30253030
text-shadow:
30263031
0 1px 0 rgba(255, 255, 255, 0.96),
30273032
0 0 12px rgba(255, 255, 255, 0.82);
30283033
transition: color 160ms ease, opacity 160ms ease;
3034+
pointer-events: none;
30293035
}
30303036

30313037
.graph-node__badge {
30323038
position: absolute;
30333039
z-index: 2;
3034-
left: 50%;
3035-
top: calc(100% + 10px);
3036-
transform: translate(-50%, 4px);
3040+
display: inline-flex;
3041+
align-items: center;
3042+
justify-content: center;
3043+
min-height: 22px;
30373044
padding: 4px 8px;
30383045
border-radius: 999px;
30393046
background: rgba(255, 255, 255, 0.94);
30403047
color: rgba(31, 39, 64, 0.76);
30413048
font-size: 10px;
30423049
letter-spacing: 0.01em;
30433050
white-space: nowrap;
3051+
border: 1px solid rgba(217, 224, 242, 0.88);
30443052
box-shadow: 0 10px 22px rgba(67, 82, 117, 0.08);
30453053
opacity: 0;
3046-
transition: opacity 160ms ease, transform 160ms ease;
3054+
transition: opacity 160ms ease;
3055+
pointer-events: none;
30473056
}
30483057

30493058
.graph-node.is-active .graph-node__badge,
30503059
.graph-node.is-selected .graph-node__badge {
30513060
opacity: 1;
3052-
transform: translate(-50%, 0);
30533061
}
30543062

30553063
.graph-node.is-muted .graph-node__title {
@@ -3100,33 +3108,45 @@ button {
31003108
}
31013109

31023110
.graph-node--anchor-right .graph-node__title {
3103-
left: calc(100% + 10px);
3111+
left: calc(100% + 14px);
31043112
top: 50%;
31053113
transform: translateY(-50%);
31063114
text-align: left;
31073115
}
31083116

31093117
.graph-node--anchor-left .graph-node__title {
3110-
right: calc(100% + 10px);
3118+
right: calc(100% + 14px);
31113119
top: 50%;
31123120
transform: translateY(-50%);
31133121
text-align: right;
31143122
}
31153123

31163124
.graph-node--anchor-top .graph-node__title {
31173125
left: 50%;
3118-
bottom: calc(100% + 10px);
3126+
bottom: calc(100% + 18px);
31193127
transform: translateX(-50%);
31203128
text-align: center;
31213129
}
31223130

31233131
.graph-node--anchor-bottom .graph-node__title {
31243132
left: 50%;
3125-
top: calc(100% + 10px);
3133+
top: calc(100% + 18px);
31263134
transform: translateX(-50%);
31273135
text-align: center;
31283136
}
31293137

3138+
.graph-node--badge-top .graph-node__badge {
3139+
left: 50%;
3140+
bottom: calc(100% + 10px);
3141+
transform: translateX(-50%);
3142+
}
3143+
3144+
.graph-node--badge-bottom .graph-node__badge {
3145+
left: 50%;
3146+
top: calc(100% + 10px);
3147+
transform: translateX(-50%);
3148+
}
3149+
31303150
.graph-overlay {
31313151
position: absolute;
31323152
left: 18px;
@@ -4077,11 +4097,11 @@ button {
40774097
}
40784098

40794099
.graph-node__title {
4080-
width: 126px;
4081-
max-width: 126px;
4100+
width: 120px;
4101+
max-width: 120px;
40824102
font-size: 11px;
40834103
font-weight: 700;
4084-
line-height: 1.18;
4104+
line-height: 1.16;
40854105
letter-spacing: -0.01em;
40864106
white-space: normal;
40874107
}

0 commit comments

Comments
 (0)