update docs

This commit is contained in:
2026-05-06 11:51:43 -03:00
parent c8bb6c7581
commit 946234eb9e
16 changed files with 1723 additions and 502 deletions

262
docs/graphs/system.svg Normal file
View File

@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: system Pages: 1 -->
<svg width="3430pt" height="767pt"
viewBox="0.00 0.00 3430.00 767.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(43.2 724.2)">
<title>system</title>
<polygon fill="#1e1e2e" stroke="none" points="-43.2,43.2 -43.2,-724.2 3386.51,-724.2 3386.51,43.2 -43.2,43.2"/>
<g id="clust1" class="cluster">
<title>cluster_sender</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="8,-40 8,-329 373.5,-329 373.5,-40 8,-40"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-311.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Sender machine — Wayland, VAAPI GPU</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_net</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="416.5,-37 416.5,-333 815.06,-333 815.06,-37 416.5,-37"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-315.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Network</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_receiver</title>
<polygon fill="#1e1e2e" stroke="#45475a" points="858.06,-8 858.06,-349 3335.31,-349 3335.31,-8 858.06,-8"/>
<text xml:space="preserve" text-anchor="middle" x="2096.69" y="-331.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Receiver (mcrn) — X11, NVENC/NVDEC GPU</text>
</g>
<g id="clust4" class="cluster">
<title>cluster_legend</title>
<polygon fill="#1e1e2e" stroke="#585b70" points="34.24,-337 34.24,-673 347.26,-673 347.26,-337 34.24,-337"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-655.7" font-family="monospace" font-size="14.00" fill="#a6adc8">Legend</text>
</g>
<!-- capture_py -->
<g id="node1" class="node">
<title>capture_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="328.38,-296.2 53.12,-296.2 53.12,-189.8 328.38,-189.8 328.38,-296.2"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-272.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">kmsgrab + PulseAudio</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-255.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-238.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">sender/stream_av.sh</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-221.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg CLI · h264_vaapi · AAC</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-203.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts over TCP</text>
</g>
<!-- net_py -->
<g id="node3" class="node">
<title>net_py</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="730.08,-299.66 548.25,-299.66 501.48,-190.34 683.31,-190.34 730.08,-299.66"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-248.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4444</text>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-231.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">mpegts</text>
</g>
<!-- capture_py&#45;&gt;net_py -->
<g id="edge1" class="edge">
<title>capture_py&#45;&gt;net_py</title>
<path fill="none" stroke="#cba6f7" d="M328.8,-243.65C388.75,-243.93 457.83,-244.26 513.09,-244.52"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="512.77,-248.02 522.79,-244.57 512.8,-241.02 512.77,-248.02"/>
</g>
<!-- capture_rs -->
<g id="node2" class="node">
<title>capture_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="365.5,-154.2 16,-154.2 16,-47.8 365.5,-47.8 365.5,-154.2"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-130.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;client (Rust)</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-113.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-96.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">media/client/</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-79.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg subprocess (subprocess backend)</text>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-61.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">NUT demux → mpsc → WirePacket TCP</text>
</g>
<!-- net_rs -->
<g id="node4" class="node">
<title>net_rs</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="807.06,-154.66 502.78,-154.66 424.5,-45.34 728.79,-45.34 807.06,-154.66"/>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-103.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">TCP :4447</text>
<text xml:space="preserve" text-anchor="middle" x="615.78" y="-86.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket framing</text>
</g>
<!-- capture_rs&#45;&gt;net_rs -->
<g id="edge5" class="edge">
<title>capture_rs&#45;&gt;net_rs</title>
<path fill="none" stroke="#89b4fa" d="M365.95,-100.59C394.5,-100.52 423.99,-100.45 452.24,-100.38"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="451.91,-103.89 461.91,-100.36 451.9,-96.89 451.91,-103.89"/>
</g>
<!-- recorder_py -->
<g id="node5" class="node">
<title>recorder_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1141.31,-315.83 866.06,-315.83 866.06,-192.17 1141.31,-192.17 1141.31,-315.83"/>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-292.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">StreamRecorder (Python)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-275.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-257.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/stream/recorder.py</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-240.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">ffmpeg listener · TCP receive</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-223.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 writer · UDP relay</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-206.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">stdout&#45;pipe scene detect</text>
</g>
<!-- net_py&#45;&gt;recorder_py -->
<g id="edge2" class="edge">
<title>net_py&#45;&gt;recorder_py</title>
<path fill="none" stroke="#cba6f7" d="M707.75,-247.12C751.47,-248.14 805,-249.39 854.39,-250.54"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="854.26,-254.04 864.34,-250.77 854.43,-247.04 854.26,-254.04"/>
</g>
<!-- recorder_rs -->
<g id="node6" class="node">
<title>recorder_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="1124.81,-156.45 882.56,-156.45 882.56,-15.55 1124.81,-15.55 1124.81,-156.45"/>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-133.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht&#45;server (Rust)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-115.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-98.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">media/server/</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-81.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">WirePacket router</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-64.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 + UDP relay (ffmpeg)</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-46.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">ADTS audio writer</text>
<text xml:space="preserve" text-anchor="middle" x="1003.69" y="-29.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix&#45;socket scene relay</text>
</g>
<!-- net_rs&#45;&gt;recorder_rs -->
<g id="edge6" class="edge">
<title>net_rs&#45;&gt;recorder_rs</title>
<path fill="none" stroke="#89b4fa" d="M764.6,-94.64C799.57,-93.37 836.63,-92.02 870.83,-90.78"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="870.94,-94.28 880.8,-90.42 870.68,-87.29 870.94,-94.28"/>
</g>
<!-- processor -->
<g id="node7" class="node">
<title>processor</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1548.81,-219.45 1281.81,-219.45 1281.81,-78.55 1548.81,-78.55 1548.81,-219.45"/>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-196.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">SessionProcessor (Python)</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-178.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-161.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/stream/processor.py</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-144.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">fMP4 → audio.wav (ffmpeg)</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-127.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">chunked WAVs for transcribe</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-109.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">[Rust mode: scene detect via</text>
<text xml:space="preserve" text-anchor="middle" x="1415.31" y="-92.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">Unix socket → ffmpeg pipe]</text>
</g>
<!-- recorder_py&#45;&gt;processor -->
<g id="edge4" class="edge">
<title>recorder_py&#45;&gt;processor</title>
<path fill="none" stroke="#cba6f7" d="M1141.66,-218.89C1183.07,-208.27 1228.67,-196.59 1270.52,-185.86"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="1271.36,-189.26 1280.18,-183.38 1269.62,-182.48 1271.36,-189.26"/>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-232.1" font-family="monospace" font-size="14.00" fill="#a6adc8">raw scene</text>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-214.85" font-family="monospace" font-size="14.00" fill="#a6adc8">frames</text>
</g>
<!-- store -->
<g id="node11" class="node">
<title>store</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="2388.56,-233.45 2385.56,-237.45 2364.56,-237.45 2361.56,-233.45 2113.31,-233.45 2113.31,-92.55 2388.56,-92.55 2388.56,-233.45"/>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-210.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">data/&lt;session_id&gt;/</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-192.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-175.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/recording_*.mp4</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-158.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">stream/audio.aac (Rust mode)</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-141.07" font-family="monospace" font-size="14.00" fill="#cdd6f4">frames/*.jpg + index.json</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-123.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">audio/chunk_*.wav</text>
<text xml:space="preserve" text-anchor="middle" x="2250.94" y="-106.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">transcript.json · thread.json</text>
</g>
<!-- recorder_py&#45;&gt;store -->
<g id="edge3" class="edge">
<title>recorder_py&#45;&gt;store</title>
<path fill="none" stroke="#cba6f7" d="M1141.45,-253.45C1198.97,-253.22 1252.81,-253 1252.81,-253 1252.81,-253 1548.81,-237 1548.81,-237 1548.81,-237 1931.56,-211 1931.56,-211 1931.56,-211 2084.31,-200 2084.31,-200 2084.31,-200 2091.16,-198.47 2102.08,-196.03"/>
<polygon fill="#cba6f7" stroke="#cba6f7" points="2102.69,-199.48 2111.68,-193.89 2101.16,-192.65 2102.69,-199.48"/>
</g>
<!-- recorder_rs&#45;&gt;processor -->
<g id="edge8" class="edge">
<title>recorder_rs&#45;&gt;processor</title>
<path fill="none" stroke="#a6e3a1" stroke-dasharray="5,2" d="M1125.12,-98.86C1188.33,-105.61 1252.81,-112.5 1252.81,-112.5 1252.81,-112.5 1259.59,-114.03 1270.37,-116.47"/>
<polygon fill="#a6e3a1" stroke="#a6e3a1" points="1269.31,-119.82 1279.84,-118.61 1270.85,-112.99 1269.31,-119.82"/>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-133.7" font-family="monospace" font-size="14.00" fill="#a6adc8">scene.sock</text>
<text xml:space="preserve" text-anchor="middle" x="1211.56" y="-116.45" font-family="monospace" font-size="14.00" fill="#a6adc8">(H.264)</text>
</g>
<!-- recorder_rs&#45;&gt;store -->
<g id="edge7" class="edge">
<title>recorder_rs&#45;&gt;store</title>
<path fill="none" stroke="#89b4fa" d="M1125.03,-75.14C1199.82,-68.4 1281.81,-61 1281.81,-61 1281.81,-61 1689.31,-59 1689.31,-59 1689.31,-59 1931.56,-59 1931.56,-59 1931.56,-59 2018.95,-87.55 2102.16,-114.73"/>
<polygon fill="#89b4fa" stroke="#89b4fa" points="2101.04,-118.04 2111.63,-117.82 2103.21,-111.39 2101.04,-118.04"/>
</g>
<!-- transcriber -->
<g id="node8" class="node">
<title>transcriber</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="1931.56,-183.2 1689.31,-183.2 1689.31,-76.8 1931.56,-76.8 1931.56,-183.2"/>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-159.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">Transcriber</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-142.57" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-125.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/transcriber/engine.py</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-108.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">faster&#45;whisper · CUDA</text>
<text xml:space="preserve" text-anchor="middle" x="1810.44" y="-90.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">segment grouping</text>
</g>
<!-- processor&#45;&gt;transcriber -->
<g id="edge10" class="edge">
<title>processor&#45;&gt;transcriber</title>
<path fill="none" stroke="#585b70" d="M1549.13,-142.58C1590.57,-140.58 1636.24,-138.37 1677.61,-136.37"/>
<polygon fill="#585b70" stroke="#585b70" points="1677.54,-139.88 1687.36,-135.9 1677.2,-132.89 1677.54,-139.88"/>
<text xml:space="preserve" text-anchor="middle" x="1619.06" y="-144.59" font-family="monospace" font-size="14.00" fill="#a6adc8">WAV chunks</text>
</g>
<!-- transcriber&#45;&gt;store -->
<g id="edge11" class="edge">
<title>transcriber&#45;&gt;store</title>
<path fill="none" stroke="#585b70" d="M1931.84,-139.06C1984.38,-143.02 2046.5,-147.69 2101.83,-151.85"/>
<polygon fill="#585b70" stroke="#585b70" points="2101.42,-155.33 2111.66,-152.59 2101.95,-148.35 2101.42,-155.33"/>
<text xml:space="preserve" text-anchor="middle" x="2022.44" y="-154.38" font-family="monospace" font-size="14.00" fill="#a6adc8">transcript.json</text>
</g>
<!-- gui -->
<g id="node9" class="node">
<title>gui</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="2870.31,-244.83 2578.56,-244.83 2578.56,-121.17 2870.31,-121.17 2870.31,-244.83"/>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-221.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">Mitus GUI (GTK4 + libadwaita)</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-204.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-186.95" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/window.py · cht/ui/*</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-169.7" font-family="monospace" font-size="14.00" fill="#cdd6f4">Monitor (mpv UDP) · Scrub bar</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-152.45" font-family="monospace" font-size="14.00" fill="#cdd6f4">Frames panel · Transcript panel</text>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-135.2" font-family="monospace" font-size="14.00" fill="#cdd6f4">Agent input/output</text>
</g>
<!-- agent -->
<g id="node10" class="node">
<title>agent</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="3327.31,-161.2 3010.81,-161.2 3010.81,-54.8 3327.31,-54.8 3327.31,-161.2"/>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-137.82" font-family="monospace" font-size="14.00" fill="#cdd6f4">Agent runner</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-120.58" font-family="monospace" font-size="14.00" fill="#cdd6f4">─────────────</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-103.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">cht/agent/*</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-86.08" font-family="monospace" font-size="14.00" fill="#cdd6f4">Claude SDK · OpenAI/Groq</text>
<text xml:space="preserve" text-anchor="middle" x="3169.06" y="-68.83" font-family="monospace" font-size="14.00" fill="#cdd6f4">@F frame refs · @T transcript refs</text>
</g>
<!-- gui&#45;&gt;agent -->
<g id="edge13" class="edge">
<title>gui&#45;&gt;agent</title>
<path fill="none" stroke="#585b70" d="M2870.68,-158.39C2911.74,-151.43 2956.79,-143.8 2999.13,-136.63"/>
<polygon fill="#585b70" stroke="#585b70" points="2999.64,-140.09 3008.91,-134.97 2998.47,-133.19 2999.64,-140.09"/>
<text xml:space="preserve" text-anchor="middle" x="2940.56" y="-156.17" font-family="monospace" font-size="14.00" fill="#a6adc8">@&#45;mentions</text>
</g>
<!-- agent&#45;&gt;store -->
<g id="edge14" class="edge">
<title>agent&#45;&gt;store</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M3010.47,-96.22C2939.05,-90.89 2870.31,-85.75 2870.31,-85.75 2870.31,-85.75 2578.56,-85.75 2578.56,-85.75 2578.56,-85.75 2486.47,-107.53 2400.08,-127.96"/>
<polygon fill="#585b70" stroke="#585b70" points="2399.43,-124.52 2390.51,-130.23 2401.05,-131.33 2399.43,-124.52"/>
<text xml:space="preserve" text-anchor="middle" x="2724.44" y="-89.7" font-family="monospace" font-size="14.00" fill="#a6adc8">thread.json</text>
</g>
<!-- store&#45;&gt;processor -->
<g id="edge9" class="edge">
<title>store&#45;&gt;processor</title>
<path fill="none" stroke="#585b70" stroke-dasharray="5,2" d="M2113.07,-179.34C2026.78,-189.64 1931.56,-201 1931.56,-201 1931.56,-201 1689.31,-201 1689.31,-201 1689.31,-201 1625.9,-188.92 1560.2,-176.41"/>
<polygon fill="#585b70" stroke="#585b70" points="1561.18,-173.03 1550.7,-174.6 1559.87,-179.91 1561.18,-173.03"/>
</g>
<!-- store&#45;&gt;gui -->
<g id="edge12" class="edge">
<title>store&#45;&gt;gui</title>
<path fill="none" stroke="#585b70" d="M2388.95,-168.81C2444.64,-171.17 2509.32,-173.92 2566.86,-176.36"/>
<polygon fill="#585b70" stroke="#585b70" points="2566.62,-179.85 2576.76,-176.78 2566.92,-172.86 2566.62,-179.85"/>
<text xml:space="preserve" text-anchor="middle" x="2483.56" y="-179.33" font-family="monospace" font-size="14.00" fill="#a6adc8">files + watchers</text>
</g>
<!-- l_py -->
<g id="node12" class="node">
<title>l_py</title>
<polygon fill="#2d2038" stroke="#cba6f7" points="233.5,-382.7 148,-382.7 148,-345.3 233.5,-345.3 233.5,-382.7"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-359.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">Python</text>
</g>
<!-- l_rs -->
<g id="node13" class="node">
<title>l_rs</title>
<polygon fill="#1e2d3e" stroke="#89b4fa" points="225.25,-455.7 156.25,-455.7 156.25,-418.3 225.25,-418.3 225.25,-455.7"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-432.32" font-family="monospace" font-size="14.00" fill="#cdd6f4">Rust</text>
</g>
<!-- l_io -->
<g id="node14" class="node">
<title>l_io</title>
<polygon fill="#1e2a3e" stroke="#89b4fa" points="339.26,-566.41 103.01,-566.41 42.24,-491.59 278.49,-491.59 339.26,-566.41"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-524.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">I/O · network</text>
</g>
<!-- l_fs -->
<g id="node15" class="node">
<title>l_fs</title>
<polygon fill="#2a2a3e" stroke="#585b70" points="250,-639.71 247,-643.71 226,-643.71 223,-639.71 131.5,-639.71 131.5,-602.29 250,-602.29 250,-639.71"/>
<text xml:space="preserve" text-anchor="middle" x="190.75" y="-616.33" font-family="monospace" font-size="14.00" fill="#cdd6f4">filesystem</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB