Make Huginn node preview payload-first

This commit is contained in:
Meta Vens 2026-05-19 10:03:32 +01:00
commit 8f802ea17b
2 changed files with 121 additions and 26 deletions

View file

@ -281,6 +281,17 @@ function InspectionView({
const terrainForces = useHuginnTerrainForces(HUGINN_ART.field);
const record = inspection.records[selection.record];
const catalogEntry = inspection.catalog[selection.catalog];
const expandedRawSelection = graphSelection?.kind === "node"
? graphProjection.nodeSelections.get(graphSelection.nodeId)
: graphSelection?.kind === "edge"
? graphProjection.edgeSelections.get(graphSelection.edgeId)
: undefined;
const expandedRecord = expandedRawSelection?.kind === "record"
? inspection.records[expandedRawSelection.index]
: undefined;
const expandedCatalogEntry = expandedRawSelection?.kind === "catalog"
? inspection.catalog[expandedRawSelection.index]
: undefined;
const expandedNode = graphSelection?.kind === "node"
? {
graphKey: graphSelection.graphKey,
@ -289,9 +300,9 @@ function InspectionView({
ariaLabel: "Selected CultCache node",
content: (
<ExpandedNodePanel
catalogEntry={catalogEntry}
catalogEntry={expandedCatalogEntry}
inspection={inspection}
record={record}
record={expandedRecord}
selection={selection}
/>
),
@ -529,31 +540,38 @@ function ExpandedNodePanel({
record: InspectedRecord | undefined;
selection: Selection;
}) {
const title = record?.key ?? catalogEntry?.schemaName ?? inspection.filePath;
return (
<article className="expanded-content">
<div className="expanded-kicker">CultCache Node</div>
<header className="expanded-header">
<h1>{record?.key ?? catalogEntry?.schemaName ?? inspection.filePath}</h1>
<h1>{title}</h1>
<div className="expanded-chips">
<span>{inspection.format}</span>
<span>{inspection.records.length} records</span>
<span>{inspection.catalog.length} schemas</span>
{record ? <span>{record.schemaName}</span> : <span>{inspection.records.length} records</span>}
{record ? <span>{record.payloadBytes} bytes</span> : <span>{inspection.catalog.length} schemas</span>}
</div>
</header>
<section className="expanded-summary">
{record ? (
<>
<p>{record.schemaName} record stored at {record.storedAt} with {record.payloadBytes} payload bytes.</p>
<p>{record.schemaName} record stored at {record.storedAt}.</p>
{record.payloadDecodeError ? <div className="error">{record.payloadDecodeError}</div> : null}
</>
) : (
<p>{inspection.filePath} contains a schema catalog and persisted MessagePack record set.</p>
)}
</section>
{record ? (
<section className="expanded-payload">
<h2>Payload</h2>
<RecordPayload record={record} />
</section>
) : null}
<section className="expanded-article">
<div>
<h2>Record Payload</h2>
{record ? <RecordDetail record={record} /> : <div className="empty">Select a record node to inspect payload data.</div>}
<h2>Record Metadata</h2>
{record ? <RecordFacts record={record} /> : <div className="empty">Select a record node to inspect payload data.</div>}
</div>
<div>
<h2>Schema Entry</h2>
@ -608,19 +626,31 @@ function CatalogButton({
function RecordDetail({ record }: { record: InspectedRecord }) {
return (
<>
<Facts rows={[
["Key", record.key],
["Schema", record.schemaName],
["Schema ID", record.schemaId],
["Stored", record.storedAt],
["Payload", `${record.payloadBytes} bytes`],
]} />
<RecordFacts record={record} />
{record.payloadDecodeError ? <div className="error">{record.payloadDecodeError}</div> : null}
<pre>{JSON.stringify(record.payloadPreview, null, 2)}</pre>
<RecordPayload record={record} />
</>
);
}
function RecordFacts({ record }: { record: InspectedRecord }) {
return (
<Facts rows={[
["Key", record.key],
["Schema", record.schemaName],
["Schema ID", record.schemaId],
["Stored", record.storedAt],
["Payload", `${record.payloadBytes} bytes`],
]} />
);
}
function RecordPayload({ record }: { record: InspectedRecord }) {
return (
<pre className="payload-code">{JSON.stringify(record.payloadPreview, null, 2)}</pre>
);
}
function CatalogDetail({ entry }: { entry: InspectedCatalogEntry }) {
return (
<>

View file

@ -578,6 +578,19 @@ pre {
line-height: 1.55;
}
.expanded-payload {
display: grid;
gap: 8px;
min-width: 0;
min-height: 0;
}
.expanded-payload h2 {
margin: 0;
color: #f8fdff;
font-size: 18px;
}
.expanded-article {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@ -600,46 +613,98 @@ pre {
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-content {
display: flex;
display: grid;
align-items: center;
gap: 12px;
justify-items: center;
gap: 6px;
padding: 10px 14px;
text-align: center;
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-kicker,
.huginn-expanded-node[data-node-stage="summary"] .expanded-summary,
.huginn-expanded-node[data-node-stage="summary"] .expanded-article,
.huginn-expanded-node[data-node-stage="summary"] .expanded-footer,
.huginn-expanded-node[data-node-stage="summary"] .expanded-chips {
.huginn-expanded-node[data-node-stage="summary"] .expanded-footer {
display: none;
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-header {
display: grid;
justify-items: center;
min-width: 0;
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-header h1 {
overflow: hidden;
max-width: 100%;
font-size: 1.02rem;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-chips {
justify-content: center;
}
.huginn-expanded-node[data-node-stage="summary"] .expanded-payload {
display: none;
}
.huginn-expanded-node[data-node-stage="preview"] {
overflow: hidden;
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-article {
grid-template-columns: 1fr;
.huginn-expanded-node[data-node-stage="preview"] .expanded-content {
align-content: center;
justify-items: center;
gap: 10px;
padding: 18px 22px;
text-align: center;
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-article > div:nth-child(2),
.huginn-expanded-node[data-node-stage="preview"] .expanded-header {
justify-items: center;
max-width: min(640px, 100%);
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-header h1 {
max-width: 100%;
font-size: clamp(1.05rem, 2.1vw, 1.7rem);
text-align: center;
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-chips {
justify-content: center;
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-summary,
.huginn-expanded-node[data-node-stage="preview"] .expanded-article,
.huginn-expanded-node[data-node-stage="preview"] .expanded-footer {
display: none;
}
.huginn-expanded-node[data-node-stage="preview"] pre {
max-height: 170px;
.huginn-expanded-node[data-node-stage="preview"] .expanded-payload {
width: min(680px, 100%);
justify-items: center;
}
.huginn-expanded-node[data-node-stage="preview"] .expanded-payload h2 {
color: var(--cyan);
font-size: 12px;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.huginn-expanded-node[data-node-stage="preview"] .payload-code {
width: 100%;
max-height: min(280px, 44vh);
margin: 0;
text-align: left;
}
.huginn-expanded-node[data-node-stage="preview"] dl {
display: none;
}
.huginn-expanded-node[data-node-stage="article"] {