xo-umbrella2/xo-websock/example/introspect/mount-origin/index.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>