Add struct field visualization to the editor message hierarchy tree visualization on the website (#2917)

* Fix Message Tree: Enforce Structure and Visibility

* Code review

* fix the erroreous ouputs

* error handling for MessageHandler

* Fix website visualization HTML generation

* error handling for tuple-style message enum variant

* cleanup

* Update messages

* Normalize BroadcastEvent

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Mohd Mohsin
2025-08-19 09:34:29 +05:30
committed by GitHub
parent 5ed45ead6f
commit 17d70dc60e
59 changed files with 988 additions and 506 deletions

View File

@@ -57,31 +57,37 @@ function buildHtmlList(nodes, currentIndex, currentLevel) {
continue;
}
const hasChildren = (i + 1 < nodes.length) && (nodes[i + 1].level > node.level);
const hasDirectChildren = i + 1 < nodes.length && nodes[i + 1].level > node.level;
const hasDeeperChildren = hasDirectChildren && i + 2 < nodes.length && nodes[i + 2].level > nodes[i + 1].level;
const linkHtml = node.link ? `<a href="${node.link}" target="_blank">${path.basename(node.link)}</a>` : "";
const fieldPieces = node.text.match(/([^:]*):(.*)/);
const partOfMessageFromNamingConvention = ["Message", "MessageHandler", "MessageContext"].some((suffix) => node.text.replace(/(.*)<.*>/g, "$1").endsWith(suffix));
const partOfMessageViolatesNamingConvention = node.link && !partOfMessageFromNamingConvention;
const partOfMessage = node.link ? "subsystem" : "";
const messageParent = (hasChildren && !node.link) ? " submessage": "";
const violatesNamingConvention = partOfMessageViolatesNamingConvention ? "<span class=\"warn\">(violates naming convention — should end with 'Message', 'MessageHandler', or 'MessageContext')</span>" : "";
let escapedText;
if (fieldPieces && fieldPieces.length === 3) {
escapedText = [escapeHtml(fieldPieces[1].trim()), escapeHtml(fieldPieces[2].trim())];
} else {
escapedText = [escapeHtml(node.text)];
}
let role = "message";
if (node.link) role = "subsystem";
else if (hasDeeperChildren) role = "submessage";
else if (escapedText.length === 2) role = "field";
if (hasChildren) {
html += `<li><span class="tree-node"><span class="${partOfMessage}${messageParent}">${escapedText}</span>${linkHtml}${violatesNamingConvention}</span>`;
const partOfMessageFromNamingConvention = ["Message", "MessageHandler", "MessageContext"].some((suffix) => node.text.replace(/(.*)<.*>/g, "$1").endsWith(suffix));
const partOfMessageViolatesNamingConvention = node.link && !partOfMessageFromNamingConvention;
const violatesNamingConvention = partOfMessageViolatesNamingConvention ? "<span class=\"warn\">(violates naming convention — should end with 'Message', 'MessageHandler', or 'MessageContext')</span>" : "";
if (hasDirectChildren) {
html += `<li><span class="tree-node"><span class="${role}">${escapedText}</span>${linkHtml}${violatesNamingConvention}</span>`;
const childResult = buildHtmlList(nodes, i + 1, node.level + 1);
html += `<div class="nested">${childResult.html}</div></li>\n`;
i = childResult.nextIndex;
} else if (escapedText.length === 2) {
html += `<li><span class="tree-leaf field">${escapedText[0]}</span><span>: ${escapedText[1]}</span>${linkHtml}</li>\n`;
} else if (role === "field") {
html += `<li><span class="tree-leaf field">${escapedText[0]}</span>: <span>${escapedText[1]}</span>${linkHtml}</li>\n`;
i++;
} else {
html += `<li><span class="tree-leaf${partOfMessage}">${escapedText[0]}</span>${linkHtml}${violatesNamingConvention}</li>\n`;
html += `<li><span class="tree-leaf ${role}">${escapedText[0]}</span>${linkHtml}${violatesNamingConvention}</li>\n`;
i++;
}
}

View File

@@ -30,10 +30,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><polygon fill="%2316323f" points="4,0 1,0 6,5 1,10 4,10 9,5 4,0" /></svg>\
');
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
margin: calc((1.5em - 10px) / 2) auto;
width: 10px;
height: 10px;
}
@@ -41,34 +39,10 @@
&.expanded::before {
transform: rotate(90deg);
}
a {
margin-left: 12px;
color: var(--color-crimson);
font-size: 12px;
font-family: Arial, sans-serif;
position: relative;
&:hover::after {
content: "";
margin-left: 4px;
position: absolute;
}
}
}
.tree-leaf {
margin-left: calc(10px + 8px);
&.field {
padding-left: 4px;
color: var(--color-storm);
}
&:not(.field) {
padding: 0 4px;
background: var(--color-fog);
}
}
.nested {
@@ -80,6 +54,7 @@
}
.warn {
display: inline;
margin-left: 12px;
color: var(--color-flamingo);
font-family: Arial, sans-serif;
@@ -87,6 +62,20 @@
text-decoration: none;
font-style: italic;
}
a {
margin-left: 12px;
color: var(--color-crimson);
font-size: 12px;
font-family: Arial, sans-serif;
position: relative;
&:hover::after {
content: "";
margin-left: 4px;
position: absolute;
}
}
}
.subsystem,
@@ -94,13 +83,27 @@
font-family: monospace;
line-height: 1.5;
padding: 0 4px;
&.subsystem {
color: #ffffff;
background: var(--color-crimson);
}
&.submessage {
background: var(--color-mustard);
}
}
.subsystem {
color: #ffffff;
background: var(--color-storm);
.message {
padding: 0 4px;
background: var(--color-fog);
}
.submessage {
background: var(--color-lilac);
.field {
padding-left: 4px;
color: #8887c0;
+ span {
color: #457297;
}
}