Skip to content

Commit 58528f4

Browse files
committed
Add resizable node data viewer dialog
- add a new node data viewer dialog component wired into app shell - expose a View Data action in the node inspector for selected nodes - detect and render attached/generated payloads across cachedData, lastOutput, and data-like fields - support JSON/object rendering and CSV table previews in a resizable modal
1 parent e89b4bf commit 58528f4

5 files changed

Lines changed: 580 additions & 0 deletions

File tree

css/panels.css

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,10 @@
454454
margin-top: 0.42rem;
455455
}
456456

457+
.inspector-summary-actions {
458+
justify-content: flex-start;
459+
}
460+
457461
.inspector-port-card {
458462
border: 1px dashed #d0deef;
459463
border-radius: 10px;
@@ -487,6 +491,160 @@
487491
word-break: break-word;
488492
}
489493

494+
.node-data-viewer-dialog {
495+
width: min(84vw, 1000px);
496+
height: min(74vh, 760px);
497+
min-width: 420px;
498+
min-height: 280px;
499+
max-width: 95vw;
500+
max-height: 90vh;
501+
resize: both;
502+
overflow: hidden;
503+
border: 1px solid #cfdced;
504+
border-radius: 14px;
505+
padding: 0;
506+
box-shadow: 0 28px 72px -40px rgba(16, 35, 62, 0.62);
507+
background: #f8fbff;
508+
}
509+
510+
.node-data-viewer-dialog::backdrop {
511+
background: rgba(14, 28, 49, 0.36);
512+
}
513+
514+
.node-data-viewer-surface {
515+
height: 100%;
516+
display: grid;
517+
grid-template-rows: auto auto 1fr;
518+
}
519+
520+
.node-data-viewer-header {
521+
display: flex;
522+
align-items: flex-start;
523+
justify-content: space-between;
524+
gap: 1rem;
525+
padding: 0.9rem 1rem 0.75rem;
526+
border-bottom: 1px solid #d9e5f3;
527+
background: linear-gradient(180deg, #fcfeff 0%, #f3f8ff 100%);
528+
}
529+
530+
.node-data-viewer-header h3 {
531+
margin: 0;
532+
font-size: 0.92rem;
533+
letter-spacing: 0.01em;
534+
}
535+
536+
.node-data-viewer-header p {
537+
margin: 0.22rem 0 0;
538+
font-size: 0.78rem;
539+
color: #5e7390;
540+
}
541+
542+
.node-data-viewer-header button {
543+
border: 1px solid #c8d7ea;
544+
background: #ffffff;
545+
color: #1f3550;
546+
border-radius: 9px;
547+
padding: 0.34rem 0.6rem;
548+
cursor: pointer;
549+
font-weight: 600;
550+
}
551+
552+
.node-data-viewer-controls {
553+
padding: 0.7rem 1rem;
554+
border-bottom: 1px solid #dfe9f6;
555+
background: #f7fbff;
556+
}
557+
558+
.node-data-viewer-controls label {
559+
display: grid;
560+
gap: 0.34rem;
561+
max-width: 420px;
562+
}
563+
564+
.node-data-viewer-controls span {
565+
font-size: 0.72rem;
566+
letter-spacing: 0.04em;
567+
text-transform: uppercase;
568+
color: #5f748f;
569+
font-weight: 700;
570+
}
571+
572+
.node-data-viewer-controls select {
573+
border: 1px solid #cfdced;
574+
border-radius: 9px;
575+
padding: 0.42rem 0.5rem;
576+
background: #ffffff;
577+
}
578+
579+
.node-data-viewer-body {
580+
padding: 0.75rem 1rem 1rem;
581+
overflow: auto;
582+
display: grid;
583+
gap: 0.62rem;
584+
align-content: flex-start;
585+
}
586+
587+
.node-data-viewer-empty {
588+
margin: 0;
589+
font-size: 0.82rem;
590+
color: #5f728d;
591+
}
592+
593+
.node-data-viewer-meta {
594+
margin: 0;
595+
font-size: 0.76rem;
596+
color: #516987;
597+
}
598+
599+
.node-data-viewer-code {
600+
margin: 0;
601+
border: 1px solid #d9e5f4;
602+
border-radius: 10px;
603+
background: #ffffff;
604+
padding: 0.6rem 0.64rem;
605+
font-family: var(--font-mono);
606+
font-size: 0.75rem;
607+
line-height: 1.42;
608+
overflow: auto;
609+
white-space: pre;
610+
}
611+
612+
.node-data-viewer-table-wrap {
613+
overflow: auto;
614+
border: 1px solid #d9e4f3;
615+
border-radius: 10px;
616+
background: #ffffff;
617+
}
618+
619+
.node-data-viewer-table {
620+
border-collapse: collapse;
621+
width: 100%;
622+
min-width: 360px;
623+
font-size: 0.74rem;
624+
}
625+
626+
.node-data-viewer-table th,
627+
.node-data-viewer-table td {
628+
border-bottom: 1px solid #edf2f9;
629+
border-right: 1px solid #edf2f9;
630+
text-align: left;
631+
padding: 0.35rem 0.42rem;
632+
vertical-align: top;
633+
white-space: nowrap;
634+
}
635+
636+
.node-data-viewer-table th {
637+
position: sticky;
638+
top: 0;
639+
background: #eff5ff;
640+
color: #344f71;
641+
}
642+
643+
.node-data-viewer-table th:last-child,
644+
.node-data-viewer-table td:last-child {
645+
border-right: 0;
646+
}
647+
490648
.inspector-list {
491649
margin: 0;
492650
padding-left: 1.1rem;
@@ -1703,6 +1861,61 @@
17031861
color: #d8e9fc;
17041862
}
17051863

1864+
:root[data-theme="dark"] .node-data-viewer-dialog {
1865+
border-color: #2e4460;
1866+
background: #121d2b;
1867+
box-shadow: 0 28px 72px -40px rgba(0, 0, 0, 0.85);
1868+
}
1869+
1870+
:root[data-theme="dark"] .node-data-viewer-dialog::backdrop {
1871+
background: rgba(0, 0, 0, 0.52);
1872+
}
1873+
1874+
:root[data-theme="dark"] .node-data-viewer-header {
1875+
border-bottom-color: #2f4765;
1876+
background: linear-gradient(180deg, #162536 0%, #132234 100%);
1877+
}
1878+
1879+
:root[data-theme="dark"] .node-data-viewer-header p,
1880+
:root[data-theme="dark"] .node-data-viewer-controls span,
1881+
:root[data-theme="dark"] .node-data-viewer-empty,
1882+
:root[data-theme="dark"] .node-data-viewer-meta {
1883+
color: #9cb4d2;
1884+
}
1885+
1886+
:root[data-theme="dark"] .node-data-viewer-header button {
1887+
border-color: #355172;
1888+
background: #1b2d43;
1889+
color: #d7e8fb;
1890+
}
1891+
1892+
:root[data-theme="dark"] .node-data-viewer-controls {
1893+
border-bottom-color: #314a68;
1894+
background: #152437;
1895+
}
1896+
1897+
:root[data-theme="dark"] .node-data-viewer-controls select {
1898+
border-color: #355171;
1899+
background: #101c2a;
1900+
color: #d7e8fb;
1901+
}
1902+
1903+
:root[data-theme="dark"] .node-data-viewer-code,
1904+
:root[data-theme="dark"] .node-data-viewer-table-wrap {
1905+
border-color: #314a68;
1906+
background: #111e2d;
1907+
}
1908+
1909+
:root[data-theme="dark"] .node-data-viewer-table th,
1910+
:root[data-theme="dark"] .node-data-viewer-table td {
1911+
border-color: #24364c;
1912+
}
1913+
1914+
:root[data-theme="dark"] .node-data-viewer-table th {
1915+
background: #1a2d43;
1916+
color: #c9dcf2;
1917+
}
1918+
17061919
:root[data-theme="dark"] .row-meta,
17071920
:root[data-theme="dark"] .panel-row-meta,
17081921
:root[data-theme="dark"] .panel-row-detail,

js/app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "./components/top-toolbar.js";
33
import "./components/left-tool-palette.js";
44
import "./components/graph-canvas.js";
55
import "./components/inspector-panel.js";
6+
import "./components/node-data-viewer-dialog.js";
67
import "./components/bottom-activity-panel.js";
78
import "./components/pan-event-console.js";
89
import "./components/bottom-panel/messages-view.js";

js/components/app-shell.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class AppShell extends HTMLElement {
1414
</div>
1515
<inspector-panel class="mg-inspector"></inspector-panel>
1616
</main>
17+
<node-data-viewer-dialog></node-data-viewer-dialog>
1718
`;
1819
}
1920
}

js/components/inspector-panel.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ class InspectorPanel extends HTMLElement {
294294
});
295295
}
296296

297+
#bindNodeInspectorActions() {
298+
this.querySelector('[data-action="open-data-viewer"]')?.addEventListener("click", () => {
299+
if (!this.#selectedNodeId) return;
300+
const viewer = document.querySelector("node-data-viewer-dialog");
301+
if (!viewer || typeof viewer.openForNode !== "function") return;
302+
viewer.openForNode(this.#selectedNodeId);
303+
});
304+
}
305+
297306
render() {
298307
if (this.#selectedEdge) {
299308
const edge = this.#selectedEdge;
@@ -452,6 +461,9 @@ class InspectorPanel extends HTMLElement {
452461
? `<p class="inspector-help">${escapeHtml(nodePlan.blockedReasons[0])}</p>`
453462
: ""
454463
}
464+
<div class="inspector-inline-row inspector-summary-actions">
465+
<button type="button" data-action="open-data-viewer" ${node ? "" : "disabled"}>View Data</button>
466+
</div>
455467
</div>
456468
<div class="inspector-tabs" role="tablist" aria-label="Inspector tabs">
457469
${visibleTabs
@@ -476,6 +488,7 @@ class InspectorPanel extends HTMLElement {
476488
const tabEl = document.createElement(activeTag);
477489
tabEl.node = node;
478490
contentEl.append(tabEl);
491+
this.#bindNodeInspectorActions();
479492
}
480493
}
481494

0 commit comments

Comments
 (0)