figdown 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/README.md +85 -0
- package/dist/figdown.js +3756 -361
- package/dist/figdown.mjs +3756 -361
- package/examples/evpn-fabric.svg +1 -1
- package/examples/showcase/arp-resolution.svg +1 -1
- package/examples/showcase/ethernet-frame.svg +1 -1
- package/examples/showcase/l2-forwarding-logic.svg +1 -1
- package/examples/showcase/tcp-handshake.svg +1 -1
- package/examples/showcase/tcp-header.svg +1 -1
- package/examples/showcase/tcp-state-machine.svg +53 -24
- package/guide/expressing.md +25 -0
- package/guide/showcase.md +12 -12
- package/integrations/mcp-server/server.js +50 -4
- package/package.json +4 -2
- package/skill/README.md +130 -0
- package/skill/figdown/SKILL.md +49 -9
- package/skill/figdown/figdown.html +6408 -856
- package/skill/figdown/reference/experimental/flowchart.md +23 -0
- package/skill/figdown/reference/experimental/sequence.md +28 -0
- package/skill/figdown/reference/experimental/statechart.md +29 -0
- package/skill/figdown/reference/experimental/topology.md +42 -0
- package/skill/figdown/reference/scene.md +23 -0
- package/skill/figdown/reference/transcribe.md +4 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 902 984" width="902" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="92" x1="433.1" y1="76" x2="433.1" y2="190" stroke="#2563eb" stroke-width="1.6"/><line data-edge="93" x1="452.7652886772431" y1="73.34906513439799" x2="724.2052886772431" y2="307.34906513439796" stroke="#2563eb" stroke-width="1.6"/><line data-edge="94" x1="465.20000000000005" y1="221.63984674329504" x2="700" y2="311.6015325670498" stroke="#2563eb" stroke-width="1.6"/><line data-edge="95" x1="395.5028227438559" y1="228.0864522267111" x2="216.56282274385592" y2="313.1609371712436" stroke="#2563eb" stroke-width="1.6"/><line data-edge="96" x1="700" y1="328" x2="234.39999999999998" y2="328" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="97" x1="700" y1="344.3984674329502" x2="476.58000000000004" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="98" x1="215.06" y1="346" x2="391.74" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="101" x1="426.1" y1="190" x2="426.1" y2="76" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="102" x1="719.6347113227569" y1="312.65093486560204" x2="448.1947113227569" y2="78.65093486560201" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="103" x1="213.5571772561441" y1="306.8390628287564" x2="392.4971772561441" y2="221.76457788422394" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="106" x1="179.4896" y1="346" x2="206.7104" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="107" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="108" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="109" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="110" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="111" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="112" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="113" x1="396.79999999999995" y1="838" x2="697" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="116" x1="472.57846153846157" y1="466" x2="697.0215384615385" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="117" x1="739.003076923077" y1="596" x2="733.796923076923" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="118" x1="732.7723076923077" y1="726" x2="732.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="190" style="cursor:move"><rect x="394" y="190" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="212.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="700" data-y="310" style="cursor:move"><rect x="700" y="310" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="742.8" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="120" data-y="310" style="cursor:move"><rect x="120" y="310" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="177.2" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="690" data-y="560" style="cursor:move"><rect x="690" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="740" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="690" data-y="690" style="cursor:move"><rect x="690" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="697" data-y="820" style="cursor:move"><rect x="697" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="439.1" y="136.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="439.1" y="136.3" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M433.1 190 L427.5 179.92 L438.70000000000005 179.92 z" fill="#2563eb" stroke="none"/><text x="588.485288677243" y="92.14216858267382" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="588.485288677243" y="92.14216858267382" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M724.2052886772431 307.34906513439796 L712.9141343807203 305.008929528901 L720.2270581478983 296.52593795897457 z" fill="#2563eb" stroke="none"/><text x="582.6" y="243.43773946360156" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="582.6" y="243.43773946360156" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M700 311.6015325670498 L688.5836683972988 313.224422808922 L692.5908047969832 302.76579680574537 z" fill="#2563eb" stroke="none"/><text x="306.03282274385595" y="315.42218915222617" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="306.03282274385595" y="315.42218915222617" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M216.56282274385592 313.1609371712436 L223.261805406868 303.7753081949488 L228.0708381872069 313.89030714292835 z" fill="#2563eb" stroke="none"/><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M234.39999999999998 328 L244.48 322.4 L244.48 333.6 z" fill="#9333ea" stroke="none"/><text x="588.29" y="355.29980842911874" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="588.29" y="355.29980842911874" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M476.58000000000004 430 L483.9891952030168 421.1642642386956 L487.99633160270133 431.6228902418722 z" fill="#2563eb" stroke="none"/><text x="303.4" y="354.43700475435816" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="303.4" y="354.43700475435816" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M391.74 430 L380.231984556649 430.7293699716848 L385.041017336988 420.61437102370525 z" fill="#2563eb" stroke="none"/><text x="303.1" y="136.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="303.1" y="136.3" font-size="11" text-anchor="start" fill="#9333ea">CLOSE / delete TCB</text><path d="M426.1 76 L431.70000000000005 86.08 L420.5 86.08 z" fill="#9333ea" stroke="none"/><text x="524.1979113227569" y="206.95196934836065" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="524.1979113227569" y="206.95196934836065" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M448.1947113227569 78.65093486560201 L459.4858656192797 80.99107047109896 L452.17294185210176 89.47406204102543 z" fill="#9333ea" stroke="none"/><text x="303.0271772561441" y="227.648492305777" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="303.0271772561441" y="227.648492305777" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M392.4971772561441 221.76457788422394 L385.79819459313205 231.1502068605187 L380.9891618127931 221.03520791253922 z" fill="#9333ea" stroke="none"/><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M206.7104 560 L199.88323361107894 550.7071963811783 L210.99371139541864 549.2939436070102 z" fill="#dc2626" stroke="none"/><text x="319.3" y="478.52153218495016" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="478.52153218495016" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="302.0616615384615" y="607.0624362606231" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="302.0616615384615" y="607.0624362606231" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="257.2065230769231" y="815.4656152758133" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="257.2065230769231" y="815.4656152758133" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M697 838 L686.92 843.6 L686.92 832.4 z" fill="#dc2626" stroke="none"/><text x="584.8000000000001" y="484.1105025773196" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="584.8000000000001" y="484.1105025773196" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M697.0215384615385 560 L685.5607329004544 561.2713519933599 L689.8873234537733 550.9407850106658 z" fill="#16a34a" stroke="none"/><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M733.796923076923 690 L728.7629148866224 679.6257452537551 L739.9457765533873 680.2451037460681 z" fill="#16a34a" stroke="none"/><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M732.6276923076923 820 L727.0432066088549 809.9113965545532 L738.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">A state IS a TCP connection state (RFC 9293 §3.3.2); the two CLOSED states are one state drawn twice — only this label says so</text><rect x="0" y="897" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#fff" stroke="#dc2626"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#fff" stroke="#9333ea" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="107a61548ef549f76483c5a2836aaf38e1021f8d9a29b82c48bf199d34aa7858" data-engine-version="0.4.0"><'s
|
|
136
|
+
> gate, ask the three questions again, and then either KEEP the figure where it
|
|
137
|
+
> is with the reason written down, or MOVE it. Confirming and keeping is a
|
|
138
|
+
> result; there is deliberately no lint on any of the three, because a symptom
|
|
139
|
+
> that fires on correct figures is not a rule.
|
|
140
|
+
>
|
|
141
|
+
> - **A hand-written `shape=diamond` with `yes`/`no` edges under a scene
|
|
142
|
+
> genre.** The full row, with the measured reason there is no lint and the
|
|
143
|
+
> two live instances that are right for opposite reasons, is in the
|
|
144
|
+
> [authoring.md pitfall table](authoring.md#field-tested-pitfalls-quick-reference)
|
|
145
|
+
> — read it there rather than twice.
|
|
146
|
+
> - **Ordinal mid-labels — `-[1: SYN]->`, `-[2: SYN-ACK]->`.** These are the
|
|
147
|
+
> sanctioned interim for time order under a scene genre and they are *naming,
|
|
148
|
+
> not semantics* (`MEANING-RECOVERY-SOURCE`; the rows above and the Known-limits entry below say
|
|
149
|
+
> so). Keeping them is the **deliberate-portability** pattern and it is only
|
|
150
|
+
> correct when the document SAYS so: a comment naming `sequence` as the genre
|
|
151
|
+
> not taken (it needs `figdown 0.4`) and stating that the numbers are a
|
|
152
|
+
> convention no parser reads. `examples/showcase/tcp-handshake.fd` is that
|
|
153
|
+
> pattern written out. Unstated ordinals are the failure case.
|
|
154
|
+
> - **Many parallel edges crowding one pair of boxes.** Settled by the boxed
|
|
155
|
+
> note immediately above — *messages between one pair over time* versus
|
|
156
|
+
> *distinct transitions between states* — and in the second case the answer
|
|
157
|
+
> is [layout.md](layout.md), not a new header line.
|
|
158
|
+
|
|
134
159
|
---
|
|
135
160
|
|
|
136
161
|
## Data & format
|
package/guide/showcase.md
CHANGED
|
@@ -334,18 +334,18 @@ class client "Active-close path (typically the client) — calls CLOSE first: F
|
|
|
334
334
|
class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" stroke=#16a34a
|
|
335
335
|
class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" stroke=#9333ea style=dashed
|
|
336
336
|
|
|
337
|
-
state closed "CLOSED"
|
|
338
|
-
state listen "LISTEN"
|
|
339
|
-
state synsent "SYN-SENT"
|
|
340
|
-
state synrcvd "SYN-RECEIVED"
|
|
341
|
-
state estab "ESTABLISHED"
|
|
342
|
-
state fw1 "FIN-WAIT-1"
|
|
343
|
-
state fw2 "FIN-WAIT-2"
|
|
344
|
-
state closing "CLOSING"
|
|
345
|
-
state closewait "CLOSE-WAIT"
|
|
346
|
-
state lastack "LAST-ACK"
|
|
347
|
-
state timewait "TIME-WAIT"
|
|
348
|
-
state closed2 "CLOSED"
|
|
337
|
+
state closed "CLOSED" class=states
|
|
338
|
+
state listen "LISTEN" class=states
|
|
339
|
+
state synsent "SYN-SENT" class=states
|
|
340
|
+
state synrcvd "SYN-RECEIVED" class=states
|
|
341
|
+
state estab "ESTABLISHED" class=states
|
|
342
|
+
state fw1 "FIN-WAIT-1" class=states
|
|
343
|
+
state fw2 "FIN-WAIT-2" class=states
|
|
344
|
+
state closing "CLOSING" class=states
|
|
345
|
+
state closewait "CLOSE-WAIT" class=states
|
|
346
|
+
state lastack "LAST-ACK" class=states
|
|
347
|
+
state timewait "TIME-WAIT" class=states
|
|
348
|
+
state closed2 "CLOSED" class=states
|
|
349
349
|
|
|
350
350
|
flow down
|
|
351
351
|
|
|
@@ -149,6 +149,36 @@ function parseFailed(label, errors) {
|
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
// GEOMETRY-TIME REFUSAL (spec core §8, §8.4). A document whose source is
|
|
153
|
+
// impeccable line by line can still describe a drawing that states something
|
|
154
|
+
// it does not — a `group` band enclosing a non-member, a `pin` covering a node
|
|
155
|
+
// completely. Those come from `render`, not from `parse`, and core §8 makes
|
|
156
|
+
// them cost exactly what a parse error costs: nothing is drawn and no artifact
|
|
157
|
+
// is written. Reporting them under the PARSE FAILED banner would send the
|
|
158
|
+
// author to look for a typo on a line that has none, so they get their own
|
|
159
|
+
// heading and their own remedy sentence.
|
|
160
|
+
function figureRefused(label, errors) {
|
|
161
|
+
return text(
|
|
162
|
+
'FIGURE REFUSED — ' + errors.length + ' geometry-time diagnostic(s)' + (label ? ' in ' + label : '') + '.\n' +
|
|
163
|
+
'The source parses clean; the DRAWING would state something the source does not,\n' +
|
|
164
|
+
'so nothing was rendered and no artifact was written (spec core §8). Each line names\n' +
|
|
165
|
+
'the line you can act on — usually the `pin` that fixed the position.',
|
|
166
|
+
errors.join('\n')
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// WHICH CHANNEL REFUSED. `render()` reports parse errors when there are any and
|
|
171
|
+
// geometry-time diagnostics ONLY when there are none — the two are mutually
|
|
172
|
+
// exclusive by construction, because nothing is rendered for a document that
|
|
173
|
+
// does not parse. So asking the parser is enough to name the channel, and it
|
|
174
|
+
// is asked directly rather than inferred from the message text: a banner keyed
|
|
175
|
+
// on wording would go wrong the first time a message is reworded.
|
|
176
|
+
function refused(label, src, errors) {
|
|
177
|
+
return figdown.parse(src).errors.length
|
|
178
|
+
? parseFailed(label, errors)
|
|
179
|
+
: figureRefused(label, errors);
|
|
180
|
+
}
|
|
181
|
+
|
|
152
182
|
// `source` or `path`, never both, never neither. Returns {src, label, file} or
|
|
153
183
|
// throws a message string.
|
|
154
184
|
function takeSource(args, exts) {
|
|
@@ -172,8 +202,12 @@ function toolBuild(args) {
|
|
|
172
202
|
try { s = takeSource(args, ['.fd']); } catch (m) { return toolError(m); }
|
|
173
203
|
|
|
174
204
|
const opts = args.with_title === true ? { title: true } : undefined;
|
|
205
|
+
// `artifact` refuses on EITHER channel (dist 0.4): a parse error, or
|
|
206
|
+
// a geometry-time diagnostic that only `render` can see. Both give svg=null
|
|
207
|
+
// and a non-empty `errors`, so nothing below can write a picture the engine
|
|
208
|
+
// has already said is wrong.
|
|
175
209
|
const { svg, errors } = figdown.artifact(s.src, opts);
|
|
176
|
-
if (errors.length) return
|
|
210
|
+
if (errors.length) return refused(s.label, s.src, errors);
|
|
177
211
|
|
|
178
212
|
const wrote = args.write === true && s.file
|
|
179
213
|
? s.file.replace(/\.fd$/, '') + '.svg'
|
|
@@ -247,9 +281,13 @@ function toolCheck(args) {
|
|
|
247
281
|
|
|
248
282
|
if (!units.length) return text('ok: true 0 .fd file(s) found under ' + args.path + ' — nothing to check.');
|
|
249
283
|
|
|
284
|
+
// BOTH CHANNELS, because a checker that clears a document `figdown_build`
|
|
285
|
+
// then refuses is a checker that lies. `render` reports parse errors when
|
|
286
|
+
// there are any and geometry-time diagnostics (core §8) otherwise, which is
|
|
287
|
+
// exactly the set that decides whether an artifact can be written.
|
|
250
288
|
const bad = [];
|
|
251
289
|
for (const u of units) {
|
|
252
|
-
const errs = figdown.
|
|
290
|
+
const errs = figdown.render(u.src).errors;
|
|
253
291
|
if (errs.length) bad.push(u.label + ':\n' + errs.map(e => ' ' + e).join('\n'));
|
|
254
292
|
}
|
|
255
293
|
|
|
@@ -291,9 +329,17 @@ function toolRead(args) {
|
|
|
291
329
|
}
|
|
292
330
|
const sidecar = s.file ? s.file.replace(/\.svg$/, '.fd') : null;
|
|
293
331
|
if (sidecar && fs.existsSync(sidecar) && recordedSha) {
|
|
294
|
-
const
|
|
332
|
+
const rebuilt = figdown.artifact(fs.readFileSync(sidecar, 'utf8'));
|
|
333
|
+
const live = rebuilt.svg;
|
|
295
334
|
const liveSha = live && (live.match(/data-sha256="([0-9a-f]{64})"/) || [])[1];
|
|
296
|
-
|
|
335
|
+
// A sidecar the engine now REFUSES cannot be compared, and calling that
|
|
336
|
+
// "stale" would name the wrong defect: the .fd did not drift, it stopped
|
|
337
|
+
// being drawable. Say which it is.
|
|
338
|
+
notes.push(rebuilt.errors.length
|
|
339
|
+
? 'SIDECAR REFUSED: ' + path.basename(sidecar) + ' no longer builds — '
|
|
340
|
+
+ rebuilt.errors.length + ' diagnostic(s), so the recorded hash could not be checked:\n '
|
|
341
|
+
+ rebuilt.errors.join('\n ')
|
|
342
|
+
: liveSha === recordedSha
|
|
297
343
|
? 'sidecar ' + path.basename(sidecar) + ' matches the artifact\'s recorded hash'
|
|
298
344
|
: 'STALE ARTIFACT: ' + path.basename(sidecar) + ' has changed since this .svg was built. '
|
|
299
345
|
+ 'The .fd is truth — rebuild.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "figdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A missing edge still looks fine. Text doesn't. A closed, deterministic figure language for Markdown whose source states the meaning, so the next reader can check the figure instead of only looking at it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "FigDown <hello@figdown.org>",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"gate:page": "node tools/page-check.js",
|
|
33
33
|
"gate:shape": "node tools/shape-check.js --strict",
|
|
34
34
|
"gate:strip": "node tools/strip-check.js --strict",
|
|
35
|
-
"gate:layout": "node tools/layout-lint.js --strict",
|
|
35
|
+
"gate:layout": "node tools/layout-lint.js --strict examples figures tools/layout-lint-fixtures/b66-typed-title",
|
|
36
36
|
"gate:namespace": "node tools/namespace-check.js --strict",
|
|
37
37
|
"gate:artifact": "node tools/artifact-check.js --strict",
|
|
38
38
|
"gate:dist": "node tools/dist-check.js --strict",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"gate:standards": "node tools/standards-check.js --strict",
|
|
49
49
|
"gate:plugin": "node tools/plugin-check.js --strict",
|
|
50
50
|
"gate:mcp": "node integrations/mcp-server/test.js",
|
|
51
|
+
"gate:proof": "node tools/make-proof.js --check",
|
|
52
|
+
"gate:safesvg": "node tools/safe-svg-check.js --strict",
|
|
51
53
|
"gates:list": "node -e \"const s=require(process.cwd()+'/package.json').scripts||{};const g=Object.keys(s).filter(k=>k.startsWith('gate:'));if(!g.length){console.error('gates:list — no gate:* scripts found');process.exit(2);}if(process.env.GATES_JSON)console.log(JSON.stringify(g));else g.forEach(k=>console.log(k+' -> '+s[k]));\"",
|
|
52
54
|
"test": "node -e \"const s=require(process.cwd()+'/package.json').scripts||{};const sp=require('child_process').spawnSync;const g=Object.keys(s).filter(k=>k.startsWith('gate:'));if(!g.length){console.error('npm test — no gate:* scripts found; refusing to report success');process.exit(2);}const bad=[];g.forEach(k=>{const a=s[k].split(' ');const bin=a[0]==='node'?process.execPath:a[0];console.log('');console.log('=== GATE '+k+' ['+s[k]+'] ===');const r=sp(bin,a.slice(1),{stdio:'inherit'});if(r.error)console.error(' spawn error: '+r.error.message);const c=(r.status===null||r.status===undefined)?1:r.status;console.log('--- '+k+': '+(c===0?'PASS':'FAIL (exit '+c+')')+' ---');if(c!==0)bad.push(k+' ['+s[k]+'] exit '+c);});console.log('');console.log('=== GATE SUMMARY: '+(g.length-bad.length)+'/'+g.length+' passed ===');if(bad.length){console.log('FAILED GATES:');bad.forEach(b=>console.log(' '+b));process.exit(1);}console.log('ALL GATES GREEN');\"",
|
|
53
55
|
"gate:reference": "node tools/reference-gate.js"
|
package/skill/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# FigDown agent skill
|
|
2
|
+
|
|
3
|
+
An installable skill that teaches an AI coding agent (Claude Code and
|
|
4
|
+
compatible agent frameworks) to maintain documentation figures with
|
|
5
|
+
FigDown: edit `.fd` sources, build deterministic `.svg` artifacts, and
|
|
6
|
+
embed them in Markdown with the `source:` footer convention.
|
|
7
|
+
|
|
8
|
+
The bundle is self-contained: `SKILL.md`, a `reference/` directory,
|
|
9
|
+
`build-svg.js` (validator/renderer CLI) and `figdown.html` (the engine
|
|
10
|
+
it extracts at runtime — also the editor: open it in any browser).
|
|
11
|
+
|
|
12
|
+
**It is built for progressive disclosure.** `SKILL.md` is the only file
|
|
13
|
+
always loaded, and it holds the workflow, the genre-independent
|
|
14
|
+
language, and a **router**. Everything else is pulled on demand, and
|
|
15
|
+
the routing rule is mechanical: line 1 of a `.fd` names its genre, so
|
|
16
|
+
the agent knows which file it needs before it needs it.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
SKILL.md always loaded
|
|
20
|
+
reference/scene.md genre `block` — its OWN vocabulary
|
|
21
|
+
reference/bitfield.md
|
|
22
|
+
reference/table.md
|
|
23
|
+
reference/layout.md arranging a scene
|
|
24
|
+
reference/reading.md reading a .fd for meaning
|
|
25
|
+
reference/transcribe.md transcribing an existing figure
|
|
26
|
+
reference/experimental/block.md block's EXPERIMENTAL markers and zones
|
|
27
|
+
reference/experimental/chart.md a chart from a table (any genre
|
|
28
|
+
that can host one)
|
|
29
|
+
reference/experimental/topology.md the five EXPERIMENTAL genres, each
|
|
30
|
+
reference/experimental/flowchart.md declaring its OWN vocabulary in
|
|
31
|
+
reference/experimental/statechart.md its own file, so an agent can
|
|
32
|
+
reference/experimental/timing.md ignore the ones it is not using
|
|
33
|
+
reference/experimental/sequence.md — the ladder genre, and the one
|
|
34
|
+
whose load set has no layout file
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`sequence` is the exception to the "genre file + layout file" pairing above:
|
|
38
|
+
both of its axes are declaration order, so nothing in the layout namespace
|
|
39
|
+
moves a mark there and its own file is the whole of what an author needs.
|
|
40
|
+
|
|
41
|
+
**Every scene genre's file is self-sufficient (`SUBJECT-VOCABULARY-SCOPE`).** There is
|
|
42
|
+
no shared scene-vocabulary file and there was never meant to be one: only
|
|
43
|
+
`figdown`, `title` and `layout` are cross-genre by definition, and a word two
|
|
44
|
+
genres spell the same is two independent declarations that happen to agree
|
|
45
|
+
today. So an agent authoring a `topology` figure loads `topology.md` and does
|
|
46
|
+
not have to follow a link into `block`'s file to find out what `bundle` means
|
|
47
|
+
— which is what it used to have to do, and what it found there was written for
|
|
48
|
+
a different domain. The file this list used to call
|
|
49
|
+
`reference/experimental/constructs.md` was that shared file; it was removed
|
|
50
|
+
with the ruling, and its contents went to the genres that own them.
|
|
51
|
+
|
|
52
|
+
The `reference/` files are a generated copy of [`read/0.5/`](../read/0.5/reading.md),
|
|
53
|
+
which is the source of truth; see "Keeping the bundle fresh" below. If you only
|
|
54
|
+
want to READ a `.fd` from this repository, go to `read/0.5/` and install
|
|
55
|
+
nothing.
|
|
56
|
+
|
|
57
|
+
An agent *reading* a figure loads `SKILL.md` + `reference/reading.md`
|
|
58
|
+
and nothing else; an agent authoring a `bitfield` never pays for any
|
|
59
|
+
scene genre's vocabulary. `node tools/skill-coverage.js --strict` is the gate:
|
|
60
|
+
it checks that every registered keyword, option key and enum value is
|
|
61
|
+
taught in its genre's load set, that no retired spelling is taught, and
|
|
62
|
+
that the always-loaded file stays genre-independent.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
Two paths, one bundle. The plugin is a **wrapper**, not a second copy:
|
|
67
|
+
`.claude-plugin/plugin.json` at the repository root declares
|
|
68
|
+
`"skills": "./skill/"`, so the plugin ships exactly the directory documented
|
|
69
|
+
here. Nothing below is duplicated anywhere.
|
|
70
|
+
|
|
71
|
+
**Claude Code plugin (recommended — installs and updates itself):**
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
/plugin marketplace add FigDown/figdown
|
|
75
|
+
/plugin install figdown@figdown
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`/plugin marketplace update figdown` refreshes the catalogue; the plugin is
|
|
79
|
+
pinned to the `version` in its manifest, so a new release arrives when that
|
|
80
|
+
number changes. No submission and no review are involved: the marketplace is
|
|
81
|
+
[`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) in this
|
|
82
|
+
repository.
|
|
83
|
+
|
|
84
|
+
**Copy the directory (no plugin mechanism, every project):**
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
cp -r skill/figdown ~/.claude/skills/figdown
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Copy the directory (no plugin mechanism, one project only):**
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
cp -r skill/figdown <your-repo>/.claude/skills/figdown
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The copy paths remain fully supported: the bundle is self-contained, so a
|
|
97
|
+
`cp` of `skill/figdown` into any skills directory works with no network, no
|
|
98
|
+
repository and no manifest.
|
|
99
|
+
|
|
100
|
+
Then ask the agent for a figure ("draw the ingress datapath as a
|
|
101
|
+
figure in docs/arch.md") — the skill triggers on diagram/figure work
|
|
102
|
+
and follows the FigDown workflow automatically. Requires Node.js for
|
|
103
|
+
the build step; no other dependencies, no network.
|
|
104
|
+
|
|
105
|
+
**Other agent frameworks:** nothing here is Claude-specific except the
|
|
106
|
+
directory convention. `SKILL.md` and `reference/` are ordinary Markdown —
|
|
107
|
+
point the agent at `skill/figdown/SKILL.md` and it follows the same router.
|
|
108
|
+
[guide/agents.md](../guide/agents.md) is the orientation page that leads here;
|
|
109
|
+
it teaches no syntax, because this bundle owns that.
|
|
110
|
+
|
|
111
|
+
## Keeping the bundle fresh
|
|
112
|
+
|
|
113
|
+
`figdown/figdown.html`, `figdown/build-svg.js` and everything under
|
|
114
|
+
`figdown/reference/` are **build artifacts** regenerated from their single
|
|
115
|
+
sources (`editor/figdown.html`, `tools/build-svg.js`, and — since `GENRE-REFERENCE-ADDRESS` —
|
|
116
|
+
[`read/0.5/`](../read/0.5/reading.md)) — never edited by hand:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
node tools/make-skill.js
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`SKILL.md` is the one hand-maintained source left in the bundle;
|
|
123
|
+
`make-skill.js` does not own it and does not delete it.
|
|
124
|
+
|
|
125
|
+
**Why `reference/` is vendored rather than linked.** `read/0.5/` is where a
|
|
126
|
+
reader of this repository is sent — nothing to install. This directory is
|
|
127
|
+
copied out of the repository into `~/.claude/skills/`, so a path pointing back
|
|
128
|
+
at `read/0.5/` would dangle the moment it is installed. The copy is therefore
|
|
129
|
+
generated, and `node tools/skill-coverage.js --strict` fails on any byte of
|
|
130
|
+
difference (check 0, VENDOR). Edit `read/0.5/`, then re-run `make-skill.js`.
|
package/skill/figdown/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: figdown
|
|
3
|
-
description: Draw a figure a human can read and an agent can read too — FigDown .fd text that states the meaning, with a deterministic SVG embedded in Markdown. Use when asked to create, edit, fix, or read diagrams/figures in docs — block and architecture diagrams, topologies, flowcharts, bit-level layouts (packet headers, hardware registers), tables, timing waveforms — or when a .md contains an SVG with a "source: *.fd" footer.
|
|
3
|
+
description: Draw a figure a human can read and an agent can read too — FigDown .fd text that states the meaning, with a deterministic SVG embedded in Markdown. Use when asked to create, edit, fix, or read diagrams/figures in docs — block and architecture diagrams, topologies, flowcharts, state machines, message-sequence exchanges between parties, bit-level layouts (packet headers, hardware registers), tables, timing waveforms — or when a .md contains an SVG with a "source: *.fd" footer.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# FigDown — figures as text, one source, two readers
|
|
@@ -52,6 +52,26 @@ Recover from there. On hash mismatch the `.fd` is truth: rebuild. If the hash
|
|
|
52
52
|
matches but your drawing differs, compare `data-engine-version` — that is the
|
|
53
53
|
other half of "same source → same SVG".
|
|
54
54
|
|
|
55
|
+
## Repair loop, truthfulness and delivery order
|
|
56
|
+
|
|
57
|
+
**Bounded repair loop.** After step 2 fails, keep fixing and rerunning only
|
|
58
|
+
while each attempt's error count is a new minimum — strictly lower than
|
|
59
|
+
every attempt before it. Two consecutive rounds with no improvement mean
|
|
60
|
+
another identical round will not help either: stop, and report the
|
|
61
|
+
remaining errors truthfully instead of trying a third time.
|
|
62
|
+
|
|
63
|
+
**Truthfulness.** A run that still reports errors is never described as a
|
|
64
|
+
success, in any wording. Never delete or weaken meaningful declared content
|
|
65
|
+
— a node, an edge, a field, a row, a signal, whatever the genre calls it —
|
|
66
|
+
merely to make the error count reach zero; removing what the figure states
|
|
67
|
+
is not a repair, it is a smaller, different figure wearing the same
|
|
68
|
+
filename. Once a run passes cleanly, the document is frozen: no further
|
|
69
|
+
edits chasing "one more improvement."
|
|
70
|
+
|
|
71
|
+
**Artifact first.** Write the file, or the fenced block, before explaining
|
|
72
|
+
it. Commentary, reasoning and a description of what changed come after the
|
|
73
|
+
artifact exists — never before it, and never interleaved with it.
|
|
74
|
+
|
|
55
75
|
## When the figure comes from a source
|
|
56
76
|
|
|
57
77
|
Drawing is step 5 of 6. The steps before it decide what is true; the step after
|
|
@@ -67,7 +87,11 @@ everything it produces is a hypothesis until checked.
|
|
|
67
87
|
is not a graph: it is a `table`. Conditions with one outcome per combination is
|
|
68
88
|
a `table`, not a `flowchart` — drawn as a chart it buys crossing edges and
|
|
69
89
|
nothing else. Two mirrored mechanisms are two figures. A run in which most of a
|
|
70
|
-
page's figures become tables is a correct run.
|
|
90
|
+
page's figures become tables is a correct run. **The SHAPES in the source
|
|
91
|
+
drawing are the tempting signal and they carry nothing** — the original author
|
|
92
|
+
picked them by habit, by stencil and by tool default, and no two drawings agree;
|
|
93
|
+
what decides the genre is the ROLES and the QUESTIONS in it, which is what the
|
|
94
|
+
extraction gives you and the picture does not.
|
|
71
95
|
|
|
72
96
|
**3 — Separate what the source states from what you concluded.** Write the
|
|
73
97
|
stated thing; for the rest, leave a `#` comment at the point of doubt and list
|
|
@@ -143,8 +167,21 @@ Two more files answer a **task** rather than a genre:
|
|
|
143
167
|
format → `reference/transcribe.md`.
|
|
144
168
|
|
|
145
169
|
Pick the genre by what the figure IS, not by its subject: the left-hand
|
|
146
|
-
column above says what each is for.
|
|
147
|
-
|
|
170
|
+
column above says what each is for.
|
|
171
|
+
|
|
172
|
+
**Where portability and that answer disagree, the ANSWER decides.** `block`,
|
|
173
|
+
`bitfield` and `table` are the portable three — inside the v0.1 conformance
|
|
174
|
+
surface and its compatibility promise — and taking one of them *against* the
|
|
175
|
+
answer is a trade you STATE, never a default. State it in a `#` comment that
|
|
176
|
+
names the genre you did not write and what the reader must not conclude from
|
|
177
|
+
the one you did. Two different prices hide under the word "portable" and they
|
|
178
|
+
are charged by different rows: `topology` and `flowchart` are dispatchable at
|
|
179
|
+
`figdown 0.1`, so choosing them moves no version and only carries the EXPERIMENTAL
|
|
180
|
+
withdrawal risk; `statechart` and `sequence` move the declared version as well.
|
|
181
|
+
"It must render on any released version" is therefore not an argument for
|
|
182
|
+
leaving the first two. An approximation you do not state is indistinguishable
|
|
183
|
+
from a judgement about what the figure is, so the reader inherits the wrong
|
|
184
|
+
one.
|
|
148
185
|
|
|
149
186
|
**Load the genre file BEFORE you write line 2, not only when something
|
|
150
187
|
fails.** A scene genre may spell the thing and the line with **its own
|
|
@@ -186,11 +223,14 @@ figdown 0.1 block # REQUIRED first significant line; comments and
|
|
|
186
223
|
# A later `figdown 0.1 <genre>` starts a new
|
|
187
224
|
# section with its own genre; one file still
|
|
188
225
|
# renders to one SVG.
|
|
189
|
-
# The VERSION is `0.1` or `0.
|
|
190
|
-
# one that carries what the figure needs
|
|
191
|
-
#
|
|
192
|
-
# `0.
|
|
193
|
-
#
|
|
226
|
+
# The VERSION is `0.1`, `0.2` or `0.4`. Write the
|
|
227
|
+
# LOWEST one that carries what the figure needs.
|
|
228
|
+
# `0.1` carries every other genre. `0.2` is the
|
|
229
|
+
# floor for `statechart` and `0.4` the floor for
|
|
230
|
+
# `sequence`: neither exists at `0.1`, and a
|
|
231
|
+
# version below a genre's floor is a line error
|
|
232
|
+
# (`figdown 0.3 sequence` does not parse).
|
|
233
|
+
# Sections may differ; each declares its own.
|
|
194
234
|
title "Some Title" # optional; the quotes are REQUIRED
|
|
195
235
|
# comments start with '#'; inside quotes the only escapes are \n \" \\
|
|
196
236
|
class hot "Congested path" stroke=#dc2626 # meaning + style, declared once
|