133 lines
8.5 KiB
HTML
133 lines
8.5 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>xo-websock introspect</title>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
<!-- automatic layout (ELK's layered algorithm); not on cdnjs -->
|
|
<script src="https://cdn.jsdelivr.net/npm/elkjs@0.12.0/lib/elk.bundled.js"></script>
|
|
<style>
|
|
body { font-family: system-ui, sans-serif; margin: 1.5em; color: #222; }
|
|
#status { color: #666; margin-left: 0.8em; }
|
|
#srcinfo { color: #666; font-size: 13px; margin-top: 0.4em; }
|
|
/* the graph's viewport: full width; its height set to fill the window */
|
|
#zoom-level { font-size: 13px; color: #555; margin: 0 0.6em; font-variant-numeric: tabular-nums; }
|
|
/* the legend, on the graph's top-left corner: a swatch (styled as the
|
|
boxes) and short type per colour, top down */
|
|
g.legend > rect.panel { fill: #fff; fill-opacity: 0.85; stroke: #ddd; stroke-width: 1px; rx: 4px; }
|
|
g.legend text { font-size: 12px; font-family: ui-monospace, monospace; fill: #444; }
|
|
g.legend .swatch > rect { rx: 3px; }
|
|
svg#graph { border: 1px solid #ccc; background: #fafafa; margin-top: 1em; display: block;
|
|
width: 100%; box-sizing: border-box; }
|
|
svg#graph.panning { cursor: grab; } /* Shift held: pan / zoom */
|
|
svg#graph.panning:active { cursor: grabbing; }
|
|
svg#graph .node:not(.expandable) { cursor: default; }
|
|
/* .swatch: the legend's sample of each colour -- same rules, not a box */
|
|
:is(.node, .swatch) > rect { stroke-width: 1.5px; rx: 6px; }
|
|
:is(.node, .swatch).server > rect { fill: #e8f0fe; stroke: #4a6fa5; }
|
|
/* one colour per C++ type: every endpoint is a DynamicEndpoint, http or stream */
|
|
:is(.node, .swatch):is(.http, .stream) > rect { fill: #fdf3e1; stroke: #b07d2b; }
|
|
:is(.node, .swatch).session > rect { fill: #f3e8fb; stroke: #7a4fa0; }
|
|
.node.closed > rect { stroke-dasharray: 4 3; opacity: 0.6; }
|
|
:is(.node, .swatch).subscription > rect { fill: #fceef3; stroke: #7a4fa0; } /* pale rose; its session's outline */
|
|
:is(.node, .swatch).sender > rect { fill: #fdf6e3; stroke: #8a7a3a; }
|
|
:is(.node, .swatch).sink > rect { fill: #edf0f5; stroke: #64748b; } /* pale slate: a subscription's WebsocketSink */
|
|
:is(.node, .swatch).nested > rect { fill: #ffffff; stroke: #8a8f98; } /* a nested struct's own box; its type's colour (nested_colour()) overrides */
|
|
.node.nested text { font-size: 12px; }
|
|
rect.group { fill: #f2f3f5; fill-opacity: 0.6; stroke: none; }
|
|
:is(.node, .swatch).receiver > rect { fill: #e2f4f3; stroke: #3a8a86; } /* pale teal: an endpoint's StreamReceiver */
|
|
.node.astray > rect { stroke: #c0392b; stroke-width: 2.5px; }
|
|
.node.sender text { font-size: 12px; }
|
|
.node.subscription text { font-size: 12px; }
|
|
.node.sink text { font-size: 12px; }
|
|
.node.receiver text { font-size: 12px; }
|
|
path.edge { fill: none; stroke: #999; stroke-width: 1.2px; marker-end: url(#arrow-own); }
|
|
/* under each edge, in the drawing sheet's colour (g.extent): where a
|
|
later edge crosses an earlier one, the earlier shows a gap */
|
|
path.casing { fill: none; stroke: #fff; stroke-width: 5px; pointer-events: none; }
|
|
/* the drawing's extent (what Fit / Center place): a white sheet on the
|
|
viewport's grey; its border 1px at any zoom */
|
|
g.extent > rect { fill: #fff; stroke: #dcdcdc; stroke-width: 1px; vector-effect: non-scaling-stroke; }
|
|
.node text { font-size: 14px; font-family: ui-monospace, monospace; }
|
|
/* a box's title, below its type: one size smaller */
|
|
.node text.sub { font-size: 12px; }
|
|
:is(.node.nested, .node.sender, .node.subscription, .node.sink, .node.receiver) text.sub { font-size: 11px; }
|
|
.node.expandable { cursor: pointer; }
|
|
text.kids { font-size: 16px; fill: #555; cursor: pointer; font-family: ui-monospace, monospace; }
|
|
text.kids:hover { fill: #000; }
|
|
.node.expandable:hover > rect { stroke-width: 3px; }
|
|
.node.open > rect { fill-opacity: 0.6; }
|
|
text.row { font-size: 12px; font-family: ui-monospace, monospace; fill: #333; }
|
|
text.row.expandable { cursor: pointer; }
|
|
text.row.error { fill: #aaa; }
|
|
text.row .mname { font-weight: 600; }
|
|
text.row .mtype { fill: #2c5e8f; }
|
|
text.row .mtype.linked { cursor: pointer; text-decoration: underline; }
|
|
text.row .mtag { fill: #888; font-size: 10px; }
|
|
/* a row's triangles: 25% larger; a square behind, as the menu button's */
|
|
text.row .tri { font-size: 125%; fill: #555; cursor: pointer; }
|
|
rect.tbtn { fill: #fff; fill-opacity: 0; cursor: pointer; }
|
|
rect.tbtn.hot { fill-opacity: 0.85; }
|
|
/* a member edge's colour: its ref kind, darker the closer the holder
|
|
holds what it names -- includes black .. refers light grey */
|
|
:root { --edge-includes: #1a1a1a; --edge-owns: #555555; --edge-shares: #8c8c8c; --edge-refers: #c4c4c4; }
|
|
path.edge.member { stroke: var(--edge-refers); stroke-width: 1.4px; marker-end: url(#arrow-refers); }
|
|
/* where a member edge leaves its holder: ■ includes, ◆ owns, ○ shares; refers: none */
|
|
:is(path.edge.member, path.sample).from-includes { stroke: var(--edge-includes); marker-end: url(#arrow-includes); marker-start: url(#start-includes); }
|
|
:is(path.edge.member, path.sample).from-owns { stroke: var(--edge-owns); marker-end: url(#arrow-owns); marker-start: url(#start-owns); }
|
|
:is(path.edge.member, path.sample).from-shares { stroke: var(--edge-shares); marker-end: url(#arrow-shares); marker-start: url(#start-shares); }
|
|
:is(path.edge.member, path.sample).from-refers { stroke: var(--edge-refers); marker-end: url(#arrow-refers); }
|
|
path.edge.member.hot { stroke: #e0730b; stroke-width: 3px; marker-end: url(#arrow-hot); }
|
|
path.edge.member.hot.from-includes { marker-start: url(#start-includes-hot); }
|
|
path.edge.member.hot.from-owns { marker-start: url(#start-owns-hot); }
|
|
path.edge.member.hot.from-shares { marker-start: url(#start-shares-hot); }
|
|
g.legend path.sample { fill: none; stroke-width: 1.4px; } /* an edge kind's sample: its colour and markers */
|
|
text.row.hot .mname { fill: #e0730b; }
|
|
.node:focus { outline: none; }
|
|
.node.leaving { pointer-events: none; } /* fading out: not clickable */
|
|
.mbtn { cursor: pointer; }
|
|
/* plain until hovered; the transparent fill still takes the click */
|
|
.mbtn rect { fill: #fff; fill-opacity: 0; stroke: none; }
|
|
/* hovered: borderless, white, part-transparent -- tinted by the box beneath */
|
|
.mbtn:hover rect { fill: #fff; fill-opacity: 0.85; }
|
|
.mbtn circle { fill: #555; }
|
|
.node:focus > rect { stroke-width: 3px; stroke-dasharray: 3 2; }
|
|
#ctxmenu { position: absolute; z-index: 10; min-width: 12em; background: #fff;
|
|
border: 1px solid #bbb; border-radius: 6px; padding: 4px 0;
|
|
box-shadow: 0 4px 14px rgba(0,0,0,0.18); font-size: 13px; }
|
|
#ctxmenu .ctxhead { padding: 4px 12px 6px; color: #666; font-family: ui-monospace, monospace;
|
|
font-size: 12px; border-bottom: 1px solid #eee; margin-bottom: 3px; }
|
|
#ctxmenu button { display: block; width: 100%; text-align: left; border: 0; background: none;
|
|
padding: 5px 12px; font: inherit; cursor: pointer; }
|
|
#ctxmenu button:hover:enabled, #ctxmenu button:focus { background: #e8f0fe; outline: none; }
|
|
#ctxmenu button:disabled { color: #aaa; cursor: default; }
|
|
pre { background: #f4f4f4; padding: 0.8em; max-width: 60em; overflow-x: auto; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>xo-websock introspect</h1>
|
|
<div>
|
|
<button id="refresh" disabled>Refresh</button>
|
|
<button id="show-all" title="show every box's children">Show all</button>
|
|
<button id="hide-all" title="hide every box's children: only the Webserver">Hide all</button>
|
|
<button id="fit" title="the whole graph in view">Fit</button>
|
|
<button id="center" title="the graph's centre at the viewport's centre, at the current zoom">Center</button>
|
|
<span id="zoom-level" title="the graph's magnification: Shift + wheel zooms the graph, Shift + drag its background pans">zoom 100%</span>
|
|
<label title="draw the legend: box colours and edge kinds"><input type="checkbox" id="show-legend" checked> legend</label>
|
|
<span id="status">connecting...</span>
|
|
</div>
|
|
<div id="srcinfo">source links: (loading)</div>
|
|
|
|
<svg id="graph"></svg>
|
|
|
|
<div id="ctxmenu" role="menu" hidden></div>
|
|
|
|
<h3 id="detail-h" hidden></h3>
|
|
<pre id="detail" hidden></pre>
|
|
|
|
<h3>last frame</h3>
|
|
<pre id="raw">(none yet)</pre>
|
|
|
|
<script src="introspect.js"></script>
|
|
</body>
|
|
</html>
|