brane 0.0.2 → 0.3.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/AGENTS.md +362 -0
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +91 -0
- package/DESIGN.md +402 -0
- package/Dockerfile +61 -0
- package/LICENSE +656 -17
- package/README.md +174 -2
- package/SECURITY.md +205 -0
- package/dist/absorb/brane-id.js +11 -0
- package/dist/absorb/chatgpt.js +161 -0
- package/dist/absorb/claude-code.js +504 -0
- package/dist/absorb/index.js +543 -0
- package/dist/absorb/organelle.js +345 -0
- package/dist/absorb/run.js +520 -0
- package/dist/absorb/sources.js +168 -0
- package/dist/absorb/types.js +3 -0
- package/dist/agent-env.js +45 -0
- package/dist/agents.js +1168 -0
- package/dist/claude-live-session.js +124 -0
- package/dist/cli.js +50 -0
- package/dist/codex-tool-log.js +144 -0
- package/dist/commands/absorb.js +291 -0
- package/dist/commands/attach.js +31 -0
- package/dist/commands/certify.js +62 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/cost.js +126 -0
- package/dist/commands/daemon.js +350 -0
- package/dist/commands/doctor.js +262 -0
- package/dist/commands/fold.js +95 -0
- package/dist/commands/list.js +89 -0
- package/dist/commands/mcp.js +84 -0
- package/dist/commands/meld.js +332 -0
- package/dist/commands/memory.js +215 -0
- package/dist/commands/revive.js +74 -0
- package/dist/commands/run.js +85 -0
- package/dist/commands/spawn.js +1094 -0
- package/dist/commands/token.js +153 -0
- package/dist/copilot-tool-log.js +145 -0
- package/dist/cost/pricing.js +116 -0
- package/dist/cost/reprice.js +136 -0
- package/dist/crush-tool-log.js +293 -0
- package/dist/daemon/agent-scope.js +116 -0
- package/dist/daemon/awaiting.js +54 -0
- package/dist/daemon/cert-manager.js +349 -0
- package/dist/daemon/client.js +262 -0
- package/dist/daemon/gated-routes.js +112 -0
- package/dist/daemon/keep-awake.js +103 -0
- package/dist/daemon/local-socket.js +145 -0
- package/dist/daemon/presence-gate.js +206 -0
- package/dist/daemon/server.js +6349 -0
- package/dist/daemon/signal-source.js +35 -0
- package/dist/daemon/tailscale-rebind.js +58 -0
- package/dist/daemon/token-ring.js +263 -0
- package/dist/daemon/token-routes.js +190 -0
- package/dist/db/events.js +152 -0
- package/dist/db/mentions.js +179 -0
- package/dist/db/schema.js +633 -0
- package/dist/db/tokens.js +583 -0
- package/dist/db/tool-uses.js +647 -0
- package/dist/diagram/index.js +85 -0
- package/dist/diagram/label.js +455 -0
- package/dist/diagram/layout.js +476 -0
- package/dist/diagram/links.js +50 -0
- package/dist/diagram/metrics.js +152 -0
- package/dist/diagram/parse.js +487 -0
- package/dist/diagram/svg.js +389 -0
- package/dist/diagram/types.js +9 -0
- package/dist/export.js +629 -0
- package/dist/files/archive-types.js +2 -0
- package/dist/files/archive.js +182 -0
- package/dist/files/browse.js +327 -0
- package/dist/files/bytes.js +162 -0
- package/dist/files/codecs.js +33 -0
- package/dist/files/find.js +212 -0
- package/dist/files/tar.js +223 -0
- package/dist/files/zip.js +209 -0
- package/dist/folds.js +72 -0
- package/dist/grammars/ebnf.js +92 -0
- package/dist/highlight.js +250 -0
- package/dist/ids.js +24 -0
- package/dist/markdown-diagnostics.js +233 -0
- package/dist/math.js +168 -0
- package/dist/mcp/server.js +886 -0
- package/dist/memory/embedder.js +145 -0
- package/dist/memory/embedding-worker.js +71 -0
- package/dist/memory/embeddings.js +91 -0
- package/dist/memory/index.js +232 -0
- package/dist/memory/storage.js +587 -0
- package/dist/memory/wiki.js +688 -0
- package/dist/memory/wordpiece.js +247 -0
- package/dist/mentions-scan.js +40 -0
- package/dist/presence.js +259 -0
- package/dist/session-affinity.js +185 -0
- package/dist/shared/api-request.js +124 -0
- package/dist/shared/block-glyphs.js +114 -0
- package/dist/shared/diagram-css.js +17 -0
- package/dist/shared/export-selection.js +101 -0
- package/dist/shared/find-in-text.js +110 -0
- package/dist/shared/glow-hues.js +225 -0
- package/dist/shared/hex-dump.js +62 -0
- package/dist/shared/hex-pages.js +52 -0
- package/dist/shared/hit-drum.js +79 -0
- package/dist/shared/known-nids.js +58 -0
- package/dist/shared/list-scroll-keys.js +56 -0
- package/dist/shared/markdown-lists.js +72 -0
- package/dist/shared/markdown-math.js +485 -0
- package/dist/shared/markdown-mentions.js +79 -0
- package/dist/shared/markdown-nids.js +41 -0
- package/dist/shared/markdown-protocols.js +53 -0
- package/dist/shared/markdown-refs.js +123 -0
- package/dist/shared/membrane-invasion.js +342 -0
- package/dist/shared/membrane-motion.js +83 -0
- package/dist/shared/membrane-palette.js +253 -0
- package/dist/shared/membrane-terrain.js +212 -0
- package/dist/shared/message-selection.js +123 -0
- package/dist/shared/mo-glyph.js +57 -0
- package/dist/shared/narrative-format.js +237 -0
- package/dist/shared/narrative-menu.js +82 -0
- package/dist/shared/narrative-sort.js +47 -0
- package/dist/shared/narrative-status.js +52 -0
- package/dist/shared/no-pan.js +85 -0
- package/dist/shared/pane-layout.js +50 -0
- package/dist/shared/payload-inline.js +83 -0
- package/dist/shared/speaker-color.js +61 -0
- package/dist/shared/token-rotation.js +14 -0
- package/dist/shared/view-url.js +197 -0
- package/dist/shared/wiki-links.js +70 -0
- package/dist/shared/wiki-window.js +4 -0
- package/dist/tailscale.js +55 -0
- package/dist/test/absorb-claude.test.js +892 -0
- package/dist/test/agent-scope.test.js +193 -0
- package/dist/test/agents.test.js +55 -0
- package/dist/test/api-request.test.js +122 -0
- package/dist/test/awaiting-glow.test.js +54 -0
- package/dist/test/awaiting.test.js +122 -0
- package/dist/test/bind-token.test.js +88 -0
- package/dist/test/block-glyphs.test.js +68 -0
- package/dist/test/cert-manager.test.js +262 -0
- package/dist/test/claude-live-session.test.js +110 -0
- package/dist/test/codex-tool-log.test.js +568 -0
- package/dist/test/config.test.js +71 -0
- package/dist/test/copilot-tool-log.test.js +93 -0
- package/dist/test/cors-preflight.test.js +49 -0
- package/dist/test/cost-linger.test.js +31 -0
- package/dist/test/cost-pricing.test.js +72 -0
- package/dist/test/cost-reprice.test.js +139 -0
- package/dist/test/cost-sessions.test.js +301 -0
- package/dist/test/crush-tool-log.test.js +289 -0
- package/dist/test/daemon-client.test.js +109 -0
- package/dist/test/daemon-socket.test.js +293 -0
- package/dist/test/diagram-arrows.test.js +205 -0
- package/dist/test/diagram-export.test.js +210 -0
- package/dist/test/diagram-labels.test.js +313 -0
- package/dist/test/diagram-parse.test.js +191 -0
- package/dist/test/diagram-render.test.js +175 -0
- package/dist/test/diagram-review.test.js +288 -0
- package/dist/test/diagram-self-loop-labels.test.js +303 -0
- package/dist/test/embedder.test.js +84 -0
- package/dist/test/events.test.js +123 -0
- package/dist/test/export-selection.test.js +102 -0
- package/dist/test/export.test.js +505 -0
- package/dist/test/files-browse.test.js +393 -0
- package/dist/test/files-find.test.js +272 -0
- package/dist/test/find-in-text.test.js +86 -0
- package/dist/test/first-paint-viewport.test.js +59 -0
- package/dist/test/fixtures/embedding-golden.js +23 -0
- package/dist/test/fixtures/wordpiece-golden.js +167 -0
- package/dist/test/glow-hues.test.js +202 -0
- package/dist/test/harness-cost.test.js +423 -0
- package/dist/test/helpers.js +21 -0
- package/dist/test/hex-pages.test.js +68 -0
- package/dist/test/highlight.test.js +69 -0
- package/dist/test/hit-drum.test.js +71 -0
- package/dist/test/hook-config.test.js +262 -0
- package/dist/test/json-gzip.test.js +70 -0
- package/dist/test/key-routing.test.js +58 -0
- package/dist/test/known-nids.test.js +106 -0
- package/dist/test/link-preview.test.js +67 -0
- package/dist/test/list-scroll-keys.test.js +66 -0
- package/dist/test/listen-own-words.test.js +147 -0
- package/dist/test/listen-timeout.test.js +33 -0
- package/dist/test/markdown-diagnostics.test.js +121 -0
- package/dist/test/markdown-lists.test.js +182 -0
- package/dist/test/markdown-math.test.js +256 -0
- package/dist/test/markdown-nids.test.js +146 -0
- package/dist/test/markdown-protocols.test.js +139 -0
- package/dist/test/mcp-bridge-cancel.test.js +98 -0
- package/dist/test/mcp-bridge-reconnect.test.js +121 -0
- package/dist/test/mcp-narrative.test.js +193 -0
- package/dist/test/mcp.test.js +548 -0
- package/dist/test/meld.test.js +85 -0
- package/dist/test/membrane-invasion.test.js +482 -0
- package/dist/test/membrane-motion.test.js +101 -0
- package/dist/test/membrane-palette.test.js +166 -0
- package/dist/test/membrane-terrain.test.js +170 -0
- package/dist/test/memory-log.test.js +202 -0
- package/dist/test/mentions-e2e.test.js +261 -0
- package/dist/test/mentions-ledger.test.js +265 -0
- package/dist/test/mentions-listen.test.js +265 -0
- package/dist/test/mentions.test.js +238 -0
- package/dist/test/message-selection.test.js +114 -0
- package/dist/test/mo-glyph.test.js +22 -0
- package/dist/test/model-resolution.test.js +166 -0
- package/dist/test/narrate-validate.test.js +77 -0
- package/dist/test/narrate-wait.test.js +160 -0
- package/dist/test/narrative-format.test.js +219 -0
- package/dist/test/narrative-menu.test.js +137 -0
- package/dist/test/narrative-sort.test.js +75 -0
- package/dist/test/no-pan.test.js +60 -0
- package/dist/test/pane-layout.test.js +153 -0
- package/dist/test/paths.test.js +37 -0
- package/dist/test/payload-inline.test.js +91 -0
- package/dist/test/precache-fonts.test.js +72 -0
- package/dist/test/presence-gate.test.js +440 -0
- package/dist/test/presence.test.js +160 -0
- package/dist/test/read-events.test.js +220 -0
- package/dist/test/remote-meld.test.js +115 -0
- package/dist/test/revive-cli.test.js +93 -0
- package/dist/test/search-floor.test.js +206 -0
- package/dist/test/search-fusion.test.js +168 -0
- package/dist/test/search.test.js +205 -0
- package/dist/test/security.test.js +48 -0
- package/dist/test/selection-wiring.test.js +61 -0
- package/dist/test/session-affinity.test.js +295 -0
- package/dist/test/signal-source.test.js +32 -0
- package/dist/test/socket-account.test.js +268 -0
- package/dist/test/spawn-toml.test.js +72 -0
- package/dist/test/speaker-color.test.js +90 -0
- package/dist/test/tailscale-rebind.test.js +79 -0
- package/dist/test/terminal-cursor.test.js +61 -0
- package/dist/test/terminal-resync.test.js +23 -0
- package/dist/test/thin-cli.test.js +382 -0
- package/dist/test/token-bootstrap.test.js +272 -0
- package/dist/test/token-devices.test.js +587 -0
- package/dist/test/token-lapse.test.js +94 -0
- package/dist/test/token-ring.test.js +188 -0
- package/dist/test/token-rotate-cli.test.js +58 -0
- package/dist/test/token-routes.test.js +95 -0
- package/dist/test/tool-uses.test.js +1006 -0
- package/dist/test/usage-ledger.test.js +126 -0
- package/dist/test/view-url.test.js +581 -0
- package/dist/test/wiki-links.test.js +57 -0
- package/dist/test/wiki-sources.test.js +85 -0
- package/dist/test/wiki-window.test.js +31 -0
- package/dist/test/wiki.test.js +399 -0
- package/dist/test/wordpiece.test.js +68 -0
- package/dist/tls.js +148 -0
- package/dist/tmux.js +202 -0
- package/dist/user-agent.js +46 -0
- package/docker-entrypoint.sh +141 -0
- package/docs/README.md +3 -0
- package/docs/absorb.md +231 -0
- package/docs/cost-ledger.md +125 -0
- package/docs/daemon-api.md +189 -0
- package/docs/diagrams.md +163 -0
- package/docs/doctor.md +36 -0
- package/docs/export-scope.md +69 -0
- package/docs/files-view.md +133 -0
- package/docs/link-previews.md +38 -0
- package/docs/math.md +32 -0
- package/docs/mcp.md +69 -0
- package/docs/memory-search.md +75 -0
- package/docs/mentions.md +101 -0
- package/docs/multi-brane.md +14 -0
- package/docs/passkeys.md +165 -0
- package/docs/presence.md +202 -0
- package/docs/releasing.md +53 -0
- package/docs/rendering-warnings.md +22 -0
- package/docs/request-layer.md +72 -0
- package/docs/selection-store.md +50 -0
- package/docs/session-affinity.md +61 -0
- package/docs/terminal-mirroring.md +211 -0
- package/docs/token-rotation.md +413 -0
- package/docs/view-urls.md +114 -0
- package/docs/web-app.md +23 -0
- package/docs/wiki.md +43 -0
- package/expo/dist/_expo/static/js/web/entry-e56efc1291dca7258ea00b8108b033a4.js +1097 -0
- package/expo/dist/apple-touch-icon.png +0 -0
- package/expo/dist/favicon.svg +3 -0
- package/expo/dist/index.html +177 -0
- package/expo/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/expo/dist/katex/katex.min.css +1 -0
- package/expo/dist/manifest.json +16 -0
- package/expo/dist/metadata.json +1 -0
- package/expo/dist/og/membrane.v1.png +0 -0
- package/expo/dist/pdf.min.mjs +28 -0
- package/expo/dist/pdf.worker.min.mjs +28 -0
- package/expo/dist/sw.js +154 -0
- package/package.json +81 -23
- package/protocols/LIVE_loop.md +407 -0
- package/protocols/bebrief.md +41 -0
- package/protocols/digest.md +231 -0
- package/protocols/fly.md +266 -0
- package/protocols/idle.md +81 -0
- package/protocols/keeper.md +829 -0
- package/protocols/markdown.md +80 -0
- package/protocols/memory.md +419 -0
- package/protocols/mentions.md +93 -0
- package/protocols/payloads.md +53 -0
- package/protocols/prelude.md +91 -0
- package/protocols/ramify.md +105 -0
- package/protocols/review.md +356 -0
- package/protocols/rrr.md +232 -0
- package/protocols/scribe.md +429 -0
- package/protocols/summarize.md +203 -0
- package/protocols/twin.md +174 -0
- package/protocols/wiki.md +438 -0
- package/protocols/worker.md +38 -0
- package/wiki/shared/append-only-event-log.md +148 -0
- package/wiki/shared/index.md +48 -0
- package/wiki/shared/owning-your-narratives.md +161 -0
- package/wiki/shared/premature-ontologies.md +435 -0
- package/wiki/shared/sedimentary-superintelligence.md +128 -0
- package/wiki/shared/sequestering-the-narrative.md +143 -0
- package/wiki/shared/why-wikis-work.md +216 -0
- package/.npmignore +0 -2
- package/.travis.yml +0 -5
- package/index.js +0 -8
- package/index.js~ +0 -12
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/** SVG emission.
|
|
2
|
+
*
|
|
3
|
+
* Three rules the rest of this file exists to keep:
|
|
4
|
+
*
|
|
5
|
+
* **Nothing from the fence reaches the output as markup.** Labels are
|
|
6
|
+
* escaped into text nodes. The markup mermaid allows in a label — `<br/>`,
|
|
7
|
+
* `<b>`/`<i>` and their kin, and markdown strings — was parsed into lines of
|
|
8
|
+
* styled runs (`label.ts`), and becomes `<tspan>`s here whose attributes this
|
|
9
|
+
* file writes itself — parsed, never forwarded. Style declarations were filtered
|
|
10
|
+
* to a known property set with a value charset that excludes `url(`.
|
|
11
|
+
*
|
|
12
|
+
* **No `<style>` element.** An inline SVG's `<style>` is *document*-global:
|
|
13
|
+
* a `text { font-family: … }` rule inside one diagram restyles every other
|
|
14
|
+
* inline SVG on the page, and brane renders many per screen. Everything here
|
|
15
|
+
* is a presentation attribute, and theming rides on CSS custom properties
|
|
16
|
+
* whose fallbacks are brane's own dark palette, so the page can retheme
|
|
17
|
+
* without us shipping a selector and the common case needs no declaration.
|
|
18
|
+
*
|
|
19
|
+
* **Ids are per-diagram.** Two diagrams in one conversation would otherwise
|
|
20
|
+
* share `url(#arrow)` and the second would take the first's marker.
|
|
21
|
+
*/
|
|
22
|
+
import { FONT_SIZE, LABEL_FONT_SIZE, LINE_HEIGHT, MONO_FAMILY, TABLE_GAP, measureEdgeLabel, tableColumns, tableWidth, } from "./metrics.js";
|
|
23
|
+
import { isTable, slotsOf } from "./label.js";
|
|
24
|
+
const ESC = {
|
|
25
|
+
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
|
26
|
+
};
|
|
27
|
+
export const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ESC[c]);
|
|
28
|
+
/** Colours resolve from the page, and **the fallbacks are dark**.
|
|
29
|
+
*
|
|
30
|
+
* The custom properties are the extension point, but a fallback is what
|
|
31
|
+
* actually renders until something defines one — and the first draft shipped
|
|
32
|
+
* light-mode fallbacks that nothing overrode. Both of brane's targets are
|
|
33
|
+
* dark (the app's `#121214`, the export's `#000`), so every diagram drew
|
|
34
|
+
* `#33373d` lines on near-black and the subgraph title was unreadable. For a
|
|
35
|
+
* change whose subject is the arrows, that is the first thing anyone sees.
|
|
36
|
+
*
|
|
37
|
+
* Defaulting to the palette brane actually has, rather than to the palette a
|
|
38
|
+
* standalone white page would want, means the common case needs no
|
|
39
|
+
* declaration anywhere and a light host can still override. */
|
|
40
|
+
const C = {
|
|
41
|
+
line: "var(--brane-diagram-line, #b8bcc4)",
|
|
42
|
+
nodeFill: "var(--brane-diagram-node-fill, #262a33)",
|
|
43
|
+
nodeStroke: "var(--brane-diagram-node-stroke, #9370db)",
|
|
44
|
+
text: "var(--brane-diagram-text, #e6e6e6)",
|
|
45
|
+
clusterFill: "var(--brane-diagram-cluster-fill, rgba(147,112,219,0.10))",
|
|
46
|
+
clusterStroke: "var(--brane-diagram-cluster-stroke, rgba(147,112,219,0.55))",
|
|
47
|
+
/** Behind an edge label, so the line does not run through the text. Matches
|
|
48
|
+
* the bubble it sits on rather than being hard-coded white. */
|
|
49
|
+
labelBg: "var(--brane-diagram-label-bg, #1c1c1e)",
|
|
50
|
+
/** An edge picked out by hover or tap, and its label with it. */
|
|
51
|
+
highlight: "var(--brane-diagram-highlight, #ffd866)",
|
|
52
|
+
};
|
|
53
|
+
const HEAD_LEN = 10;
|
|
54
|
+
const HEAD_W = 8;
|
|
55
|
+
/** d3-shape's `curveBasis`, ported so this module adds no dependency.
|
|
56
|
+
*
|
|
57
|
+
* A uniform cubic B-spline. It is what mermaid fits and — after a comparison
|
|
58
|
+
* against clamped Hermite, catmull-rom and monotone — the one that keeps an
|
|
59
|
+
* edge to a single calm sweep. Its terminal points are duplicated, so the
|
|
60
|
+
* curve does reach both ends exactly; the tilt it used to arrive at was
|
|
61
|
+
* never the curve's fault but the attachment point's (see `layout.ts`). */
|
|
62
|
+
export function basisPath(pts) {
|
|
63
|
+
if (pts.length === 0)
|
|
64
|
+
return "";
|
|
65
|
+
if (pts.length === 1)
|
|
66
|
+
return `M${r(pts[0].x)},${r(pts[0].y)}`;
|
|
67
|
+
if (pts.length === 2) {
|
|
68
|
+
return `M${r(pts[0].x)},${r(pts[0].y)}L${r(pts[1].x)},${r(pts[1].y)}`;
|
|
69
|
+
}
|
|
70
|
+
const out = [];
|
|
71
|
+
let x0 = NaN, y0 = NaN, x1 = NaN, y1 = NaN, n = 0;
|
|
72
|
+
const seg = (x, y) => out.push(`C${r((2 * x0 + x1) / 3)},${r((2 * y0 + y1) / 3)}` +
|
|
73
|
+
`,${r((x0 + 2 * x1) / 3)},${r((y0 + 2 * y1) / 3)}` +
|
|
74
|
+
`,${r((x0 + 4 * x1 + x) / 6)},${r((y0 + 4 * y1 + y) / 6)}`);
|
|
75
|
+
for (const p of pts) {
|
|
76
|
+
switch (n) {
|
|
77
|
+
case 0:
|
|
78
|
+
n = 1;
|
|
79
|
+
out.push(`M${r(p.x)},${r(p.y)}`);
|
|
80
|
+
break;
|
|
81
|
+
case 1:
|
|
82
|
+
n = 2;
|
|
83
|
+
break;
|
|
84
|
+
case 2:
|
|
85
|
+
n = 3;
|
|
86
|
+
out.push(`L${r((5 * x0 + x1) / 6)},${r((5 * y0 + y1) / 6)}`);
|
|
87
|
+
seg(p.x, p.y);
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
seg(p.x, p.y);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
x0 = x1;
|
|
94
|
+
x1 = p.x;
|
|
95
|
+
y0 = y1;
|
|
96
|
+
y1 = p.y;
|
|
97
|
+
}
|
|
98
|
+
if (n === 3)
|
|
99
|
+
seg(x1, y1);
|
|
100
|
+
if (n === 2 || n === 3)
|
|
101
|
+
out.push(`L${r(x1)},${r(y1)}`);
|
|
102
|
+
return out.join("");
|
|
103
|
+
}
|
|
104
|
+
const r = (v) => Math.round(v * 100) / 100;
|
|
105
|
+
function nodeShapePath(n) {
|
|
106
|
+
const hx = n.width / 2, hy = n.height / 2;
|
|
107
|
+
const l = r(n.x - hx), t = r(n.y - hy), w = r(n.width), h = r(n.height);
|
|
108
|
+
const fill = n.style?.fill ?? C.nodeFill;
|
|
109
|
+
const stroke = n.style?.stroke ?? C.nodeStroke;
|
|
110
|
+
const sw = n.style?.["stroke-width"] ?? "1.5";
|
|
111
|
+
const dash = n.style?.["stroke-dasharray"];
|
|
112
|
+
const common = ` fill="${esc(fill)}" stroke="${esc(stroke)}" stroke-width="${esc(sw)}"` +
|
|
113
|
+
(dash ? ` stroke-dasharray="${esc(dash)}"` : "");
|
|
114
|
+
switch (n.shape) {
|
|
115
|
+
case "circle":
|
|
116
|
+
return `<ellipse cx="${r(n.x)}" cy="${r(n.y)}" rx="${r(hx)}" ry="${r(hy)}"${common}/>`;
|
|
117
|
+
case "diamond":
|
|
118
|
+
return `<polygon points="${r(n.x)},${t} ${r(n.x + hx)},${r(n.y)} ${r(n.x)},${r(n.y + hy)} ${r(n.x - hx)},${r(n.y)}"${common}/>`;
|
|
119
|
+
case "hexagon": {
|
|
120
|
+
const k = Math.min(hx * 0.3, 14);
|
|
121
|
+
return `<polygon points="${r(n.x - hx + k)},${t} ${r(n.x + hx - k)},${t} ${r(n.x + hx)},${r(n.y)} ${r(n.x + hx - k)},${r(n.y + hy)} ${r(n.x - hx + k)},${r(n.y + hy)} ${r(n.x - hx)},${r(n.y)}"${common}/>`;
|
|
122
|
+
}
|
|
123
|
+
case "asymmetric": {
|
|
124
|
+
const k = Math.min(hx * 0.35, 16);
|
|
125
|
+
return `<polygon points="${l},${t} ${r(n.x + hx)},${t} ${r(n.x + hx)},${r(n.y + hy)} ${l},${r(n.y + hy)} ${r(n.x - hx + k)},${r(n.y)}"${common}/>`;
|
|
126
|
+
}
|
|
127
|
+
case "cylinder": {
|
|
128
|
+
const ry = Math.min(hy * 0.34, 10);
|
|
129
|
+
return (`<path d="M${l},${r(n.y - hy + ry)}` +
|
|
130
|
+
`a${r(hx)},${r(ry)} 0 0 1 ${w},0` +
|
|
131
|
+
`v${r(n.height - ry * 2)}` +
|
|
132
|
+
`a${r(hx)},${r(ry)} 0 0 1 ${r(-w)},0z"${common}/>` +
|
|
133
|
+
`<path d="M${l},${r(n.y - hy + ry)}a${r(hx)},${r(ry)} 0 0 0 ${w},0" fill="none" stroke="${esc(stroke)}" stroke-width="${esc(sw)}"/>`);
|
|
134
|
+
}
|
|
135
|
+
case "subroutine": {
|
|
136
|
+
const k = Math.min(hx * 0.16, 9);
|
|
137
|
+
return (`<rect x="${l}" y="${t}" width="${w}" height="${h}" rx="3"${common}/>` +
|
|
138
|
+
`<path d="M${r(n.x - hx + k)},${t}v${h}M${r(n.x + hx - k)},${t}v${h}" fill="none" stroke="${esc(stroke)}" stroke-width="${esc(sw)}"/>`);
|
|
139
|
+
}
|
|
140
|
+
case "stadium":
|
|
141
|
+
return `<rect x="${l}" y="${t}" width="${w}" height="${h}" rx="${r(hy)}"${common}/>`;
|
|
142
|
+
case "round":
|
|
143
|
+
return `<rect x="${l}" y="${t}" width="${w}" height="${h}" rx="11"${common}/>`;
|
|
144
|
+
case "rect":
|
|
145
|
+
default:
|
|
146
|
+
return `<rect x="${l}" y="${t}" width="${w}" height="${h}" rx="4"${common}/>`;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** One run's text, wrapped in a `<tspan>` only when it is styled, so an
|
|
150
|
+
* unstyled label draws byte-for-byte as it did before runs existed. */
|
|
151
|
+
function runSpan(run) {
|
|
152
|
+
let attrs = "";
|
|
153
|
+
if (run.bold)
|
|
154
|
+
attrs += ` font-weight="bold"`;
|
|
155
|
+
if (run.italic)
|
|
156
|
+
attrs += ` font-style="italic"`;
|
|
157
|
+
if (run.code)
|
|
158
|
+
attrs += ` font-family="${MONO_FAMILY}"`;
|
|
159
|
+
const deco = [run.underline && "underline", run.strike && "line-through"].filter(Boolean).join(" ");
|
|
160
|
+
if (deco)
|
|
161
|
+
attrs += ` text-decoration="${deco}"`;
|
|
162
|
+
return attrs ? `<tspan${attrs}>${esc(run.text)}</tspan>` : esc(run.text);
|
|
163
|
+
}
|
|
164
|
+
const lineContent = (line) => line.map(runSpan).join("");
|
|
165
|
+
/** Stacked lines centred on `y0`: the first `dy` lifts the block by half its
|
|
166
|
+
* height, each later one steps down a line. */
|
|
167
|
+
function lineTspans(lines, x, fontSize) {
|
|
168
|
+
const first = -((lines.length - 1) / 2) * fontSize * LINE_HEIGHT;
|
|
169
|
+
return lines
|
|
170
|
+
.map((ln, i) => `<tspan x="${r(x)}" dy="${i === 0 ? r(first) : r(fontSize * LINE_HEIGHT)}">${lineContent(ln)}</tspan>`)
|
|
171
|
+
.join("");
|
|
172
|
+
}
|
|
173
|
+
/** A subgraph title's lines, hanging down from the first baseline. */
|
|
174
|
+
function titleTspans(lines, x) {
|
|
175
|
+
if (lines.length === 1)
|
|
176
|
+
return lineContent(lines[0]);
|
|
177
|
+
return lines
|
|
178
|
+
.map((ln, i) => `<tspan x="${r(x)}" dy="${i === 0 ? 0 : r(LABEL_FONT_SIZE * LINE_HEIGHT)}">${lineContent(ln)}</tspan>`)
|
|
179
|
+
.join("");
|
|
180
|
+
}
|
|
181
|
+
const FONT_FAMILY = "system-ui, -apple-system, Segoe UI, sans-serif";
|
|
182
|
+
function nodeText(n, opts) {
|
|
183
|
+
const color = n.style?.color ?? C.text;
|
|
184
|
+
const text = n.runs.some(isTable)
|
|
185
|
+
? slottedText(n.runs, n.x, n.y, color)
|
|
186
|
+
: `<text x="${r(n.x)}" y="${r(n.y + FONT_SIZE * 0.35)}" text-anchor="middle"` +
|
|
187
|
+
` font-family="${FONT_FAMILY}" font-size="${FONT_SIZE}"` +
|
|
188
|
+
` fill="${esc(color)}">${lineTspans(n.runs, n.x, FONT_SIZE)}</text>`;
|
|
189
|
+
const href = opts.hrefFor?.(n.lines.join(" "));
|
|
190
|
+
return href ? `<a href="${esc(href)}">${text}</a>` : text;
|
|
191
|
+
}
|
|
192
|
+
function edgePath(e, id, hl = false) {
|
|
193
|
+
const d = basisPath(hl ? trimForHeads(e) : e.points);
|
|
194
|
+
const dash = e.line === "dotted" ? ` stroke-dasharray="5 4"` : "";
|
|
195
|
+
const width = (e.line === "thick" ? 3 : 1.7) + (hl ? 1.3 : 0);
|
|
196
|
+
const m = hl ? "ah-hl" : "ah";
|
|
197
|
+
const end = e.arrow === "open" ? "" : ` marker-end="url(#${m}-${id})"`;
|
|
198
|
+
const start = e.arrow === "biarrow" ? ` marker-start="url(#${m}-start-${id})"` : "";
|
|
199
|
+
return (`<path d="${d}" fill="none" stroke="${hl ? C.highlight : C.line}" stroke-width="${width}"` +
|
|
200
|
+
`${dash} stroke-linecap="round"${start}${end}/>`);
|
|
201
|
+
}
|
|
202
|
+
/** A highlighted edge's points, stopped short of each arrowhead.
|
|
203
|
+
*
|
|
204
|
+
* The base line runs to the arrowhead's tip, under the head, and at 1.7px
|
|
205
|
+
* that is invisible. The highlight is 3px, and running it under the point
|
|
206
|
+
* blunted the tip. So an end that carries a head is pulled back one head
|
|
207
|
+
* length along its last segment, and the highlight's markers are anchored
|
|
208
|
+
* at their base (`refX` 0 at the end, `HEAD_LEN` at the start), which puts
|
|
209
|
+
* the tip back exactly where the base line's is. The basis curve leaves its
|
|
210
|
+
* last point along the last segment, so moving that point along the same
|
|
211
|
+
* segment keeps the head's angle.
|
|
212
|
+
*
|
|
213
|
+
* An end with no head is left alone: it runs on into the box, and the box,
|
|
214
|
+
* painted above the highlight layer, covers it. */
|
|
215
|
+
function trimForHeads(e) {
|
|
216
|
+
const pts = e.points.slice();
|
|
217
|
+
const pull = (at, toward) => {
|
|
218
|
+
const a = pts[at], b = pts[toward];
|
|
219
|
+
const len = Math.hypot(b.x - a.x, b.y - a.y);
|
|
220
|
+
if (len === 0)
|
|
221
|
+
return;
|
|
222
|
+
// A segment shorter than a head can't give one up whole; keep a sliver so
|
|
223
|
+
// the curve still has a direction to end in.
|
|
224
|
+
const k = Math.min(HEAD_LEN, len - 0.5) / len;
|
|
225
|
+
if (k > 0)
|
|
226
|
+
pts[at] = { x: a.x + (b.x - a.x) * k, y: a.y + (b.y - a.y) * k };
|
|
227
|
+
};
|
|
228
|
+
if (pts.length < 2)
|
|
229
|
+
return pts;
|
|
230
|
+
if (e.arrow !== "open")
|
|
231
|
+
pull(pts.length - 1, pts.length - 2);
|
|
232
|
+
if (e.arrow === "biarrow")
|
|
233
|
+
pull(0, 1);
|
|
234
|
+
return pts;
|
|
235
|
+
}
|
|
236
|
+
function edgeLabel(e, hl = false) {
|
|
237
|
+
if (!e.label || !e.labelPos)
|
|
238
|
+
return "";
|
|
239
|
+
const { x, y } = e.labelPos;
|
|
240
|
+
const runs = e.labelRuns ?? [[{ text: e.label }]];
|
|
241
|
+
// The same measurement layout reserved space with. Sizing the chip as
|
|
242
|
+
// `length × 0.55em` instead agreed with it only for ASCII, and left Greek or
|
|
243
|
+
// CJK text hanging out of its own background.
|
|
244
|
+
const w = measureEdgeLabel(runs).width;
|
|
245
|
+
// One line keeps its old geometry exactly; each extra line grows the chip
|
|
246
|
+
// by a line height, centred on the same point.
|
|
247
|
+
const extra = (runs.length - 1) * LABEL_FONT_SIZE * LINE_HEIGHT;
|
|
248
|
+
const body = runs.length === 1
|
|
249
|
+
? lineContent(runs[0])
|
|
250
|
+
: lineTspans(runs, x, LABEL_FONT_SIZE);
|
|
251
|
+
return (`<rect x="${r(x - w / 2)}" y="${r(y - LABEL_FONT_SIZE * 0.8 - extra / 2)}" width="${r(w)}"` +
|
|
252
|
+
` height="${r(LABEL_FONT_SIZE * 1.6 + extra)}" rx="3" fill="${C.labelBg}"` +
|
|
253
|
+
`${hl ? ` stroke="${C.highlight}" stroke-width="1.3"` : ""}/>` +
|
|
254
|
+
`<text x="${r(x)}" y="${r(y + LABEL_FONT_SIZE * 0.34)}" text-anchor="middle"` +
|
|
255
|
+
` font-family="system-ui, -apple-system, Segoe UI, sans-serif" font-size="${LABEL_FONT_SIZE}"` +
|
|
256
|
+
` fill="${hl ? C.highlight : C.text}">${body}</text>`);
|
|
257
|
+
}
|
|
258
|
+
/** Half the width of the invisible stroke that catches a pointer or a tap.
|
|
259
|
+
* A 1.7px line is too thin to hit with a finger; much wider and neighbouring
|
|
260
|
+
* arrows' strokes overlap, and the later one wins every tap between them. */
|
|
261
|
+
const HIT_HALF_WIDTH = 5;
|
|
262
|
+
/** One edge, again, above every other edge: the way a reader tells which
|
|
263
|
+
* arrow a label belongs to where several cross.
|
|
264
|
+
*
|
|
265
|
+
* The base drawing paints in layers (every path, then every label, then
|
|
266
|
+
* every node), and that order is what keeps labels above lines and boxes
|
|
267
|
+
* above both. Grouping each path with its own label would let a later path
|
|
268
|
+
* paint over an earlier label, so the pair is drawn a second time here, in
|
|
269
|
+
* a layer of its own, between the labels and the nodes. Above the labels so
|
|
270
|
+
* nothing else crosses it; below the nodes so the boxes cover its ends, as
|
|
271
|
+
* they do the base line's. Painted over them, the highlight's heavier
|
|
272
|
+
* stroke and round cap tipped over the box edge it leaves from. Below them
|
|
273
|
+
* also means a box, not an edge's hit stroke, catches a tap on the box.
|
|
274
|
+
*
|
|
275
|
+
* It is revealed by CSS alone (`:hover`, and `:focus` for a tap, which is
|
|
276
|
+
* why the group takes `tabindex`), so the export stays scriptless. Hidden is
|
|
277
|
+
* an `opacity` *attribute*, not a rule, so a page without the stylesheet
|
|
278
|
+
* shows nothing extra.
|
|
279
|
+
*
|
|
280
|
+
* Each edge gets up to two of these, one per way to pick it. `line` is
|
|
281
|
+
* caught by an invisible stroke along the path; `label` by the label's
|
|
282
|
+
* chip. All the `label` groups paint after all the `line` groups, because a
|
|
283
|
+
* label is the thing a reader taps to ask "which arrow is this?", and in a
|
|
284
|
+
* single layer a later edge's stroke covered an earlier edge's label. (In
|
|
285
|
+
* WebKit, tapping every label and line of three real diagrams: 16 of 72
|
|
286
|
+
* taps picked the wrong edge with one layer.) */
|
|
287
|
+
function edgeHighlight(e, id, by) {
|
|
288
|
+
if (by === "label" && (!e.label || !e.labelPos))
|
|
289
|
+
return "";
|
|
290
|
+
const hit = by === "line"
|
|
291
|
+
? `<path d="${basisPath(e.points)}" fill="none" stroke="transparent"` +
|
|
292
|
+
` stroke-width="${HIT_HALF_WIDTH * 2}" stroke-linecap="butt" pointer-events="stroke"/>`
|
|
293
|
+
: "";
|
|
294
|
+
// Only the hit stroke and the chip should catch a pointer: the copy of the
|
|
295
|
+
// line under a `label` group would otherwise reach along the whole path.
|
|
296
|
+
const copy = by === "line"
|
|
297
|
+
? edgePath(e, id, true) + edgeLabel(e, true)
|
|
298
|
+
: `<g pointer-events="none">${edgePath(e, id, true)}</g>` + edgeLabel(e, true);
|
|
299
|
+
const aria = e.label ? ` aria-label="${esc(e.label)}"` : "";
|
|
300
|
+
return `<g class="brane-diagram-edge-hl" tabindex="0" opacity="0"${aria}>${hit}${copy}</g>`;
|
|
301
|
+
}
|
|
302
|
+
export function renderSvg(placed, opts) {
|
|
303
|
+
const id = opts.idSuffix;
|
|
304
|
+
const defs = `<defs>` +
|
|
305
|
+
`<marker id="ah-${id}" markerUnits="userSpaceOnUse" markerWidth="${HEAD_LEN}"` +
|
|
306
|
+
` markerHeight="${HEAD_W}" refX="${HEAD_LEN}" refY="${HEAD_W / 2}" orient="auto">` +
|
|
307
|
+
`<path d="M0,0L${HEAD_LEN},${HEAD_W / 2}L0,${HEAD_W}z" fill="${C.line}"/></marker>` +
|
|
308
|
+
`<marker id="ah-start-${id}" markerUnits="userSpaceOnUse" markerWidth="${HEAD_LEN}"` +
|
|
309
|
+
` markerHeight="${HEAD_W}" refX="0" refY="${HEAD_W / 2}" orient="auto">` +
|
|
310
|
+
`<path d="M${HEAD_LEN},0L0,${HEAD_W / 2}L${HEAD_LEN},${HEAD_W}z" fill="${C.line}"/></marker>` +
|
|
311
|
+
(opts.edgeHighlight
|
|
312
|
+
// Anchored at the head's base, not its tip: the highlighted line stops
|
|
313
|
+
// short of the tip (`trimForHeads`), and the head makes up the rest.
|
|
314
|
+
? `<marker id="ah-hl-${id}" markerUnits="userSpaceOnUse" markerWidth="${HEAD_LEN}"` +
|
|
315
|
+
` markerHeight="${HEAD_W}" refX="0" refY="${HEAD_W / 2}" orient="auto">` +
|
|
316
|
+
`<path d="M0,0L${HEAD_LEN},${HEAD_W / 2}L0,${HEAD_W}z" fill="${C.highlight}"/></marker>` +
|
|
317
|
+
`<marker id="ah-hl-start-${id}" markerUnits="userSpaceOnUse" markerWidth="${HEAD_LEN}"` +
|
|
318
|
+
` markerHeight="${HEAD_W}" refX="${HEAD_LEN}" refY="${HEAD_W / 2}" orient="auto">` +
|
|
319
|
+
`<path d="M${HEAD_LEN},0L0,${HEAD_W / 2}L${HEAD_LEN},${HEAD_W}z" fill="${C.highlight}"/></marker>`
|
|
320
|
+
: "") +
|
|
321
|
+
`</defs>`;
|
|
322
|
+
const clusters = placed.clusters
|
|
323
|
+
.map((c) => `<rect x="${r(c.x - c.width / 2)}" y="${r(c.y - c.height / 2)}" width="${r(c.width)}"` +
|
|
324
|
+
` height="${r(c.height)}" rx="6" fill="${C.clusterFill}" stroke="${C.clusterStroke}"` +
|
|
325
|
+
` stroke-width="1" stroke-dasharray="4 3"/>` +
|
|
326
|
+
`<text x="${r(c.x)}" y="${r(c.y - c.height / 2 + 15)}" text-anchor="middle"` +
|
|
327
|
+
` font-family="system-ui, -apple-system, Segoe UI, sans-serif" font-size="${LABEL_FONT_SIZE}"` +
|
|
328
|
+
` fill="${C.text}" opacity="0.75">${titleTspans(c.titleLines, c.x)}</text>`)
|
|
329
|
+
.join("");
|
|
330
|
+
const edges = placed.edges.map((e) => edgePath(e, id)).join("");
|
|
331
|
+
const labels = placed.edges.map((e) => edgeLabel(e)).join("");
|
|
332
|
+
const nodes = placed.nodes.map((n) => nodeShapePath(n) + nodeText(n, opts)).join("");
|
|
333
|
+
const highlights = opts.edgeHighlight
|
|
334
|
+
? placed.edges.map((e) => edgeHighlight(e, id, "line")).join("") +
|
|
335
|
+
placed.edges.map((e) => edgeHighlight(e, id, "label")).join("")
|
|
336
|
+
: "";
|
|
337
|
+
return (`<svg xmlns="http://www.w3.org/2000/svg" class="brane-diagram"` +
|
|
338
|
+
` viewBox="0 0 ${placed.width} ${placed.height}"` +
|
|
339
|
+
` width="${placed.width}" height="${placed.height}" role="img">` +
|
|
340
|
+
defs + clusters + edges + labels + highlights + nodes +
|
|
341
|
+
`</svg>`);
|
|
342
|
+
}
|
|
343
|
+
/** A label holding a table. One `<text>` per line or cell, each on its own
|
|
344
|
+
* baseline: a cell has to start at its column's edge, and the centred
|
|
345
|
+
* `dy`-stacked `<tspan>`s every other label uses can't say that. The slot
|
|
346
|
+
* baselines are the same ones that stack would have produced, so a table
|
|
347
|
+
* sits in a label exactly where its rows' worth of lines would. */
|
|
348
|
+
function slottedText(lines, cx, cy, color) {
|
|
349
|
+
const total = lines.reduce((k, l) => k + slotsOf(l), 0);
|
|
350
|
+
const step = FONT_SIZE * LINE_HEIGHT;
|
|
351
|
+
const baseline = (slot) => cy + FONT_SIZE * 0.35 + (slot - (total - 1) / 2) * step;
|
|
352
|
+
const at = (x, y, anchor, body) => `<text x="${r(x)}" y="${r(y)}" text-anchor="${anchor}">${body}</text>`;
|
|
353
|
+
let slot = 0;
|
|
354
|
+
let out = "";
|
|
355
|
+
for (const line of lines) {
|
|
356
|
+
if (!isTable(line)) {
|
|
357
|
+
out += at(cx, baseline(slot++), "middle", lineContent(line));
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
out += tableText(line, cx, slot, baseline);
|
|
361
|
+
slot += slotsOf(line);
|
|
362
|
+
}
|
|
363
|
+
return `<g font-family="${FONT_FAMILY}" font-size="${FONT_SIZE}" fill="${esc(color)}">${out}</g>`;
|
|
364
|
+
function tableText(t, x0, first, base) {
|
|
365
|
+
const cols = tableColumns(t, FONT_SIZE);
|
|
366
|
+
const left = x0 - tableWidth(t, FONT_SIZE) / 2;
|
|
367
|
+
const starts = cols.map((_, i) => left + cols.slice(0, i).reduce((a, w) => a + w + TABLE_GAP, 0));
|
|
368
|
+
const cell = (cells, y) => cells.map((c, i) => {
|
|
369
|
+
if (!c.length)
|
|
370
|
+
return "";
|
|
371
|
+
const a = t.align[i];
|
|
372
|
+
const [x, anchor] = a === "center" ? [starts[i] + cols[i] / 2, "middle"]
|
|
373
|
+
: a === "right" ? [starts[i] + cols[i], "end"]
|
|
374
|
+
: [starts[i], "start"];
|
|
375
|
+
return at(x, y, anchor, lineContent(c));
|
|
376
|
+
}).join("");
|
|
377
|
+
// The rule sits halfway between the header's line box and the first
|
|
378
|
+
// row's, overhanging the outer cells by half a gap, as a table's border
|
|
379
|
+
// would.
|
|
380
|
+
const ruleY = base(first) - FONT_SIZE * 0.35 + step / 2;
|
|
381
|
+
const half = TABLE_GAP / 2;
|
|
382
|
+
const width = tableWidth(t, FONT_SIZE);
|
|
383
|
+
const rule = t.rows.length
|
|
384
|
+
? `<path d="M${r(left - half)},${r(ruleY)}h${r(width + TABLE_GAP)}" stroke="${C.line}" stroke-width="1" opacity="0.6" fill="none"/>`
|
|
385
|
+
: "";
|
|
386
|
+
return cell(t.header, base(first)) + rule + t.rows.map((row, k) => cell(row, base(first + 1 + k))).join("");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=svg.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The intermediate representation a ```mermaid fence parses into.
|
|
2
|
+
*
|
|
3
|
+
* Deliberately small. The dialect brane renders is the one its agents
|
|
4
|
+
* actually write — measured over every mermaid fence in the archive, not
|
|
5
|
+
* taken from mermaid's manual — and anything outside it falls back to the
|
|
6
|
+
* syntax-highlighted code block the fence renders as today.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|