Colorful, themeable rendering for Claude Code replies: tables, headings, code, numbers, paths and links.

<h1 align="center"><img src="docs/brand/banner.png" alt="prismantis" width="640"></h1>
Sees 16 colors. Your terminal only had 8.
Colorful, themeable replies for Claude Code: tables, code, diagrams, charts and tool calls, in 15 themes, with copy buttons on everything.


| Feature | What you get |
|---|---|
| Themes | /prismantis theme <name> switches on the spot. 15 MIT palettes (Catppuccin, Dracula, Nord, Tokyo Night, Gruvbox, Rosé Pine, Everforest, GitHub, One Dark, Solarized) plus 20 color slots you can override |
| Tables | colored headers, rules, column alignment, colored numbers, sized to the terminal |
| Code | a language header and copy button, Prism highlighting in 24 languages, shell lines colored like a prompt |
| Diagrams and charts | flowcharts, sequence, state, class and ER diagrams, bar and line charts, one color per box, participant and bar |
| Layout | back-to-back tables and diagrams sit side by side and wrap on narrow terminals |
| Copy buttons | [ ⧉ copy ] on code, tables, lists and quotes, plus ⧉ art on tables and diagrams for pasting into Slack, and /prismantis copy without a mouse |
| Tool rows | Ran gh pr view 12, Read ~/src/app.ts, groups summed up as Ran 3 commands, read 2 files, with status dots |
| Turn footer | ✻ Baked for 6m 20s with the duration in the number color |
| Slash commands | command output (/cost, /context, plugin commands) gets the same tables and code styling |
| Other mods | $.prismantis.markdown draws any markdown the way replies are drawn, for another mod's pane or band |
| Diagram hints | a short model-only note on each prompt so Claude reaches for diagrams and charts when they help |
| Text | bold, italic, strikethrough, inline code, links, versions, durations, percentages and paths in their own colors |
| Headings, lists, quotes | 4 heading styles, nested lists, task lists, quotes with an accent bar |
| Your prompts | what you type draws in a rounded bubble, an accent bar or a chevron, so you can find your turns when you scroll back |
| Right to left | Hebrew and Arabic read right to left, right aligned, with bullets, quote bars and table columns mirrored, in Warp, kitty, Apple Terminal and more |
Try it: ask Claude to print docs/demo.md verbatim as its whole reply. Every feature is in there.
Requires Claude Code 2.1.287 or later.
/plugin marketplace add NahumLitvin/prismantis
/plugin install prismantis@prismantis
To update, run claude plugin marketplace update prismantis && claude plugin update prismantis@prismantis, then /reload in every open session. A session keeps the version it loaded until it reloads.
Tested in the terminal on macOS; CI runs the tests on macOS, Linux and Windows. The desktop app, VS Code and mobile should work through the same mod API but have not been checked by hand yet. Turn it off any time in /plugin, and Claude Code's own renderer comes back. Press ctrl+o on a reply to see the original.
It's a Claude Code mod in plain TypeScript. It bundles two MIT libraries, beautiful-mermaid for diagrams and Prism for highlighting. It makes no network calls, reads no files and runs no commands. It redraws text already on your screen and, with diagramHints on, attaches a short model-only note to your prompts.
Dark: catppuccin-mocha (default), dracula, nord, tokyo-night, gruvbox-dark, rose-pine, everforest, github-dark, one-dark, solarized-dark.
Light: catppuccin-latte, gruvbox-light, rose-pine-dawn, github-light, solarized-light.
Switch on the spot with /prismantis theme nord, or run /prismantis for the help screen with every theme. mono uses no color, only bold and dim. Every palette is MIT licensed and credited in THIRD_PARTY_NOTICES.md.
Header cells take the tableHeader color, and every cell sits in a box with a double line under the header (tableStyle: box, or rules, grid and minimal for lighter looks), and :---:/---: alignment is honored. Numbers inside cells are colored like everywhere else. Columns shrink to fit the terminal.
Code blocks get a header row with the language on the left and a copy button on the right, and the code sits indented below with no frame, so selecting it with the mouse copies only the code.
bash, sh, zsh, unlabeled) color the command word, --flags, quoted strings and # comments, and restart after |, && and ;.Code blocks tagged mermaid draw as colored text art:
graph LR, graph TD, decisions), sequence, state, class and ER diagramsxychart-beta, sized to the terminal widthDiagrams too wide for the window, or over 80 lines, stay as code. mermaidAscii swaps box-drawing characters for + - |. Pie charts are not supported.
When tables and diagrams follow each other, they share a row and wrap to the next line once the terminal runs out of width. A wide terminal shows a table, a flowchart and two charts side by side.
[ ⧉ copy ], drawn in Claude Code's accent color, sits on code blocks, tables, lists and quotes and puts the raw markdown on your clipboard. Quotes copy without their > markers, ready to paste as a message. Diagrams get two: ⧉ source copies the mermaid code and ⧉ art copies the drawn art, ready to paste into a chat code block. Tables get ⧉ art too: a plain boxed table that reads right in Slack, where pasted markdown does not. Replies end with ⧉ copy reply, which copies the reply as Claude wrote it (one-line English narration gets none). Use it for Hebrew and Arabic: selecting right-to-left text on screen copies the letters in the order they are drawn, not the order they are read. Clicking works where the terminal passes clicks through (fullscreen mode does); terminals with copy-on-select, such as Warp, may grab the word "copy" instead. Without a mouse, /prismantis copy copies the last reply and /prismantis copy code its last code block. Since Claude Code 2.1.291 ctrl+x then tab focuses the area above the prompt, not the buttons in replies.
Each tool call draws as one line: a bold verb and its target, Ran with a colored shell command, Read and Edited with the path. The dot is green when done, hollow while running and red on failure. Output still draws below. Collapsed groups draw one line too, Ran 3 commands, read 2 files · last: npm test, with a red count when any call failed. Expand a group (ctrl+o or --verbose) and its calls draw with Claude Code's own rows, inline output included.
toolStyle keeps tool rows apart from what Claude says. The default, chat, puts them dimmed on the right, like the other side of a chat, and leaves Claude's sentences on the left. Rows are capped at 60% of the width.

tree-dim tucks them under the sentence with ⎿ and dims them.

tree-bold is tree-dim with one-line sentences in bold.

classic is the original look: a bold verb and a status dot, at full brightness.

The line that closes a turn keeps Claude Code's word and colors the duration: ✻ Baked for 6m 20s. Terminal only, since that's the only surface that draws it.
Output from slash commands, built-in or from other plugins, is parsed as markdown and drawn like a reply, copy buttons included. Errors keep Claude Code's own red line.
A mod that draws markdown in its own pane or band can have prismantis draw it, in the user's theme, by calling $.prismantis.markdown with the surface, the text and the columns it has.
undefined when prismantis is disabled or the text holds nothing to draw.try.columns is the width of your content. Prismantis draws at least 20 columns wide.let drawn
try {
drawn = await $.prismantis.markdown({ surface: e.surface, text, columns: e.props.bodyColumns })
} catch {}
return drawn ?? <Markdown text={text} />
The types are in types/index.d.ts. List prismantis under dependencies in your plugin.json to have Claude Code lay them into your .claude-plugin/types/. When prismantis is optional, declare the noun in your own contract instead:
declare module 'claude-code' {
interface EngineInterface {
prismantis: {
markdown: (args: { surface: RenderSurface; text: string; columns: number }) => Promise<RenderElement | undefined>
}
}
}
Claude rarely writes a chart unless it knows the terminal can draw one. With diagramHints on (the default), prismantis attaches a short note to each prompt you type, read by the model and never shown, saying tables, alerts, code, mermaid diagrams and xychart-beta charts render here and to use one when a numeric series or a flow is easier to see than read. It also asks for commands in fenced blocks, since only those get a copy button. It costs about 190 tokens per prompt. It's off whenever mermaid is off, and skipped for headless claude -p runs and background notifications. Claude Code doesn't let installed plugins edit the system prompt (its built-in sec-default policy keeps that for the organization), so the note rides along with your prompt instead.
Bold, italic, strikethrough, inline code, links and bare URLs, clickable as terminal hyperlinks (where the terminal has none, the URL shows dimmed after the text). Numbers, versions (v2.14.0), durations (250ms, 3h), sizes (16Gi) and percentages (99.9%) take the number color, and paths like ~/src/app.ts the path color.
headingStyle picks banner (the default: a box around H1, a heavy rule under H2), bold, underline or uppercase. Terminals have one font size, so headings stand out through style and color. Lists keep their numbers and nest with • and ◦. Quotes get an accent bar, and GitHub alerts (> [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]) draw as colored boxes. Single-series bar charts print each value above its bar and color the tallest one.
Task lists draw as [ ] and [✓], with done items dimmed and struck through. taskStyle switches to ticks (○ ✓), box (□ ✓) or progress, which adds a done-count bar above each list.

What you type, at the prompt or through Remote Control, draws in the theme's colors. promptStyle picks the look, and off keeps Claude Code's own. Task notifications and teammate messages are left alone.
bubble (default) | bar | chevron |
|---|---|---|
![]() | ![]() | ![]() |

Hebrew and Arabic blocks are right aligned, with bullets, numbers and quote bars on the right and table columns mirrored, while code, numbers, paths and links stay left to right inside them. Terminals differ in how they treat right-to-left letters, so prismantis detects yours and sends the letters the way it needs them. /prismantis demo-rtl shows every element, and the rtl option forces a terminal's handling or turns it off.
Open /config and look for the prismantis rows, or set values in ~/.claude/settings.json:
{
"pluginConfigs": {
"prismantis": {
"theme": "tokyo-night",
"tableStyle": "grid",
"headingStyle": "banner",
"tableHeaderColor": "#ffcc00",
"numberColor": "cyan"
}
}
}
| Option | Values | Default |
|---|---|---|
enabled | true, false | true |
theme | see Themes | catppuccin-mocha |
tableStyle | box, rules, grid, minimal | box |
taskStyle | checks ([ ] [✓], done struck through), ticks (○ ✓), box (□ ✓), progress (ticks with a done-count bar) | checks |
promptStyle | bubble, bar, chevron, off | bubble |
headingStyle | banner, bold, underline, uppercase | banner |
highlightNumbers | true, false | true |
highlightPaths | true, false | true |
toolRows | true, false | true |
toolStyle | chat, tree-dim, tree-bold, classic | chat |
copyButtons | true, false | true |
diagramHints | true, false | true |
rtl | auto, a terminal (warp, kitty, apple-terminal, iterm, ghostty, wezterm, vscode, alacritty, windows-terminal, gnome, konsole), off | auto |
mermaid | true, false | true |
mermaidAscii | true, false | false |
<token>Color | any color, see below | theme |
A color is hex (#a6e3a1, #fc0), rgb(166,227,161), ansi256(114) or a name (green, cyanBright). Values that don't parse are ignored. Every token has a <token>Color option and a row in /config:
| Token | Colors |
|---|---|
accent | reply bullet, H3+ headings, quote bar, running tool dots |
heading | H1 and H2 |
strong | bold text |
emphasis | italic text, variables, attribute names |
inlineCode | inline code |
codeText | code block text |
codeCommand | shell commands, functions, class names, keys |
codeFlag | --flags, keywords, failures |
codeString | strings |
codeComment | comments, the code block's language label |
link | links, URLs, properties, tags |
path | file paths, regexes |
number | numbers, versions, durations, done dots |
quote | quote text |
rule | rules, chart gridlines |
tableHeader | table header cells |
tableRule | table rules |
bullet | list bullets and numbers |
diagram | diagram lines |
diagramText | diagram labels |
codeText.Planned features are on the roadmap board, one issue each. Give an issue a 👍 to vote for it, or open one for what you miss.
git clone https://github.com/NahumLitvin/prismantis
claude --plugin-dir ./prismantis
Edits hot-reload in that session. Before a PR run claude plugin validate . and claude plugin test ., and print docs/demo.md to check the look. npm --prefix scripts run bench times a full render of the demo reply, so speed claims can be checked on any machine. CI also type-checks, rebuilds the vendored bundles byte for byte and installs from a clean config. See CONTRIBUTING.md.
Built by Nahum Litvin, who writes about running untrusted code in production at catchkill9.dev.
MIT. Bundled third-party code is listed in THIRD_PARTY_NOTICES.md.
hooks/register.tsx 214 lines1import type { EngineInterface, Register, RenderElement } from 'claude-code'
2
3import { parse } from './markdown'
4import { boxArt, mermaidText } from './mermaid'
5import type { Drawn } from './render'
6import { remember, renderBlocks, renderExpandedShell, renderToolGroup, renderToolRow, renderTurnDuration, renderUserPrompt, width } from './render'
7import { helpText, rtlShowcaseText, showcaseText } from './help'
8import { PRESET_NAMES } from './presets'
9import type { Style } from './theme'
10import { resolveStyle } from './theme'
11import type { Terminal } from './rtl'
12import { TERMINALS, hasRtl } from './rtl'
13
14const HINT = [
15 'Replies in this session are drawn by the prismantis mod, which runs inside Claude Code and is not a command or tool to call: when the user asks to show something with prismantis, write it as markdown in the reply.',
16 'Markdown tables, GitHub alerts (> [!WARNING], > [!NOTE]), fenced code with a language tag, and ```mermaid blocks render as colored terminal graphics:',
17 'flowcharts, sequence diagrams and xychart-beta bar or line charts.',
18 'When a reply carries a numeric series or a flow that is easier to see than read, add one small diagram or chart with short labels.',
19 'Skip diagrams for simple answers.',
20 'Put any command or snippet the user may run or copy in a fenced block with a language tag, never inline code: fenced blocks get a copy button, inline code does not.',
21].join(' ')
22
23const detectTerminal = async ($: EngineInterface): Promise<Terminal | null> => {
24 const program = await $.env.get('TERM_PROGRAM')
25 const term = await $.env.get('TERM')
26 if ((await $.env.get('KITTY_WINDOW_ID')) || term === 'xterm-kitty') return 'kitty'
27 if (program === 'Apple_Terminal') return 'apple-terminal'
28 if (program === 'WarpTerminal') return 'warp'
29 if (program === 'ghostty') return 'ghostty'
30 if (program === 'WezTerm') return 'wezterm'
31 if (program === 'vscode') return 'vscode'
32 if (program === 'iTerm.app') return 'iterm'
33 if (term === 'alacritty' || (await $.env.get('ALACRITTY_WINDOW_ID'))) return 'alacritty'
34 if (await $.env.get('WT_SESSION')) return 'windows-terminal'
35 if (await $.env.get('VTE_VERSION')) return 'gnome'
36 if (await $.env.get('KONSOLE_VERSION')) return 'konsole'
37 return null
38}
39
40const applyRtl = async ($: EngineInterface, style: Style): Promise<void> => {
41 if (style.rtl !== 'auto') return
42 const terminal = await detectTerminal($)
43 style.reorder = terminal !== null
44 if (terminal) style.shape = TERMINALS[terminal]
45}
46
47const expandedCalls = new Set<string>()
48
49const savePng = async ($: EngineInterface, art: string): Promise<string> => {
50 const home = (await $.env.get('USERPROFILE')) ?? (await $.env.get('HOME')) ?? '.'
51 const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
52 const tmp = (await $.env.get('TEMP')) ?? home
53 const out = `${tmp}/prismantis-${stamp}.png`
54 const script = `${$.plugin.root}/scripts/text2png.py`
55 for (const python of ['python', 'py', 'python3']) {
56 const run = await $.process.run([python, script, out], { stdin: art }).catch(() => undefined)
57 if (run?.exitCode === 0) return out
58 }
59 throw new Error('no python with pillow')
60}
61
62const copyPngToClipboard = async ($: EngineInterface, path: string): Promise<boolean> => {
63 const file = path.replace(/\//g, '\\')
64 const script = `Add-Type -AssemblyName System.Windows.Forms,System.Drawing; $f='${file.replace(/'/g, "''")}'; $i=[System.Drawing.Image]::FromFile($f); $b=New-Object System.Drawing.Bitmap($i); $i.Dispose(); [System.Windows.Forms.Clipboard]::SetImage($b); $b.Dispose(); Remove-Item -LiteralPath $f`
65 const run = await $.process.run(['powershell', '-NoProfile', '-STA', '-Command', script]).catch(() => undefined)
66 return run?.exitCode === 0
67}
68
69const drawMarkdown = ($: EngineInterface, el: ReturnType<EngineInterface['ui']['resolve']>, style: Style, blocks: ReturnType<typeof parse>, columns: number, reply?: string): RenderElement[] => {
70 const { Button } = el
71 const copy = (text: string | (() => string), key: string, label = '⧉ copy') =>
72 style.copyButtons ? (
73 <Button
74 key={key}
75 variant="primary"
76 label={label}
77 onPress={press => {
78 $.ui.copy({ text: typeof text === 'function' ? text() : text, surface: press.surface })
79 .then(r => $.ui.toast(r.isCopied ? 'Copied' : `Copy failed: ${r.reason}`))
80 .catch(() => $.ui.toast('Copy failed'))
81 }}
82 />
83 ) : null
84 const png = (text: string | (() => string), key: string, label = '▣ png') =>
85 style.copyButtons ? (
86 <Button
87 key={key}
88 variant="primary"
89 label={label}
90 onPress={() => {
91 savePng($, typeof text === 'function' ? text() : text)
92 .then(async path => $.ui.toast((await copyPngToClipboard($, path)) ? 'Copied image' : `Clipboard copy failed, image kept at ${path}`))
93 .catch(() => $.ui.toast('PNG save failed (needs python + pillow)'))
94 }}
95 />
96 ) : null
97 const drawn: Drawn = new Map()
98 if (style.mermaid) {
99 for (const [i, block] of blocks.entries()) {
100 if (block.kind !== 'code' || block.lang.toLowerCase() !== 'mermaid') continue
101 const art = mermaidText(block.lines.join('\n'), style.mermaidAscii, columns)
102 if (art !== null && art.split('\n').every(l => width(l) <= columns - 2)) drawn.set(i, { element: boxArt(el, style, art, `b${i}`), art })
103 }
104 }
105 const elements = renderBlocks(el, style, blocks, columns, drawn, copy, png)
106 const button = reply === undefined ? null : copy(reply, 'reply', '⧉ copy reply')
107 return button ? [...elements, <el.Box key="reply" alignSelf="flex-end">{button}</el.Box>] : elements
108}
109
110export const register: Register = (on, options) => {
111 on('engine.create', async (_$, e, next) => ({ ...(await next(e)), prismantis: { markdown: async () => undefined } }))
112 if (options.enabled === false) return
113 const style = resolveStyle(options)
114 const parsed = new Map<string, ReturnType<typeof parse>>()
115 const parseCached = (text: string, cache = parsed, limit?: number) => remember(cache, text, () => parse(text, { numbers: style.highlightNumbers, paths: style.highlightPaths }), limit)
116 const shared = new Map<string, ReturnType<typeof parse>>()
117
118 if (options.toolRows !== false) {
119 on('ui.render', { component: 'ToolGroup' }, ($, e, next) => {
120 if (e.props.isExpanded) {
121 for (const call of e.props.calls) if (call.tool_use_id) expandedCalls.add(call.tool_use_id)
122 return next(e)
123 }
124 return renderToolGroup($.ui.resolve(e), style, e.props.calls, e.props.isActive, e.viewport?.columns)
125 })
126 on('ui.render', { component: 'ToolUse' }, ($, e, next) => {
127 if (!expandedCalls.has(e.props.tool_use_id)) return renderToolRow($.ui.resolve(e), style, e.props, e.viewport?.columns)
128 return e.props.tool === 'Bash' || e.props.tool === 'PowerShell' ? renderExpandedShell($.ui.resolve(e), style, e.props) : next(e)
129 })
130 }
131
132 on('session.start', async ($, e, next) => {
133 await applyRtl($, style)
134 const started = await next(e)
135 await $.command
136 .register({ name: 'prismantis', description: 'Switch the prismantis theme, copy the last reply, or show the demo', argumentHint: '[theme <name> | copy [code] | demo]' })
137 .catch(() => undefined)
138 return started
139 })
140
141 on('command.run', { command: 'prismantis' }, async ($, e) => {
142 const [sub, name] = e.args.trim().split(/\s+/)
143 if (sub === 'demo') return { text: showcaseText(PRESET_NAMES) }
144 if (sub === 'copy') {
145 const reply = (await $.session.messages()).findLast(m => m.role === 'assistant' && m.text.trim())
146 if (!reply) return { text: 'Nothing to copy yet.' }
147 const code = name === 'code' ? parseCached(reply.text).findLast(b => b.kind === 'code') : undefined
148 if (name === 'code' && code?.kind !== 'code') return { text: 'The last reply has no code block.' }
149 const result = await $.ui.copy({ text: code?.kind === 'code' ? code.lines.join('\n') : reply.text })
150 return { text: result.isCopied ? `Copied the last ${code ? 'code block' : 'reply'}.` : `Copy failed: ${result.reason}` }
151 }
152 if (sub === 'demo-rtl') {
153 await applyRtl($, style)
154 return { text: rtlShowcaseText() }
155 }
156 if (sub !== 'theme' || !name) return { text: helpText(PRESET_NAMES) }
157 if (!(PRESET_NAMES as readonly string[]).includes(name)) return { text: `Unknown theme "${name}". Themes: ${PRESET_NAMES.join(', ')}` }
158 const result = await $.config.set({ key: `${$.plugin.name}.theme`, value: name })
159 return { text: result.deny ? `Could not switch theme: ${result.deny}` : `Theme set to ${name}.` }
160 })
161
162 on('ui.render', { component: 'TurnDuration' }, ($, e) => renderTurnDuration($.ui.resolve(e), style, e.props.word, e.props.durationMs))
163
164 on('prompt.submit', async ($, e, next) => {
165 await applyRtl($, style)
166 if (!style.diagramHints || (e.origin.kind !== 'composer' && e.origin.kind !== 'bridge')) return next(e)
167 return next({ ...e, context: [...(e.context ?? []), HINT] })
168 })
169
170 on('ui.render', { component: 'CommandOutput' }, ($, e, next) => {
171 if (e.props.isErrored) return next(e)
172 const blocks = parseCached(e.props.text)
173 if (blocks.length === 0) return next(e)
174 const el = $.ui.resolve(e)
175 const { Box } = el
176 const columns = Math.max(20, (e.viewport?.columns ?? 100) - 4)
177 return <Box flexDirection="column" rowGap={1} {...(style.reorder && hasRtl(e.props.text) ? { width: '100%' } : {})}>{drawMarkdown($, el, style, blocks, columns)}</Box>
178 })
179
180 on('ui.render', { component: 'UserMessage' }, ($, e, next) => {
181 const kind = e.props.origin.kind
182 const own = kind === 'composer' || kind === 'bridge' || (kind === 'unclassified' && !e.props.from && !e.props.task)
183 if (style.promptStyle === 'off' || !own) return next(e)
184 return renderUserPrompt($.ui.resolve(e), style, e.props.text, Math.max(20, (e.viewport?.columns ?? 100) - 4))
185 })
186
187 on('ui.render', { component: 'AssistantMessage' }, ($, e, next) => {
188 const blocks = parseCached(e.props.text)
189 if (blocks.length === 0) return next(e)
190 const el = $.ui.resolve(e)
191 const { Box, Text } = el
192 const columns = Math.max(20, (e.viewport?.columns ?? 100) - 4)
193 const narration = style.toolStyle === 'tree-bold' && blocks.length === 1 && blocks[0]!.kind === 'paragraph'
194 return (
195 <Box flexDirection="row">
196 <Box width={2} flexShrink={0}>
197 <Text color={style.theme.accent}>{e.props.isFirstOfReply ? '●' : ' '}</Text>
198 </Box>
199 <Box flexDirection="column" rowGap={1} flexGrow={1}>
200 {drawMarkdown($, el, narration ? { ...style, narration } : style, blocks, columns, blocks.length > 1 || hasRtl(e.props.text) ? e.props.text : undefined)}
201 </Box>
202 </Box>
203 )
204 })
205
206 on('prismantis.markdown', ($, e, next) => {
207 const blocks = parseCached(e.text, shared, 20)
208 if (blocks.length === 0) return next(e)
209 const el = $.ui.resolve({ surface: e.surface, component: 'AssistantMessage' })
210 const { Box } = el
211 return { value: <Box flexDirection="column" rowGap={1} {...(style.reorder && hasRtl(e.text) ? { width: '100%' } : {})}>{drawMarkdown($, el, { ...style, copyButtons: false }, blocks, Math.max(20, e.columns || 0))}</Box> }
212 })
213}
214hooks/markdown.ts 208 lines1export type Inline =
2 | { kind: 'text'; text: string }
3 | { kind: 'strong'; children: Inline[] }
4 | { kind: 'emphasis'; children: Inline[] }
5 | { kind: 'strike'; children: Inline[] }
6 | { kind: 'code'; text: string }
7 | { kind: 'link'; text: string; href: string }
8 | { kind: 'number'; text: string }
9 | { kind: 'path'; text: string }
10 | { kind: 'dim'; text: string }
11
12export type Block = { raw: string } & (
13 | { kind: 'heading'; level: number; inline: Inline[] }
14 | { kind: 'paragraph'; inline: Inline[] }
15 | { kind: 'list'; ordered: boolean; items: { marker: string; depth: number; task?: boolean; inline: Inline[] }[] }
16 | { kind: 'code'; lang: string; lines: string[] }
17 | { kind: 'quote'; inline: Inline[] }
18 | { kind: 'alert'; level: AlertLevel; inline: Inline[] }
19 | { kind: 'rule' }
20 | { kind: 'table'; header: Inline[][]; align: ('left' | 'right' | 'center')[]; rows: Inline[][][] }
21)
22
23type Draft = Block extends infer B ? (B extends unknown ? Omit<B, 'raw'> : never) : never
24
25export type AlertLevel = 'note' | 'tip' | 'important' | 'warning' | 'caution'
26const ALERT = /^\[!(note|tip|important|warning|caution)\]\s*(.*)$/i
27
28export type Highlight = { numbers: boolean; paths: boolean }
29
30const MAX_INLINE = 4000
31const TABLE_SEP = /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/
32const LIST_ITEM = /^(\s*)([-*+]|\d+[.)])\s+(.*)$/
33const FENCE = /^\s*(`{3,}|~{3,})\s*([\w+-]*)/
34
35const splitRow = (line: string): string[] => {
36 const trimmed = line.trim().replace(/^\|/, '').replace(/(?<!\\)\|$/, '')
37 const cells: string[] = []
38 let cell = ''
39 for (let i = 0; i < trimmed.length; i++) {
40 const ch = trimmed[i]
41 if (ch === '\\' && trimmed[i + 1] === '|') {
42 cell += '|'
43 i++
44 } else if (ch === '|') {
45 cells.push(cell.trim())
46 cell = ''
47 } else {
48 cell += ch
49 }
50 }
51 cells.push(cell.trim())
52 return cells
53}
54
55const INLINE = /(`+)(?!`)(.+?)(?<!`)\1(?!`)|\[([^\]]+)\]\(([^)\s]+)\)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|(?<![\w*])\*([^*\s][^*]*?)\*(?!\w)|(?<![\w_])_([^_\s][^_]*?)_(?!\w)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/g
56const NUMBER = /(?<![\w.#/-])(v?\d+(?:[.,:]\d+)*(?:%|ms|s|m|h|d|Gi|Mi|GB|MB|KB|x)?)(?![\w/])/g
57const PATH = /(?<![\w/.:])((?:~|\.{1,2})?\/[\w.@+-]+(?:\/[\w.@+-]*)*)/g
58
59const decorate = (text: string, hl: Highlight): Inline[] => {
60 if (!hl.numbers && !hl.paths) return [{ kind: 'text', text }]
61 const marks: { start: number; end: number; kind: 'number' | 'path' }[] = []
62 if (hl.paths) for (const m of text.matchAll(PATH)) marks.push({ start: m.index, end: m.index + m[0].length, kind: 'path' })
63 if (hl.numbers) {
64 for (const m of text.matchAll(NUMBER)) {
65 const start = m.index
66 if (!marks.some(p => start < p.end && start + m[0].length > p.start)) {
67 marks.push({ start, end: start + m[0].length, kind: 'number' })
68 }
69 }
70 }
71 marks.sort((a, b) => a.start - b.start)
72 const out: Inline[] = []
73 let at = 0
74 for (const m of marks) {
75 if (m.start > at) out.push({ kind: 'text', text: text.slice(at, m.start) })
76 out.push({ kind: m.kind, text: text.slice(m.start, m.end) })
77 at = m.end
78 }
79 if (at < text.length) out.push({ kind: 'text', text: text.slice(at) })
80 return out
81}
82
83export const parseInline = (text: string, hl: Highlight): Inline[] => {
84 if (text.length > MAX_INLINE) return [{ kind: 'text', text }]
85 const out: Inline[] = []
86 let at = 0
87 for (const m of text.matchAll(INLINE)) {
88 if (m.index > at) out.push(...decorate(text.slice(at, m.index), hl))
89 if (m[2] !== undefined) out.push({ kind: 'code', text: m[2] })
90 else if (m[3] !== undefined) out.push({ kind: 'link', text: m[3], href: m[4] ?? "" })
91 else if (m[5] !== undefined || m[6] !== undefined) out.push({ kind: 'strong', children: parseInline(m[5] ?? m[6] ?? "", hl) })
92 else if (m[7] !== undefined) out.push({ kind: 'strike', children: parseInline(m[7], hl) })
93 else if (m[8] !== undefined || m[9] !== undefined) out.push({ kind: 'emphasis', children: parseInline(m[8] ?? m[9] ?? "", hl) })
94 else if (m[10] !== undefined) out.push({ kind: 'link', text: m[10], href: m[10] })
95 at = m.index + m[0].length
96 }
97 if (at < text.length) out.push(...decorate(text.slice(at), hl))
98 return out
99}
100
101export const inlineText = (inline: Inline[]): string =>
102 inline.map(n => ('children' in n ? inlineText(n.children) : n.text)).join('')
103
104export const parse = (source: string, hl: Highlight): Block[] => {
105 const lines = source.replace(/\r\n?/g, '\n').split('\n')
106 const at = (n: number) => lines[n] ?? ''
107 const blocks: Block[] = []
108 const add = (block: Draft, from: number, to: number) => blocks.push({ ...block, raw: lines.slice(from, to).join('\n') } as Block)
109 let paraStart = 0
110 let para: string[] = []
111
112 const flush = (end: number) => {
113 if (para.length) add({ kind: 'paragraph', inline: parseInline(para.join(' '), hl) }, paraStart, end)
114 para = []
115 }
116
117 for (let i = 0; i < lines.length; i++) {
118 const line = at(i)
119 const fence = FENCE.exec(line)
120 if (fence) {
121 flush(i)
122 const start = i
123 const run = fence[1] ?? '```'
124 const closer = new RegExp(`^\\s*\\${run[0]}{${run.length},}\\s*$`)
125 const body: string[] = []
126 i++
127 while (i < lines.length && !closer.test(at(i))) body.push(at(i++))
128 add({ kind: 'code', lang: fence[2] ?? '', lines: body }, start, i + 1)
129 continue
130 }
131 if (line.trim() === '') {
132 flush(i)
133 continue
134 }
135 const heading = /^(#{1,6})\s+(.*?)\s*#*\s*$/.exec(line)
136 if (heading) {
137 flush(i)
138 add({ kind: 'heading', level: (heading[1] ?? '#').length, inline: parseInline(heading[2] ?? '', hl) }, i, i + 1)
139 continue
140 }
141 if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(line)) {
142 flush(i)
143 add({ kind: 'rule' }, i, i + 1)
144 continue
145 }
146 if (line.includes('|') && i + 1 < lines.length && TABLE_SEP.test(at(i + 1))) {
147 flush(i)
148 const start = i
149 const header = splitRow(line)
150 const align = splitRow(at(i + 1)).map(c =>
151 c.startsWith(':') && c.endsWith(':') ? 'center' : c.endsWith(':') ? 'right' : 'left',
152 )
153 i += 2
154 const rows: Inline[][][] = []
155 while (i < lines.length && at(i).includes('|') && at(i).trim() !== '') {
156 const cells = splitRow(at(i++))
157 rows.push(header.map((_, c) => parseInline(cells[c] ?? '', hl)))
158 }
159 add({ kind: 'table', header: header.map(c => parseInline(c, { numbers: false, paths: false })), align, rows }, start, i)
160 i--
161 continue
162 }
163 if (/^\s*>/.test(line)) {
164 flush(i)
165 const start = i
166 const body: string[] = []
167 while (i < lines.length && /^\s*>/.test(at(i))) body.push(at(i++).replace(/^\s*>\s?/, ''))
168 const alert = ALERT.exec(body[0] ?? '')
169 if (alert) add({ kind: 'alert', level: alert[1]!.toLowerCase() as AlertLevel, inline: parseInline([alert[2]!, ...body.slice(1)].join(' ').trim(), hl) }, start, i)
170 else add({ kind: 'quote', inline: parseInline(body.join(' '), hl) }, start, i)
171 i--
172 continue
173 }
174 const item = LIST_ITEM.exec(line)
175 if (item) {
176 flush(i)
177 const start = i
178 const ordered = /\d/.test(item[2] ?? '')
179 const items: { marker: string; depth: number; task?: boolean; inline: Inline[] }[] = []
180 const contentIndent: number[] = []
181 while (i < lines.length) {
182 const it = LIST_ITEM.exec(at(i))
183 if (it) {
184 contentIndent.push((it[1] ?? '').replace(/\t/g, ' ').length + (it[2] ?? '').length + 1)
185 const task = /^\[([ xX])\]\s+(.*)$/.exec(it[3] ?? '')
186 items.push({ marker: it[2] ?? '-', depth: Math.floor((it[1] ?? '').replace(/\t/g, ' ').length / 2), ...(task ? { task: task[1] !== ' ' } : {}), inline: parseInline(task ? task[2]! : it[3] ?? '', hl) })
187 } else if (/^\s{2,}\S/.test(at(i)) && items.length) {
188 const indent = (at(i).match(/^\s*/)?.[0] ?? '').replace(/\t/g, ' ').length
189 let owner = items.length - 1
190 while (owner > 0 && contentIndent[owner]! > indent) owner--
191 const target = items[owner]!
192 target.inline = [...target.inline, { kind: 'text', text: ' ' }, ...parseInline(at(i).trim(), hl)]
193 } else {
194 break
195 }
196 i++
197 }
198 add({ kind: 'list', ordered, items }, start, i)
199 i--
200 continue
201 }
202 if (!para.length) paraStart = i
203 para.push(line.trim())
204 }
205 flush(lines.length)
206 return blocks
207}
208hooks/mermaid.tsx 138 lines1import type { ElementTable, RenderElement } from 'claude-code'
2
3import { remember } from './render'
4import type { Style } from './theme'
5import { renderMermaidAscii, setChartSize } from './vendor/mermaid-text.js'
6
7const MAX_LINES = 80
8const textCache = new Map<string, string | null>()
9
10export const chartSize = (columns: number, source = '') => {
11 const labels = (/^\s*x-axis\b[^[\n]*\[([^\]\n]*)\]/m.exec(source)?.[1] ?? '').split(',').map(s => s.trim().replace(/^"|"$/g, ''))
12 const fit = labels.length * (Math.max(...labels.map(l => l.length)) + 2)
13 const width = Math.max(24, Math.min(60, Math.max(Math.floor(columns / 6), fit), columns - 12))
14 return { width, height: Math.max(8, Math.min(20, Math.round(width * 0.3))) }
15}
16
17const unquoteCategories = (source: string) =>
18 source.replace(/^(\s*x-axis\b[^[\n]*\[)([^\]\n]*)\]/m, (_, head: string, items: string) => `${head}${items.replace(/"([^"]*)"/g, (_q, s: string) => s.replaceAll(',', ' '))}]`)
19
20const labelBars = (art: string, source: string): string => {
21 const series = source.match(/^\s*(bar|line)\b.*$/gm) ?? []
22 const values = /^\s*bar\b[^[\n]*\[([^\]\n]*)\]/m.exec(source)?.[1]?.split(',').map(v => v.trim())
23 if (series.length !== 1 || !values || /^\s*xychart(-beta)?\s+horizontal/m.test(source)) return art
24 const grid = art.split('\n').map(l => [...l])
25 const axis = grid.findLastIndex(row => row.includes('┬'))
26 const ticks = grid[axis]?.flatMap((ch, x) => (ch === '┬' ? [x] : [])) ?? []
27 if (ticks.length !== values.length) return art
28 ticks.forEach((x, k) => {
29 const top = grid.findIndex(row => row[x] === '█')
30 const y = top === -1 ? axis - 1 : top - 1
31 const text = [...values[k]!]
32 const from = x - Math.floor((text.length - 1) / 2)
33 const row = grid[y]
34 if (!row || y < 0) return
35 while (row.length < from + text.length) row.push(' ')
36 if (!text.every((_, i) => /[ ·]/.test(row[from + i] ?? ' '))) return
37 text.forEach((ch, i) => (row[from + i] = ch))
38 })
39 return grid.map(row => row.join('').trimEnd()).join('\n')
40}
41
42export const mermaidText =(source: string, ascii: boolean, columns: number): string | null => {
43 if (source.length > 8000 || source.split('\n').length > MAX_LINES) return null
44 const isChart = /^\s*xychart/.test(source)
45 const size = chartSize(columns, isChart ? source : '')
46 const key = `${ascii}:${isChart ? size.width : 0}:${source}`
47 return remember(textCache, key, () => {
48 try {
49 if (isChart) setChartSize(size.width, size.height)
50 const art = renderMermaidAscii(unquoteCategories(source.replace(/^(\s*%%[^\n]*\n)+/, '')).replace(/(-->|-\.->|==>|---|-\.-|===)[ \t]+\|/g, '$1|'), { useAscii: ascii, colorMode: 'none', paddingX: 3, paddingY: 1 }).replace(/[ \t]+$/gm, '').trimEnd().replace(/▶/g, '►').replace(/◀/g, '◄')
51 return isChart ? labelBars(art, source) : art.split('\n').filter(l => !/^[\s│|]*$/.test(l)).join('\n')
52 } catch {
53 return null
54 }
55 })
56}
57
58const LINE = /[─-╿◇]/
59const ARROW = /[►◄▲▼]/
60
61const paint = (art: string, style: Style): (string | undefined)[][] => {
62 const t = style.theme
63 const grid = art.split('\n').map(l => [...l])
64 const cell = (r: number, c: number) => grid[r]?.[c] ?? ''
65 const color: (string | undefined)[][] = grid.map(row => row.map(() => undefined))
66 const palette = [...new Set([t.link, t.number, t.heading, t.emphasis, t.path, t.codeFlag, t.accent])].filter((c): c is string => c !== undefined)
67 const next = (i: number) => palette[i % palette.length]
68 const labels = new Map<string, string | undefined>()
69
70 const rects: { r: number; c: number; r2: number; c2: number }[] = []
71 for (let r = 0; r < grid.length; r++) {
72 for (let c = 0; c < (grid[r]?.length ?? 0); c++) {
73 if (!/[┌╭(]/.test(cell(r, c))) continue
74 let c2 = c + 1
75 while (/[─┬┴┼▲▼]/.test(cell(r, c2))) c2++
76 if (!/[┐╮)]/.test(cell(r, c2)) || c2 === c + 1) continue
77 let r2 = r + 1
78 while (/[│├┤┼►◄]/.test(cell(r2, c))) r2++
79 if (!/[└╰(]/.test(cell(r2, c)) || !/[┘╯)]/.test(cell(r2, c2))) continue
80 rects.push({ r, c, r2, c2 })
81 }
82 }
83 const inside = (a: (typeof rects)[number], b: (typeof rects)[number]) => a !== b && b.r > a.r && b.r2 < a.r2 && b.c > a.c && b.c2 < a.c2
84 for (const box of rects.filter(a => !rects.some(b => inside(a, b)))) {
85 const { r, c, r2, c2 } = box
86 const label = grid.slice(r + 1, r2).map(row => row.slice(c + 1, c2).join('')).join(' ').trim()
87 if (!labels.has(label)) labels.set(label, next(labels.size))
88 const hue = labels.get(label)
89 for (let y = r; y <= r2; y++) for (let x = c; x <= c2; x++) if (cell(y, x).trim()) color[y]![x] = hue
90 }
91
92 const bars = [...new Set(grid.flatMap(row => row.flatMap((ch, c) => (ch === '█' && row[c - 1] !== '█' ? [c] : []))))].sort((a, b) => a - b)
93 const ticks = grid.findLast(row => row.includes('┬'))?.filter(ch => ch === '┬').length ?? 0
94 const tops = bars.map(x => grid.findIndex(row => row[x] === '█'))
95 const single = bars.length > 1 && bars.length <= ticks
96 const barColor = (i: number) => (single ? (tops[i] === Math.min(...tops) ? t.emphasis ?? t.accent : t.quote) : next(i))
97 grid.forEach((row, r) =>
98 row.forEach((ch, c) => {
99 if (color[r]![c] !== undefined) return
100 if (ch === '█') {
101 let start = c
102 while (row[start - 1] === '█') start--
103 color[r]![c] = barColor(bars.indexOf(start))
104 } else if (ch === '·') color[r]![c] = t.rule
105 else if (ARROW.test(ch)) color[r]![c] = t.accent
106 else if (LINE.test(ch)) color[r]![c] = t.diagram
107 else if (/^\d+[┤┼]/.test(row.slice(c).join(''))) color[r]![c] = t.number
108 else color[r]![c] = t.diagramText
109 }),
110 )
111 return color
112}
113
114const painted = new WeakMap<Style, Map<string, (string | undefined)[][]>>()
115
116export const boxArt = ({ Box, Text }: ElementTable, style: Style, art: string, key: string): RenderElement => {
117 const cache = painted.get(style) ?? new Map<string, (string | undefined)[][]>()
118 painted.set(style, cache)
119 const colors = remember(cache, art, () => paint(art, style))
120 return (
121 <Box key={key} flexDirection="column" paddingLeft={2}>
122 {art.split('\n').map((line, i) => {
123 const chars = [...line]
124 const parts: RenderElement[] = []
125 let at = 0
126 while (at < chars.length) {
127 const hue = colors[i]?.[at]
128 let end = at + 1
129 while (end < chars.length && colors[i]?.[end] === hue) end++
130 parts.push(<Text key={`t${parts.length}`} color={hue}>{chars.slice(at, end).join('')}</Text>)
131 at = end
132 }
133 return <Text key={`${key}.${i}`}>{parts.length ? parts : ' '}</Text>
134 })}
135 </Box>
136 )
137}
138hooks/render.tsx 728 lines1import type { ElementTable, RenderElement } from 'claude-code'
2
3import type { Block, Inline } from './markdown'
4import { inlineText } from './markdown'
5import { commentTail, commentVisual, flow, hasRtl } from './rtl'
6import type { Style, Theme } from './theme'
7import type { PrismToken } from './vendor/prism.js'
8import { languages, tokenize } from './vendor/prism.js'
9
10const WIDE = /[\u1100-\u115F\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFF60\uFFE0-\uFFE6]|\p{Extended_Pictographic}/u
11const segmenter = typeof Intl !== 'undefined' && 'Segmenter' in Intl ? new Intl.Segmenter() : undefined
12
13const graphemes = (s: string): string[] => (segmenter ? [...segmenter.segment(s)].map(g => g.segment) : [...s])
14
15export const width = (s: string): number =>
16 graphemes(s).reduce((w, g) => (/^\p{M}+$/u.test(g) ? w : w + (WIDE.test(g) ? 2 : 1)), 0)
17
18const wrapRanges = (text: string, w: number): [number, number][] => {
19 if (width(text) <= w) return [[0, text.length]]
20 const out: [number, number][] = []
21 let line: [number, number] | undefined
22 for (const word of text.matchAll(/\S+/g)) {
23 let start = word.index
24 const end = start + word[0].length
25 while (width(text.slice(start, end)) > w) {
26 if (line) out.push(line)
27 line = undefined
28 let cut = start
29 for (const g of graphemes(text.slice(start, end))) {
30 if (cut > start && width(text.slice(start, cut + g.length)) > w) break
31 cut += g.length
32 }
33 out.push([start, cut])
34 start = cut
35 }
36 if (start === end) continue
37 if (line && width(text.slice(line[0], end)) <= w) line = [line[0], end]
38 else {
39 if (line) out.push(line)
40 line = [start, end]
41 }
42 }
43 if (line) out.push(line)
44 return out.length ? out : [[0, 0]]
45}
46
47const sliceInline = (nodes: Inline[], from: number, to: number): Inline[] => {
48 const out: Inline[] = []
49 let at = 0
50 for (const n of nodes) {
51 const length = inlineText([n]).length
52 const a = Math.max(from - at, 0)
53 const b = Math.min(to - at, length)
54 at += length
55 if (a >= b) continue
56 if ('children' in n) out.push({ ...n, children: sliceInline(n.children, a, b) })
57 else out.push({ ...n, text: n.text.slice(a, b) })
58 }
59 return out
60}
61
62const hasLink = (nodes: Inline[]): boolean => nodes.some(n => n.kind === 'link' || ('children' in n && hasLink(n.children)))
63
64const mostLines = (text: string, w: number): number => (text.match(/\S+/g)?.length ?? 0) + Math.ceil(width(text) / w)
65
66const wrapInline = (nodes: Inline[], w: number): Inline[][] => {
67 const text = inlineText(nodes)
68 return width(text) <= w ? [nodes] : wrapRanges(text, w).map(([a, b]) => sliceInline(nodes, a, b))
69}
70
71const flowOf = (style: Style, nodes: Inline[], columns: number) => (style.reorder ? flow(nodes, columns, width, style.shape) : null)
72
73const renderInline = (el: ElementTable, style: Style, nodes: Inline[], keyBase: string): RenderElement[] => {
74 const { Text } = el
75 const t = style.theme
76 return nodes.map((n, i) => {
77 const key = `${keyBase}.${i}`
78 switch (n.kind) {
79 case 'text':
80 return <Text key={key}>{n.text}</Text>
81 case 'strong':
82 return <Text key={key} bold color={t.strong}>{renderInline(el, style, n.children, key)}</Text>
83 case 'emphasis':
84 return <Text key={key} italic color={t.emphasis}>{renderInline(el, style, n.children, key)}</Text>
85 case 'strike':
86 return <Text key={key} strikethrough dimColor>{renderInline(el, style, n.children, key)}</Text>
87 case 'code':
88 return <Text key={key} color={t.inlineCode}>{n.text}</Text>
89 case 'link':
90 return /^[a-z][\w+.-]*:/i.test(n.href)
91 ? n.text === n.href ? <el.Link key={key} href={n.href} /> : <el.Link key={key} href={n.href}><Text color={t.link} underline>{n.text}</Text></el.Link>
92 : <Text key={key} color={t.link} underline>{n.text}</Text>
93 case 'number':
94 return <Text key={key} color={t.number}>{n.text}</Text>
95 case 'path':
96 return <Text key={key} color={t.path}>{n.text}</Text>
97 case 'dim':
98 return <Text key={key} dimColor>{n.text}</Text>
99 }
100 })
101}
102
103const renderFlow = (el: ElementTable, style: Style, lines: Inline[][], key: string, props: { italic?: boolean; color?: string; bold?: boolean } = {}) => {
104 const { Text } = el
105 return lines.map((line, i) => <Text key={`${key}.${i}`} {...props}>{renderInline(el, style, line, `${key}.${i}`)}</Text>)
106}
107
108const PRISM_COLORS: Record<string, keyof Theme> = {
109 comment: 'codeComment', prolog: 'codeComment', doctype: 'codeComment', cdata: 'codeComment',
110 string: 'codeString', char: 'codeString', 'template-string': 'codeString', 'attr-value': 'codeString', url: 'codeString',
111 number: 'number', boolean: 'number', constant: 'number', symbol: 'number', inserted: 'number',
112 keyword: 'codeFlag', important: 'codeFlag', atrule: 'codeFlag', rule: 'codeFlag', deleted: 'codeFlag',
113 function: 'codeCommand', 'class-name': 'codeCommand', builtin: 'codeCommand', key: 'codeCommand', selector: 'codeCommand',
114 property: 'link', tag: 'link', 'attr-name': 'emphasis', variable: 'emphasis', regex: 'path',
115}
116
117type Segment = { text: string; color?: string; italic: boolean }
118
119const flatten = (tokens: PrismToken[], style: Style, color?: string, italic = false): Segment[] =>
120 tokens.flatMap(token => {
121 if (typeof token === 'string') return [{ text: token, color, italic }]
122 const names = [token.type, ...(Array.isArray(token.alias) ? token.alias : token.alias ? [token.alias] : [])]
123 const slot = names.map(n => PRISM_COLORS[n]).find(Boolean)
124 const inner = Array.isArray(token.content) ? token.content : [token.type === 'comment' && typeof token.content === 'string' && style.reorder ? commentVisual(token.content, style.shape) : token.content]
125 return flatten(inner, style, slot ? style.theme[slot] : color, italic || token.type === 'comment')
126 })
127
128export const remember = <T,>(cache: Map<string, T>, key: string, make: () => T, limit = 200): T => {
129 const hit = cache.get(key)
130 if (hit !== undefined) return hit
131 const value = make()
132 cache.set(key, value)
133 if (cache.size > limit) cache.delete(cache.keys().next().value!)
134 return value
135}
136
137const highlighted = new WeakMap<Style, Map<string, Segment[][]>>()
138
139const grammarFor = (lang: string) => {
140 const name = lang.toLowerCase()
141 const grammar = Object.hasOwn(languages, name) ? languages[name] : undefined
142 return grammar !== null && typeof grammar === 'object' ? grammar : undefined
143}
144
145export const highlightBlock = ({ Text }: ElementTable, style: Style, lines: string[], lang: string, key: string): RenderElement[] | null => {
146 const grammar = grammarFor(lang)
147 if (!grammar) return null
148 const code = lines.join('\n')
149 const cache = highlighted.get(style) ?? new Map<string, Segment[][]>()
150 highlighted.set(style, cache)
151 const rows = remember(cache, `${lang}\0${code}`, () => {
152 const out: Segment[][] = [[]]
153 for (const seg of flatten(tokenize(code, grammar), style)) {
154 seg.text.split('\n').forEach((piece, i) => {
155 if (i > 0) out.push([])
156 if (piece) out[out.length - 1]!.push({ ...seg, text: piece })
157 })
158 }
159 return out
160 })
161 return rows.map((row, r) => (
162 <Text key={`${key}.${r}`} color={style.theme.codeText}>
163 {row.length ? row.map((s, i) => <Text key={`${key}.${r}.${i}`} color={s.color} italic={s.italic}>{s.text}</Text>) : ' '}
164 </Text>
165 ))
166}
167
168const isShellLang = (lang: string) => lang === '' || /^(sh|bash|zsh|shell|console|fish|powershell|ps1)$/i.test(lang)
169
170export const codeLine = (el: ElementTable, style: Style, line: string, lang: string, key: string): RenderElement => {
171 const { Text } = el
172 const t = style.theme
173 const isShell = isShellLang(lang)
174 if (/[\u2500-\u257F]/.test(line)) return <Text key={key} color={t.codeText}>{line}</Text>
175 const comment = style.reorder ? commentTail(line, style.shape) : null
176 if (comment) {
177 return (
178 <Text key={key} color={t.codeText}>
179 {comment.head ? codeLine(el, style, comment.head, lang, `${key}.h`) : null}
180 <Text color={t.codeComment}>{comment.marker + comment.tail}</Text>
181 </Text>
182 )
183 }
184 if (!isShell) return <Text key={key} color={t.codeText}>{line || ' '}</Text>
185 if (/^\s*#/.test(line)) return <Text key={key} color={t.codeComment}>{line}</Text>
186 const parts = line.split(/("[^"]*"|'[^']*'|\s+)/).filter(p => p !== '')
187 let seenCommand = false
188 return (
189 <Text key={key} color={t.codeText}>
190 {parts.map((p, i) => {
191 if (/^\s+$/.test(p)) return <Text key={`${key}.${i}`}>{p}</Text>
192 if (/^["']/.test(p)) return <Text key={`${key}.${i}`} color={t.codeString}>{p}</Text>
193 if (/^--?[\w-]/.test(p)) return <Text key={`${key}.${i}`} color={t.codeFlag}>{p}</Text>
194 if (!seenCommand && !/^[$>|&;]+$/.test(p)) {
195 seenCommand = true
196 return <Text key={`${key}.${i}`} color={t.codeCommand}>{p}</Text>
197 }
198 if (/^(\||&&|;|\|\|)$/.test(p)) seenCommand = false
199 return <Text key={`${key}.${i}`}>{p}</Text>
200 })}
201 </Text>
202 )
203}
204
205const columnWidths = (natural: number[], available: number, gap: number): number[] => {
206 const room = Math.max(natural.length, available - gap * (natural.length - 1))
207 const total = natural.reduce((a, b) => a + b, 0)
208 if (total <= room) return natural
209 const widths = natural.map(() => 0)
210 let open = natural.map((_, c) => c)
211 let left = room
212 for (let fits = [-1]; fits.length; ) {
213 const share = Math.floor(left / open.length)
214 fits = open.filter(c => natural[c]! <= share)
215 fits.forEach(c => (widths[c] = natural[c]!, left -= natural[c]!))
216 open = open.filter(c => !fits.includes(c))
217 }
218 open.forEach((c, i) => (widths[c] = Math.max(1, Math.floor(left / open.length) + (i < left % open.length ? 1 : 0))))
219 while (widths.reduce((a, b) => a + b, 0) > room) {
220 const widest = widths.indexOf(Math.max(...widths))
221 if (widths[widest]! <= 1) break
222 widths[widest]!--
223 }
224 return widths
225}
226
227const displayText = (inline: Inline[]): string =>
228 inline.map(n => (n.kind === 'link' && n.text !== n.href ? `${n.text} (${n.href})` : 'children' in n ? displayText(n.children) : n.text)).join('')
229
230const isRtlTable = (style: Style, block: Extract<Block, { kind: 'table' }>): boolean => {
231 const cells = [...block.header, ...block.rows.flat()].filter(cell => displayText(cell).trim() !== '')
232 return cells.filter(cell => flowOf(style, cell, Infinity)?.base === 'R').length * 2 > cells.length
233}
234
235const ART_WIDTH = 100
236
237export const tableArt = (block: Extract<Block, { kind: 'table' }>): string => {
238 const cells = [block.header, ...block.rows].map(r => block.header.map((_, c) => displayText(r[c] ?? [])))
239 const widths = columnWidths(block.header.map((_, c) => Math.max(...cells.map(r => width(r[c]!)))), ART_WIDTH - 4, 3)
240 const pad = (text: string, c: number, align: 'left' | 'right' | 'center') => {
241 const room = widths[c]! - width(text)
242 const left = align === 'right' ? room : align === 'center' ? Math.floor(room / 2) : 0
243 return ' '.repeat(left) + text + ' '.repeat(room - left)
244 }
245 const line = (l: string, m: string, r: string) => l + widths.map(w => '─'.repeat(w + 2)).join(m) + r
246 const row = (r: string[], header: boolean) => {
247 const lines = r.map((text, c) => wrapRanges(text, widths[c]!).map(([a, b]) => text.slice(a, b)))
248 return Array.from({ length: Math.max(...lines.map(l => l.length)) }, (_, i) =>
249 `│ ${lines.map((l, c) => pad(l[i] ?? '', c, header ? 'center' : block.align[c] ?? 'left')).join(' │ ')} │`)
250 }
251 const art = [line('┌', '┬', '┐'), ...row(cells[0]!, true), ...cells.slice(1).flatMap(r => [line('├', '┼', '┤'), ...row(r, false)]), line('└', '┴', '┘')]
252 return ['```', ...art, '```'].join('\n')
253}
254
255const renderTable = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'table' }>, columns: number, key: string) => {
256 const { Box, Text } = el
257 const t = style.theme
258 const rtl = isRtlTable(style, block)
259 const box = style.tableStyle === 'box'
260 const gap = box ? 0 : style.tableStyle === 'grid' ? 3 : 2
261 const natural = block.header.map((h, c) =>
262 Math.max(width(displayText(h)), ...block.rows.map(r => width(displayText(r[c] ?? [])))),
263 )
264 const widths = box ? columnWidths(natural, columns - 4, 3) : columnWidths(natural, columns, gap)
265 const order = natural.map((_, c) => c)
266 if (rtl) order.reverse()
267 const ruleChar = style.tableStyle === 'grid' ? '━' : '─'
268 const justify = (c: number) =>
269 block.align[c] === 'right' ? 'flex-end' : block.align[c] === 'center' ? 'center' : 'flex-start'
270
271 const rule = (k: string, heavy: boolean) => (
272 <Box key={k} flexDirection="row" columnGap={gap}>
273 {order.map(c => (
274 <Text key={`${k}.${c}`} color={t.tableRule} dimColor={!heavy && !t.tableRule}>
275 {(heavy ? ruleChar : '─').repeat(widths[c]!)}
276 </Text>
277 ))}
278 </Box>
279 )
280
281 const bar = (k: string, text: string, lines = 1) => (
282 <Text key={k} color={t.tableRule} dimColor={!t.tableRule}>{Array.from({ length: lines }, () => text).join('\n')}</Text>
283 )
284 const clipped = (lines: number) => (k: string, text: string) => (
285 <Box key={k} minWidth={width(text)}>
286 <Box position="absolute" top={0} bottom={0} left={0} minWidth={width(text)} overflow="hidden">
287 {bar(`${k}.b`, text, lines)}
288 </Box>
289 </Box>
290 )
291 const edge = (k: string, [left, fill, mid, right]: string) =>
292 bar(k, left + order.map(c => fill!.repeat(widths[c]! + 2)).join(mid) + right)
293
294 const row = (cells: Inline[][], k: string, isHeader: boolean, border: (k: string, text: string) => RenderElement = bar) => (
295 <Box key={k} flexDirection="row" columnGap={gap} alignItems="stretch">
296 {box && border(`${k}.l`, '│ ')}
297 {order.map((c, i) => {
298 const w = widths[c]!
299 const cell = flowOf(style, cells[c] ?? [], Infinity)
300 const content = cell ? cell.lines[0]! : (cells[c] ?? [])
301 const side = cell?.base === 'R' && block.align[c] !== 'center' ? 'flex-end' : justify(c)
302 const cellBox = (
303 <Box key={`${k}.${c}`} width={w} flexShrink={0} justifyContent={side}>
304 {isHeader
305 ? <Text bold color={t.tableHeader}>{inlineText(content)}</Text>
306 : <Text>{renderInline(el, style, content, `${k}.${c}`)}</Text>}
307 </Box>
308 )
309 return box && i > 0 ? [border(`${k}.${c}s`, ' │ '), cellBox] : cellBox
310 })}
311 {box && border(`${k}.r`, ' │')}
312 </Box>
313 )
314
315 const wrappedRow = (cells: Inline[][], k: string, isHeader: boolean) => {
316 if (!isHeader && cells.some(hasLink)) return [row(cells, k, isHeader, clipped(Math.max(1, ...widths.map((w, c) => mostLines(displayText(cells[c] ?? []), w)))))]
317 const wrapped = widths.map((w, c) => {
318 const content = cells[c] ?? []
319 if (natural[c]! <= w) return [content]
320 return wrapInline(isHeader ? [{ kind: 'text', text: inlineText(content) }] : content, w)
321 })
322 return Array.from({ length: Math.max(...wrapped.map(l => l.length)) }, (_, i) =>
323 row(wrapped.map(l => l[i] ?? []), `${k}.${i}`, isHeader))
324 }
325
326 if (box) {
327 const lines: RenderElement[] = [edge(`${key}.t`, '┌─┬┐'), ...wrappedRow(block.header, `${key}.h`, true), edge(`${key}.hr`, '╞═╪╡')]
328 block.rows.forEach((r, i) => {
329 if (i > 0) lines.push(edge(`${key}.r${i}r`, '├─┼┤'))
330 lines.push(...wrappedRow(r, `${key}.r${i}`, false))
331 })
332 lines.push(edge(`${key}.b`, '└─┴┘'))
333 return <Box key={key} flexDirection="column" {...(rtl ? { alignSelf: 'flex-end' as const } : {})}>{lines}</Box>
334 }
335
336 const body: RenderElement[] = [row(block.header, `${key}.h`, true), rule(`${key}.hr`, true)]
337 block.rows.forEach((r, i) => {
338 body.push(row(r, `${key}.r${i}`, false))
339 if (style.tableStyle !== 'minimal' && i < block.rows.length - 1) body.push(rule(`${key}.r${i}r`, false))
340 })
341 return <Box key={key} flexDirection="column" {...(rtl ? { alignSelf: 'flex-end' as const } : {})}>{body}</Box>
342}
343
344const renderHeading = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'heading' }>, key: string) => {
345 const rtl = flowOf(style, block.inline, Infinity)
346 if (!rtl) return drawHeading(el, style, block, block.inline, key)
347 const heading = drawHeading(el, style, block, rtl.lines[0]!, key)
348 return rtl.base === 'R' ? <el.Box key={key} alignSelf="flex-end">{heading}</el.Box> : heading
349}
350
351const drawHeading = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'heading' }>, inline: Inline[], key: string) => {
352 const { Box, Text } = el
353 const t = style.theme
354 const color = block.level <= 2 ? t.heading : t.accent ?? t.heading
355 const label = inlineText(inline)
356 switch (style.headingStyle) {
357 case 'uppercase':
358 return <Text key={key} bold color={color}>{block.level === 1 ? label.toUpperCase() : label}</Text>
359 case 'underline':
360 return <Text key={key} bold underline={block.level <= 2} color={color}>{label}</Text>
361 case 'banner':
362 if (block.level === 1) return <Box key={key} alignSelf="flex-start" borderStyle="bold" borderColor={color} paddingX={1}><Text bold color={color}>{renderInline(el, style, inline, key)}</Text></Box>
363 return block.level === 2
364 ? <Box key={key} flexDirection="column" alignSelf="flex-start"><Text bold color={color}>{renderInline(el, style, inline, key)}</Text><Text color={color}>{'━'.repeat(width(label))}</Text></Box>
365 : <Text key={key} bold color={block.level === 3 ? color : t.strong}>{renderInline(el, style, inline, key)}</Text>
366 default:
367 return <Text key={key} bold color={color}>{renderInline(el, style, inline, key)}</Text>
368 }
369}
370
371const ALERT_COLOR = { note: 'blue', tip: 'green', important: 'magenta', warning: 'yellow', caution: 'red' } as const
372
373const renderParagraph = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'paragraph' }>, columns: number, key: string) => {
374 const { Box, Text } = el
375 const rtl = flowOf(style, block.inline, columns)
376 if (rtl?.base === 'R') return <Box key={key} flexDirection="column" alignItems="flex-end">{renderFlow(el, style, rtl.lines, key)}</Box>
377 return <Text key={key} bold={style.narration}>{renderInline(el, style, rtl ? rtl.lines[0]! : block.inline, key)}</Text>
378}
379
380const renderQuote = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'quote' }>, columns: number, key: string) => {
381 const { Box, Text } = el
382 const t = style.theme
383 const rtl = flowOf(style, block.inline, columns - 2)
384 if (rtl?.base === 'R') {
385 return (
386 <Box key={key} flexDirection="row" justifyContent="flex-end">
387 <Box flexDirection="column" alignItems="flex-end">{renderFlow(el, style, rtl.lines, key, { italic: true, color: t.quote })}</Box>
388 <Text color={t.accent}> │</Text>
389 </Box>
390 )
391 }
392 return (
393 <Box key={key} flexDirection="row">
394 <Text color={t.accent}>│ </Text>
395 <Text italic color={t.quote}>{renderInline(el, style, rtl ? rtl.lines[0]! : block.inline, key)}</Text>
396 </Box>
397 )
398}
399
400const renderAlert = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'alert' }>, columns: number, key: string) => {
401 const { Box, Text } = el
402 const color = ALERT_COLOR[block.level]
403 const title = <Text bold color={color}>{block.level[0]!.toUpperCase() + block.level.slice(1)}</Text>
404 const rtl = flowOf(style, block.inline, columns - 4)
405 if (rtl?.base === 'R') {
406 return (
407 <Box key={key} flexDirection="column" alignSelf="flex-end" alignItems="flex-end" borderStyle="round" borderColor={color} paddingX={1}>
408 {title}
409 {renderFlow(el, style, rtl.lines, key)}
410 </Box>
411 )
412 }
413 const inline = rtl ? rtl.lines[0]! : block.inline
414 return (
415 <Box key={key} flexDirection="column" alignSelf="flex-start" borderStyle="round" borderColor={color} paddingX={1}>
416 {title}
417 {inline.length ? <Text>{renderInline(el, style, inline, key)}</Text> : null}
418 </Box>
419 )
420}
421
422const TASK_GLYPHS = { checks: ['[ ]', '[✓]'], ticks: ['○', '✓'], box: ['□', '✓'], progress: ['○', '✓'] } as const
423
424const renderList = (el: ElementTable, style: Style, block: Extract<Block, { kind: 'list' }>, columns: number, key: string) => {
425 const { Box, Text } = el
426 const t = style.theme
427 const tasks = block.items.filter(item => item.task !== undefined)
428 const done = tasks.filter(item => item.task).length
429 const filled = tasks.length ? Math.round((done / tasks.length) * 20) : 0
430 const strike = style.taskStyle === 'checks' || style.taskStyle === 'box'
431 const tick = style.taskStyle === 'ticks' || style.taskStyle === 'progress'
432 return (
433 <Box key={key} flexDirection="column">
434 {style.taskStyle === 'progress' && tasks.length > 0 && (
435 <Text>
436 <Text color={t.accent}>{'━'.repeat(filled)}</Text>
437 <Text color={t.bullet} dimColor>{'─'.repeat(20 - filled)}</Text>
438 <Text color={t.bullet}>{` ${done}/${tasks.length} done`}</Text>
439 </Text>
440 )}
441 {block.items.map((item, i) => {
442 const k = `${key}.${i}`
443 const glyph = item.task !== undefined ? TASK_GLYPHS[style.taskStyle][item.task ? 1 : 0] : /\d/.test(item.marker) ? item.marker : item.depth ? '◦' : '•'
444 const glyphColor = item.task && tick ? t.accent : t.bullet
445 const rtl = flowOf(style, item.inline, columns - item.depth * 2 - glyph.length - 1)
446 if (rtl?.base === 'R') {
447 return (
448 <Box key={k} flexDirection="row" justifyContent="flex-end" paddingRight={item.depth * 2}>
449 <Box flexDirection="column" alignItems="flex-end">{renderFlow(el, style, rtl.lines, k)}</Box>
450 <Text color={glyphColor}>{` ${glyph}`}</Text>
451 </Box>
452 )
453 }
454 return (
455 <Box key={k} flexDirection="row" paddingLeft={item.depth * 2}>
456 <Text color={glyphColor}>{`${glyph} `}</Text>
457 <Text dimColor={item.task === true} strikethrough={item.task === true && strike}>{renderInline(el, style, rtl ? rtl.lines[0]! : item.inline, k)}</Text>
458 </Box>
459 )
460 })}
461 </Box>
462 )
463}
464
465export type CopyButton = (text: string | (() => string), key: string, label?: string) => RenderElement | null
466export type Drawn = Map<number, { element: RenderElement; art: string }>
467
468const copySource = (block: Block): string | undefined =>
469 block.kind === 'code' ? block.lines.join('\n') : block.kind === 'table' || block.kind === 'list' ? block.raw : block.kind === 'quote' || block.kind === 'alert' ? block.raw.split('\n').map(line => line.replace(/^\s*>\s?/, '')).join('\n') : undefined
470
471export const renderBlocks = (el: ElementTable, style: Style, blocks: Block[], columns: number, drawn: Drawn = new Map(), copy?: CopyButton, png?: CopyButton): RenderElement[] => {
472 const { Box, Text } = el
473 const t = style.theme
474 const rendered = blocks.map((block, b) => {
475 const key = `b${b}`
476 switch (block.kind) {
477 case 'heading':
478 return renderHeading(el, style, block, key)
479 case 'paragraph':
480 return renderParagraph(el, style, block, columns, key)
481 case 'quote':
482 return renderQuote(el, style, block, columns, key)
483 case 'alert':
484 return renderAlert(el, style, block, columns, key)
485 case 'rule':
486 return <Text key={key} color={t.rule} dimColor={!t.rule}>{'─'.repeat(Math.max(8, Math.min(columns, 80)))}</Text>
487 case 'code':
488 return drawn.get(b)?.element ?? (
489 <Box key={key} flexDirection="column" alignSelf="flex-start">
490 <Box flexDirection="row" justifyContent="space-between" columnGap={4}>
491 <Text color={t.codeComment}>{`── ${block.lang || 'code'}`}</Text>
492 {copy?.(block.lines.join('\n'), `copy${b}`) ?? null}
493 </Box>
494 <Box flexDirection="column" paddingLeft={2}>
495 {(isShellLang(block.lang) ? null : highlightBlock(el, style, block.lines, block.lang, key)) ?? block.lines.map((line, i) => codeLine(el, style, line, block.lang, `${key}.${i}`))}
496 </Box>
497 </Box>
498 )
499 case 'list':
500 return renderList(el, style, block, columns, key)
501 case 'table':
502 return renderTable(el, style, block, columns, key)
503 }
504 })
505 const copied = rendered.map((element, b) => {
506 const block = blocks[b]
507 const text = block ? copySource(block) : undefined
508 const isPlainCode = block?.kind === 'code' && !drawn.has(b)
509 const art = drawn.get(b)?.art ?? (block?.kind === 'table' ? () => tableArt(block) : undefined)
510 const first = text === undefined || isPlainCode ? null : copy?.(text, `copy${b}`, art === undefined || block?.kind === 'table' ? undefined : '⧉ source')
511 const second = first && art !== undefined ? copy?.(art, `art${b}`, '⧉ art') : null
512 const third = second && art !== undefined ? png?.(art, `png${b}`, '▣ png') : null
513 const button = second ? (
514 <el.Box key={`copies${b}`} flexDirection="row" columnGap={1}>
515 {first}
516 {second}
517 {third}
518 </el.Box>
519 ) : first
520 if (!button) return element
521 const { Box } = el
522 const rtl = style.reorder && block !== undefined && hasRtl(block.raw)
523 return block?.kind === 'quote' || block?.kind === 'alert' ? (
524 <Box key={`c${b}`} flexDirection="row" columnGap={2} {...(rtl ? { justifyContent: 'flex-end' as const } : {})}>
525 {element}
526 {button}
527 </Box>
528 ) : (
529 <Box key={`c${b}`} flexDirection="column" {...(rtl && (block?.kind === 'list' || (block?.kind === 'table' && isRtlTable(style, block))) ? {} : { alignSelf: 'flex-start' as const })}>
530 <Box justifyContent="flex-end">{button}</Box>
531 {element}
532 </Box>
533 )
534 })
535 const isFigure = (b: number) => blocks[b]?.kind === 'table' || drawn.has(b)
536 const out: RenderElement[] = []
537 for (let b = 0; b < rendered.length; b++) {
538 if (!isFigure(b) || !isFigure(b + 1)) {
539 out.push(copied[b]!)
540 continue
541 }
542 const start = b
543 while (isFigure(b + 1)) b++
544 out.push(
545 <Box key={`row${start}`} flexDirection="row" flexWrap="wrap" columnGap={4} rowGap={1}>
546 {copied.slice(start, b + 1).map((figure, i) => <Box key={`f${start + i}`} flexShrink={0}>{figure}</Box>)}
547 </Box>,
548 )
549 }
550 return out
551}
552
553export type ToolRow = { tool: string; input: unknown; isRunning: boolean; isErrored: boolean; isInterrupted: boolean }
554
555const VERBS: Record<string, string> = {
556 Bash: 'Ran', PowerShell: 'Ran', Read: 'Read', Write: 'Wrote', Edit: 'Edited', MultiEdit: 'Edited', NotebookEdit: 'Edited',
557 Grep: 'Searched', Glob: 'Listed', WebFetch: 'Fetched', WebSearch: 'Searched the web for', Agent: 'Delegated', Task: 'Delegated',
558}
559
560const field = (input: unknown, ...keys: string[]): string | undefined => {
561 if (input === null || typeof input !== 'object') return undefined
562 for (const k of keys) {
563 const v = (input as Record<string, unknown>)[k]
564 if (typeof v === 'string' && v.trim() !== '') return v
565 }
566 return undefined
567}
568
569const toolDim = (style: Style) => style.toolStyle !== "classic"
570
571const toolGutter = ({ Box, Text }: ElementTable, style: Style, color: string | undefined, running: boolean) =>
572 style.toolStyle.startsWith("tree")
573 ? <Box width={4} flexShrink={0}><Text color={color}>{' ⎿ '}</Text></Box>
574 : <Box width={2} flexShrink={0}><Text color={color}>{running ? '◌' : '●'}</Text></Box>
575
576const toolLayout = (el: ElementTable, style: Style, columns: number, label: string, color: string | undefined, running: boolean, text: RenderElement) => {
577 const { Box, Text } = el
578 if (style.toolStyle !== "chat") return <Box flexDirection="row">{toolGutter(el, style, color, running)}{text}</Box>
579 const w = Math.min(width(label) + 2, Math.max(20, Math.floor(columns * 0.6)))
580 return (
581 <Box flexDirection="row" justifyContent="flex-end" width="100%">
582 <Box width={w} flexDirection="row">{text}<Text color={color}>{running ? " ◌" : " ●"}</Text></Box>
583 </Box>
584 )
585}
586
587export const renderToolRow = (el: ElementTable, style: Style, row: ToolRow, columns = 100): RenderElement => {
588 const { Box, Text } = el
589 const t = style.theme
590 const isShell = row.tool === 'Bash' || row.tool === 'PowerShell'
591 const verb = VERBS[row.tool] ?? row.tool.replace(/^mcp__([^_]+)__/, '$1 ')
592 const target = isShell
593 ? field(row.input, 'command')?.split('\n')[0]
594 : field(row.input, 'file_path', 'notebook_path', 'path', 'pattern', 'url', 'query', 'description')
595 const dot = row.isErrored ? t.codeFlag : row.isInterrupted ? t.codeComment : row.isRunning ? t.accent : t.number
596 const isPath = target !== undefined && /^(~|\.{0,2}\/|[A-Za-z]:\\)/.test(target)
597
598 const label = `${verb}${target === undefined ? "" : ` ${target}`}${row.isInterrupted ? " interrupted" : row.isErrored ? " failed" : ""}`
599 return toolLayout(el, style, columns, label, dot, row.isRunning, (
600 <Text wrap="truncate-end" dimColor={toolDim(style)}>
601 <Text bold={!toolDim(style)} dimColor={toolDim(style)}>{verb}</Text>
602 {target === undefined ? null : <Text> </Text>}
603 {target === undefined ? null : isShell ? codeLine(el, style, target, 'bash', 'cmd') : <Text color={isPath ? t.path : t.inlineCode} dimColor={toolDim(style)}>{target}</Text>}
604 {row.isInterrupted ? <Text dimColor> interrupted</Text> : row.isErrored ? <Text color={t.codeFlag}> failed</Text> : null}
605 </Text>
606 ))
607}
608
609const OUTPUT_LINES = 120
610
611const lines = (value: unknown): string[] => (typeof value === 'string' && value !== '' ? value.replace(/\n$/, '').split('\n') : [])
612
613export const renderExpandedShell = (el: ElementTable, style: Style, row: ToolRow & { output?: unknown }): RenderElement => {
614 const { Box, Text } = el
615 const t = style.theme
616 const command = (field(row.input, 'command') ?? '').split('\n')
617 const out = row.output !== null && typeof row.output === 'object' ? (row.output as Record<string, unknown>) : {}
618 const stdout = lines(out.stdout)
619 const stderr = lines(out.stderr)
620 const shown = [...stdout.map(text => ({ text, color: undefined as string | undefined })), ...stderr.map(text => ({ text, color: t.codeFlag as string | undefined }))]
621 const visible = shown.slice(0, OUTPUT_LINES)
622 const dot = row.isErrored ? t.codeFlag : row.isInterrupted ? t.codeComment : row.isRunning ? t.accent : t.number
623 return (
624 <Box flexDirection="column">
625 <Box flexDirection="row">
626 <Box width={2} flexShrink={0}>
627 <Text color={dot}>{row.isRunning ? '◌' : '●'}</Text>
628 </Box>
629 <Box flexDirection="column">
630 {command.map((line, i) => (
631 <Text key={`c${i}`}>
632 {i === 0 ? <Text bold>{`${row.tool}(`}</Text> : null}
633 {codeLine(el, style, line, 'bash', `cmd${i}`)}
634 {i === command.length - 1 ? <Text bold>)</Text> : null}
635 </Text>
636 ))}
637 </Box>
638 </Box>
639 {row.isRunning ? null : (
640 <Box paddingLeft={2}>
641 <Box flexDirection="column" alignSelf="flex-start" borderStyle="round" borderColor={t.codeComment} paddingX={1}>
642 {visible.length === 0 ? <Text dimColor>(No output)</Text> : visible.map((l, i) => <Text key={`o${i}`} color={l.color}>{l.text === '' ? ' ' : l.text}</Text>)}
643 {shown.length > visible.length ? <Text dimColor>{`\u2026 +${shown.length - visible.length} lines`}</Text> : null}
644 </Box>
645 </Box>
646 )}
647 </Box>
648 )
649}
650
651const GROUPS: [RegExp, string, string][] = [
652 [/^(Bash|PowerShell)$/, 'ran', 'command'],
653 [/^Read$/, 'read', 'file'],
654 [/^(Write|Edit|MultiEdit|NotebookEdit)$/, 'edited', 'file'],
655 [/^(Grep|Glob)$/, 'searched', 'pattern'],
656 [/^(WebFetch|WebSearch)$/, 'fetched', 'page'],
657 [/^(Agent|Task)$/, 'delegated', 'task'],
658]
659
660export const groupSummary = (calls: readonly { tool: string }[]): string => {
661 const counts = new Map<string, number>()
662 for (const call of calls) {
663 const [, verb, noun] = GROUPS.find(([re]) => re.test(call.tool)) ?? [, 'used', call.tool.replace(/^mcp__([^_]+)__/, '$1 ')]
664 const label = `${verb} ${noun}`
665 counts.set(label, (counts.get(label) ?? 0) + 1)
666 }
667 const parts = [...counts].map(([label, n]) => {
668 const [verb, ...noun] = label.split(' ')
669 const name = noun.join(' ')
670 return `${verb} ${n} ${n === 1 ? name : name.endsWith('h') ? `${name}es` : `${name}s`}`
671 })
672 const text = parts.join(', ')
673 return text.charAt(0).toUpperCase() + text.slice(1)
674}
675
676export const renderToolGroup = (el: ElementTable, style: Style, calls: readonly ToolRow[], isActive: boolean, columns = 100): RenderElement => {
677 const { Box, Text } = el
678 const t = style.theme
679 const failed = calls.filter(c => c.isErrored).length
680 const running = isActive && calls.some(c => c.isRunning)
681 const dot = failed ? t.codeFlag : running ? t.accent : t.number
682 const last = calls[calls.length - 1]
683 const lastTarget = last ? field(last.input, 'command', 'file_path', 'notebook_path', 'path', 'pattern', 'url', 'query', 'description')?.split('\n')[0] : undefined
684 const label = `${groupSummary(calls)}${failed ? ` · ${failed} failed` : ""}${lastTarget ? ` · last: ${lastTarget}` : ""}`
685 return toolLayout(el, style, columns, label, dot, running, (
686 <Text wrap="truncate-end" dimColor={toolDim(style)}>
687 <Text bold={!toolDim(style)} dimColor={toolDim(style)}>{groupSummary(calls)}</Text>
688 {failed ? <Text color={t.codeFlag}>{` · ${failed} failed`}</Text> : null}
689 {lastTarget ? <Text dimColor>{` · last: ${lastTarget}`}</Text> : null}
690 </Text>
691 ))
692}
693
694export const formatDuration = (ms: number): string => {
695 const s = Math.round(ms / 1000)
696 if (s < 60) return `${Math.max(s, 0)}s`
697 if (s < 3600) return `${Math.floor(s / 60)}m ${s % 60}s`
698 return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`
699}
700
701export const renderTurnDuration = ({ Text }: ElementTable, style: Style, word: string, durationMs: number): RenderElement => (
702 <Text color={style.theme.codeComment}>
703 {`✻ ${word} for `}
704 <Text color={style.theme.number}>{formatDuration(durationMs)}</Text>
705 </Text>
706)
707
708export const renderUserPrompt = (el: ElementTable, style: Style, text: string, columns: number): RenderElement => {
709 const { Box, Text } = el
710 const t = style.theme
711 const color = style.promptStyle === 'chevron' ? t.accent : t.heading
712 const lines = text.split('\n').map(line => flowOf(style, [{ kind: 'text', text: line }], columns - 4))
713 const rtl = lines.some(l => l?.base === 'R')
714 const body = (
715 <Box flexDirection="column" {...(rtl ? { alignItems: 'flex-end' as const } : {})}>
716 {text.split('\n').map((line, i) => {
717 const flow = lines[i]
718 return flow ? renderFlow(el, style, flow.lines, `p${i}`, { color, bold: style.promptStyle === 'chevron' }) : <Text key={`p${i}`} color={color} bold={style.promptStyle === 'chevron'}>{line}</Text>
719 })}
720 </Box>
721 )
722 if (style.promptStyle === 'bubble') {
723 return <Box borderStyle="round" borderColor={t.accent} paddingX={1} alignSelf={rtl ? 'flex-end' : 'flex-start'}>{body}</Box>
724 }
725 const mark = <Text color={t.accent} bold>{style.promptStyle === 'bar' ? (rtl ? ' ▐' : '▌ ') : rtl ? ' ‹' : '› '}</Text>
726 return <Box flexDirection="row" {...(rtl ? { justifyContent: 'flex-end' as const } : {})}>{rtl ? body : mark}{rtl ? mark : body}</Box>
727}
728hooks/help.ts 175 lines1export const showcaseText = (themes: readonly string[]): string => `
2# prismantis
3
4Colored markdown for Claude Code replies: **bold**, *italic*, ~~struck~~, \`inline code\`, a [link](https://github.com/NahumLitvin/prismantis) and a bare URL https://github.com/NahumLitvin/prismantis/issues, both clickable, numbers like 99.9% and 250ms, paths like ~/src/app.ts.
5
6## Commands
7
8| Command | Does |
9|---------|------|
10| \`/prismantis\` | This screen |
11| \`/prismantis theme <name>\` | Switch theme on the spot |
12| \`/prismantis copy\` | Copy the last reply, or \`copy code\` for its last code block |
13| \`/config\` | Edit any option |
14
15> [!NOTE]
16> ${themes.length} themes ship with it. \`mono\` uses no color, only bold and dim.
17
18### Pick a theme
19
20\`\`\`bash
21/prismantis theme nord
22/prismantis theme github-light
23\`\`\`
24
251. Dark: ${themes.filter(t => !/latte|light|dawn/.test(t) && t !== 'mono').join(', ')}
262. Light: ${themes.filter(t => /latte|light|dawn/.test(t)).join(', ')}
273. Plain
28 - mono
29
30> [!TIP]
31> Any color slot beats the theme. Set \`headingColor\` or \`numberColor\` to a hex value in \`/config\`.
32
33## Everything it draws
34
35### Alerts
36
37> [!IMPORTANT]
38> After updating the plugin, open sessions need \`/reload\`.
39
40> [!WARNING]
41> Terminals that copy on select (Warp) can turn a click on a copy button into a selection. Use the keyboard shortcut.
42
43> [!CAUTION]
44> Claude Code refuses trees over 20000 nodes. A code block past about 500 highlighted lines falls back to plain text.
45
46### Task lists
47
48- [x] Parse the reply
49- [x] Draw it
50 - [x] Tables and code
51 - [ ] Diagrams on the desktop app
52- [ ] Ship the next release
53
54### Your prompts
55
56Scroll up: what you typed draws in a bubble. \`promptStyle\` switches to \`bar\`, \`chevron\` or \`off\` in \`/config\`.
57
58### Tool rows
59
60Tool calls draw as one line, dimmed on the right by default. Set \`toolStyle\` in \`/config\` to \`chat\`, \`tree-dim\`, \`tree-bold\` or \`classic\`.
61
62### Quotes and rules
63
64> A quote keeps its text when you copy it, without the \`> \` markers.
65
66---
67
68### Code
69
70\`\`\`json
71{ "theme": "dracula", "headingStyle": "banner", "mermaid": true }
72\`\`\`
73
74#### Diagrams
75
76\`\`\`mermaid
77flowchart LR
78 R[Reply] --> P[Parse]
79 P --> D[Draw]
80 D --> S[Screen]
81\`\`\`
82
83\`\`\`mermaid
84sequenceDiagram
85 participant U as You
86 participant C as Claude
87 participant P as prismantis
88 U->>C: prompt
89 C->>P: markdown
90 P-->>U: colored reply
91\`\`\`
92
93\`\`\`mermaid
94xychart-beta
95 title "Color options per group"
96 x-axis [text, head, num, code, diag]
97 y-axis "options" 0 --> 8
98 bar [7, 3, 2, 6, 2]
99\`\`\`
100`
101
102export const helpText = (themes: readonly string[]): string => `
103## Commands
104
105| Command | Does |
106|---------|------|
107| \`/prismantis theme <name>\` | Switch theme on the spot |
108| \`/prismantis copy\` | Copy the last reply, or \`copy code\` for its last code block |
109| \`/prismantis demo\` | Full showcase, every element and diagram |
110| \`/prismantis demo-rtl\` | Hebrew right-to-left showcase |
111
112### ${themes.length} themes
113
114- Dark: ${themes.filter(t => !/latte|light|dawn/.test(t) && t !== 'mono').join(', ')}
115- Light: ${themes.filter(t => /latte|light|dawn/.test(t)).join(', ')}
116- Plain: mono, no color, only bold and dim
117
118Docs and issues: https://github.com/NahumLitvin/prismantis
119
120> [!TIP]
121> Any color slot beats the theme. Set \`headingColor\` or \`numberColor\` to a hex value in \`/config\`.
122
123### Task lists
124
125- [x] Tables, diagrams and charts drawn in the terminal
126- [x] A copy button on every block
127- [ ] Pick a \`taskStyle\` in \`/config\`: checks, ticks, box or progress
128
129\`\`\`mermaid
130flowchart LR
131 R[Reply] --> P[Parse]
132 P --> D[Draw]
133 D --> S[Screen]
134\`\`\`
135
136\`\`\`mermaid
137xychart-beta
138 title "Color options per group"
139 x-axis [text, head, num, code, diag]
140 y-axis "options" 0 --> 8
141 bar [7, 3, 2, 6, 2]
142\`\`\`
143
144> [!CAUTION]
145> Claude Code refuses trees over 20000 nodes. A code block past about 500 highlighted lines falls back to plain text.
146`
147
148export const rtlShowcaseText = (): string => `
149# עברית מימין לשמאל
150
151**שלום חברים**, זו הדגמה של עברית עם מונחים באנגלית כמו \`kubectl\`, מספרים כמו 99.9% ו-250ms, נתיב כמו ~/src/app.ts וגם [קישור](https://github.com/NahumLitvin/prismantis).
152
153## רשימות
154
155- פרוסים בשני אזורים (us-east ו-eu-west)
156- מחליפים ערכת נושא עם \`/prismantis theme nord\`
157
1581. מתקינים את התוסף
1592. שואלים שאלה בעברית
160
161> עברית נקראת מימין לשמאל, גם בטרמינל בלי תמיכה בכיווניות
162
163> [!TIP]
164> כל ערכת נושא עובדת גם בעברית
165
166| שירות | אזור | גרסה |
167| :--- | :--- | ---: |
168| שער | us-east | 2.14.0 |
169| חיוב | eu-west | 1.8.3 |
170
171\`\`\`bash
172ls -la # רשימת הקבצים בתיקייה
173\`\`\`
174`
175hooks/presets.ts 99 lines1import type { Theme } from './theme'
2
3export const PRESETS = {
4 'catppuccin-mocha': {
5 accent: '#cba6f7', heading: '#f9e2af', tableHeader: '#f9e2af', strong: '#ffffff', emphasis: '#f5c2e7',
6 inlineCode: '#89dceb', codeText: '#cdd6f4', codeCommand: '#89b4fa', codeFlag: '#f38ba8', codeString: '#a6e3a1',
7 codeComment: '#6c7086', link: '#89b4fa', path: '#89dceb', number: '#a6e3a1', quote: '#9399b2',
8 rule: '#45475a', tableRule: '#585b70', bullet: '#cba6f7', diagram: '#89b4fa', diagramText: '#cdd6f4',
9 },
10 'catppuccin-latte': {
11 accent: '#8839ef', heading: '#df8e1d', tableHeader: '#df8e1d', strong: '#11111b', emphasis: '#ea76cb',
12 inlineCode: '#04a5e5', codeText: '#4c4f69', codeCommand: '#1e66f5', codeFlag: '#d20f39', codeString: '#40a02b',
13 codeComment: '#9ca0b0', link: '#1e66f5', path: '#209fb5', number: '#40a02b', quote: '#6c6f85',
14 rule: '#bcc0cc', tableRule: '#acb0be', bullet: '#8839ef', diagram: '#1e66f5', diagramText: '#4c4f69',
15 },
16 dracula: {
17 accent: '#bd93f9', heading: '#bd93f9', tableHeader: '#f1fa8c', strong: '#f8f8f2', emphasis: '#ff79c6',
18 inlineCode: '#8be9fd', codeText: '#f8f8f2', codeCommand: '#50fa7b', codeFlag: '#ff79c6', codeString: '#f1fa8c',
19 codeComment: '#6272a4', link: '#8be9fd', path: '#ffb86c', number: '#bd93f9', quote: '#6272a4',
20 rule: '#44475a', tableRule: '#6272a4', bullet: '#ff79c6', diagram: '#bd93f9', diagramText: '#f8f8f2',
21 },
22 nord: {
23 accent: '#88c0d0', heading: '#88c0d0', tableHeader: '#ebcb8b', strong: '#eceff4', emphasis: '#b48ead',
24 inlineCode: '#8fbcbb', codeText: '#d8dee9', codeCommand: '#88c0d0', codeFlag: '#81a1c1', codeString: '#a3be8c',
25 codeComment: '#4c566a', link: '#88c0d0', path: '#8fbcbb', number: '#b48ead', quote: '#81a1c1',
26 rule: '#3b4252', tableRule: '#4c566a', bullet: '#88c0d0', diagram: '#81a1c1', diagramText: '#d8dee9',
27 },
28 'tokyo-night': {
29 accent: '#bb9af7', heading: '#7dcfff', tableHeader: '#e0af68', strong: '#c0caf5', emphasis: '#f7768e',
30 inlineCode: '#7dcfff', codeText: '#a9b1d6', codeCommand: '#7aa2f7', codeFlag: '#bb9af7', codeString: '#9ece6a',
31 codeComment: '#565f89', link: '#73daca', path: '#2ac3de', number: '#ff9e64', quote: '#9aa5ce',
32 rule: '#414868', tableRule: '#565f89', bullet: '#bb9af7', diagram: '#7aa2f7', diagramText: '#c0caf5',
33 },
34 'gruvbox-dark': {
35 accent: '#d3869b', heading: '#fabd2f', tableHeader: '#fabd2f', strong: '#fbf1c7', emphasis: '#d3869b',
36 inlineCode: '#8ec07c', codeText: '#ebdbb2', codeCommand: '#83a598', codeFlag: '#fe8019', codeString: '#b8bb26',
37 codeComment: '#928374', link: '#83a598', path: '#8ec07c', number: '#d3869b', quote: '#a89984',
38 rule: '#3c3836', tableRule: '#504945', bullet: '#fe8019', diagram: '#83a598', diagramText: '#ebdbb2',
39 },
40 'gruvbox-light': {
41 accent: '#8f3f71', heading: '#b57614', tableHeader: '#b57614', strong: '#282828', emphasis: '#8f3f71',
42 inlineCode: '#427b58', codeText: '#3c3836', codeCommand: '#076678', codeFlag: '#af3a03', codeString: '#79740e',
43 codeComment: '#928374', link: '#076678', path: '#427b58', number: '#8f3f71', quote: '#7c6f64',
44 rule: '#ebdbb2', tableRule: '#a89984', bullet: '#af3a03', diagram: '#076678', diagramText: '#3c3836',
45 },
46 'rose-pine': {
47 accent: '#c4a7e7', heading: '#c4a7e7', tableHeader: '#f6c177', strong: '#e0def4', emphasis: '#ebbcba',
48 inlineCode: '#9ccfd8', codeText: '#e0def4', codeCommand: '#9ccfd8', codeFlag: '#eb6f92', codeString: '#f6c177',
49 codeComment: '#6e6a86', link: '#c4a7e7', path: '#9ccfd8', number: '#ebbcba', quote: '#908caa',
50 rule: '#26233a', tableRule: '#6e6a86', bullet: '#eb6f92', diagram: '#c4a7e7', diagramText: '#e0def4',
51 },
52 'rose-pine-dawn': {
53 accent: '#907aa9', heading: '#907aa9', tableHeader: '#ea9d34', strong: '#464261', emphasis: '#d7827e',
54 inlineCode: '#56949f', codeText: '#464261', codeCommand: '#56949f', codeFlag: '#b4637a', codeString: '#ea9d34',
55 codeComment: '#9893a5', link: '#907aa9', path: '#286983', number: '#d7827e', quote: '#797593',
56 rule: '#f2e9e1', tableRule: '#9893a5', bullet: '#b4637a', diagram: '#907aa9', diagramText: '#464261',
57 },
58 everforest: {
59 accent: '#a7c080', heading: '#e69875', tableHeader: '#dbbc7f', strong: '#d3c6aa', emphasis: '#d699b6',
60 inlineCode: '#83c092', codeText: '#d3c6aa', codeCommand: '#7fbbb3', codeFlag: '#e67e80', codeString: '#a7c080',
61 codeComment: '#859289', link: '#7fbbb3', path: '#83c092', number: '#d699b6', quote: '#9da9a0',
62 rule: '#475258', tableRule: '#7a8478', bullet: '#e69875', diagram: '#7fbbb3', diagramText: '#d3c6aa',
63 },
64 'github-dark': {
65 accent: '#d2a8ff', heading: '#79c0ff', tableHeader: '#ffa657', strong: '#e6edf3', emphasis: '#d2a8ff',
66 inlineCode: '#79c0ff', codeText: '#e6edf3', codeCommand: '#d2a8ff', codeFlag: '#ff7b72', codeString: '#a5d6ff',
67 codeComment: '#7d8590', link: '#58a6ff', path: '#7ee787', number: '#79c0ff', quote: '#7d8590',
68 rule: '#30363d', tableRule: '#8b949e', bullet: '#ff7b72', diagram: '#58a6ff', diagramText: '#e6edf3',
69 },
70 'github-light': {
71 accent: '#8250df', heading: '#0550ae', tableHeader: '#953800', strong: '#1f2328', emphasis: '#8250df',
72 inlineCode: '#0550ae', codeText: '#1f2328', codeCommand: '#8250df', codeFlag: '#cf222e', codeString: '#0a3069',
73 codeComment: '#6e7781', link: '#0969da', path: '#116329', number: '#0550ae', quote: '#656d76',
74 rule: '#d0d7de', tableRule: '#6e7781', bullet: '#cf222e', diagram: '#0969da', diagramText: '#1f2328',
75 },
76 'one-dark': {
77 accent: '#c678dd', heading: '#e06c75', tableHeader: '#e5c07b', strong: '#d19a66', emphasis: '#c678dd',
78 inlineCode: '#56b6c2', codeText: '#abb2bf', codeCommand: '#61afef', codeFlag: '#e06c75', codeString: '#98c379',
79 codeComment: '#5c6370', link: '#61afef', path: '#56b6c2', number: '#d19a66', quote: '#828997',
80 rule: '#3a3f4b', tableRule: '#5c6370', bullet: '#c678dd', diagram: '#61afef', diagramText: '#abb2bf',
81 },
82 'solarized-dark': {
83 accent: '#6c71c4', heading: '#b58900', tableHeader: '#b58900', strong: '#eee8d5', emphasis: '#d33682',
84 inlineCode: '#2aa198', codeText: '#93a1a1', codeCommand: '#268bd2', codeFlag: '#cb4b16', codeString: '#859900',
85 codeComment: '#586e75', link: '#268bd2', path: '#2aa198', number: '#859900', quote: '#657b83',
86 rule: '#073642', tableRule: '#586e75', bullet: '#6c71c4', diagram: '#268bd2', diagramText: '#93a1a1',
87 },
88 'solarized-light': {
89 accent: '#6c71c4', heading: '#b58900', tableHeader: '#b58900', strong: '#073642', emphasis: '#d33682',
90 inlineCode: '#2aa198', codeText: '#586e75', codeCommand: '#268bd2', codeFlag: '#cb4b16', codeString: '#859900',
91 codeComment: '#93a1a1', link: '#268bd2', path: '#2aa198', number: '#859900', quote: '#839496',
92 rule: '#eee8d5', tableRule: '#93a1a1', bullet: '#6c71c4', diagram: '#268bd2', diagramText: '#586e75',
93 },
94 mono: {} as Theme,
95} satisfies Record<string, Theme>
96
97export type PresetName = keyof typeof PRESETS
98export const PRESET_NAMES = Object.keys(PRESETS) as PresetName[]
99hooks/theme.ts 66 lines1import type { PluginOptions } from 'claude-code'
2
3import { PRESETS } from './presets'
4import type { Shape, Terminal } from './rtl'
5import { TERMINALS } from './rtl'
6
7export const TOKENS = [
8 'accent', 'heading', 'strong', 'emphasis', 'inlineCode', 'codeText', 'codeCommand', 'codeFlag', 'codeString', 'codeComment',
9 'link', 'path', 'number', 'quote', 'rule', 'tableHeader', 'tableRule', 'bullet', 'diagram', 'diagramText',
10] as const
11
12export type Theme = Partial<Record<(typeof TOKENS)[number], string>>
13
14export type Style = {
15 theme: Theme
16 headingStyle: 'bold' | 'underline' | 'uppercase' | 'banner'
17 tableStyle: 'box' | 'rules' | 'grid' | 'minimal'
18 taskStyle: 'checks' | 'ticks' | 'box' | 'progress'
19 promptStyle: 'bubble' | 'bar' | 'chevron' | 'off'
20 toolStyle: 'chat' | 'tree-dim' | 'tree-bold' | 'classic'
21 narration?: boolean
22 highlightNumbers: boolean
23 highlightPaths: boolean
24 mermaid: boolean
25 mermaidAscii: boolean
26 copyButtons: boolean
27 diagramHints: boolean
28 rtl: 'auto' | Terminal | 'off'
29 reorder: boolean
30 shape: Shape
31}
32
33const COLOR = /^(#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)|ansi256\(\d{1,3}\)|(black|red|green|yellow|blue|magenta|cyan|white|gray|grey)(Bright)?)$/i
34
35export const isColor = (value: unknown): value is string => typeof value === 'string' && COLOR.test(value.trim())
36
37const pick = <T extends string>(value: unknown, allowed: readonly T[], fallback: T): T =>
38 allowed.includes(value as T) ? (value as T) : fallback
39
40export const resolveStyle = (options: PluginOptions): Style => {
41 const base: Theme = (PRESETS as Record<string, Theme>)[String(options.theme)] ?? PRESETS['catppuccin-mocha']
42 const fromFields = Object.fromEntries(
43 TOKENS.filter(k => isColor(options[`${k}Color`])).map(k => [k, String(options[`${k}Color`]).trim()]),
44 )
45
46 const rtl = pick(options.rtl, ['auto', 'off', ...(Object.keys(TERMINALS) as Terminal[])], 'auto')
47
48 return {
49 theme: { ...base, ...fromFields },
50 headingStyle: pick(options.headingStyle, ['bold', 'underline', 'uppercase', 'banner'] as const, 'banner'),
51 tableStyle: pick(options.tableStyle, ['box', 'rules', 'grid', 'minimal'] as const, 'box'),
52 taskStyle: pick(options.taskStyle, ['checks', 'ticks', 'box', 'progress'] as const, 'checks'),
53 promptStyle: pick(options.promptStyle, ['bubble', 'bar', 'chevron', 'off'] as const, 'bubble'),
54 toolStyle: pick(options.toolStyle, ['chat', 'tree-dim', 'tree-bold', 'classic'] as const, 'chat'),
55 highlightNumbers: options.highlightNumbers !== false,
56 highlightPaths: options.highlightPaths !== false,
57 mermaid: options.mermaid !== false,
58 mermaidAscii: options.mermaidAscii === true,
59 copyButtons: options.copyButtons !== false,
60 diagramHints: options.diagramHints !== false && options.mermaid !== false,
61 rtl,
62 reorder: rtl !== 'auto' && rtl !== 'off',
63 shape: rtl === 'auto' || rtl === 'off' ? 'visual' : TERMINALS[rtl],
64 }
65}
66hooks/rtl.ts 189 lines1import type { Inline } from './markdown'
2
3type Dir = 'R' | 'L'
4export type Flow = { base: Dir; lines: Inline[][] }
5export type Shape = 'visual' | 'words' | 'logical'
6
7export const TERMINALS = {
8 warp: 'visual',
9 ghostty: 'visual',
10 wezterm: 'visual',
11 vscode: 'visual',
12 alacritty: 'visual',
13 'windows-terminal': 'visual',
14 'apple-terminal': 'logical',
15 kitty: 'words',
16 iterm: 'logical',
17 gnome: 'logical',
18 konsole: 'logical',
19} as const satisfies Record<string, Shape>
20
21export type Terminal = keyof typeof TERMINALS
22
23type Fmt = { wrap: ('strong' | 'emphasis' | 'strike')[]; leaf: 'text' | 'code' | 'number' | 'path' | 'link' | 'dim' }
24type Unit = { ch: string; fmt: Fmt }
25
26const R = /[--ٟ٪-ۯۺ-ݿࢠ-ࣿיִ-﷿ﹰ-]/
27const L = /[A-Za-z0-9À-ɏ٠-٩۰-۹]/
28const LATIN = /[A-Za-zÀ-ɏ]/
29const MARK = /\p{M}/u
30const MIRROR: Record<string, string> = { '(': ')', ')': '(', '[': ']', ']': '[', '{': '}', '}': '{', '<': '>', '>': '<' }
31const DIGIT = /[0-9\u0660-\u0669\u06F0-\u06F9]/
32const TERMINATOR = /[%\u2030\u00B0$\u20AC\u00A3\u00A5\u20AA]/
33const COMMENT = /^(.*?)((?:^|\s)(?:#|\/\/)\s+)(.*)$/
34
35const classify = (ch: string): Dir | null => (R.test(ch) ? 'R' : L.test(ch) ? 'L' : null)
36
37const units = (text: string, fmt: Fmt): Unit[] => {
38 const out: Unit[] = []
39 for (const cp of text) {
40 const last = out.at(-1)
41 if (last && MARK.test(cp)) last.ch += cp
42 else out.push({ ch: cp, fmt })
43 }
44 return out
45}
46
47const flatten = (nodes: Inline[], wrap: Fmt['wrap'] = []): Unit[] =>
48 nodes.flatMap(n => {
49 switch (n.kind) {
50 case 'strong':
51 case 'emphasis':
52 case 'strike':
53 return flatten(n.children, [...wrap, n.kind])
54 case 'link':
55 return n.text === n.href
56 ? units(n.text, { wrap, leaf: 'link' })
57 : [...units(n.text, { wrap, leaf: 'link' }), ...units(` (${n.href})`, { wrap, leaf: 'dim' })]
58 default:
59 return units(n.text, { wrap, leaf: n.kind })
60 }
61 })
62
63const sameFmt = (a: Fmt, b: Fmt) => a.leaf === b.leaf && a.wrap.length === b.wrap.length && a.wrap.every((k, i) => k === b.wrap[i])
64
65const leafNode = (leaf: Fmt['leaf'], text: string): Inline => (leaf === 'link' ? { kind: 'link', text, href: text } : { kind: leaf, text })
66
67const rebuild = (all: Unit[]): Inline[] => {
68 const out: Inline[] = []
69 let i = 0
70 while (i < all.length) {
71 const fmt = all[i]!.fmt
72 let text = ''
73 let j = i
74 while (j < all.length && sameFmt(all[j]!.fmt, fmt)) text += all[j++]!.ch
75 out.push(fmt.wrap.reduceRight<Inline>((inner, kind) => ({ kind, children: [inner] }), leafNode(fmt.leaf, text)))
76 i = j
77 }
78 return out
79}
80
81const baseOf = (all: Unit[]): Dir => {
82 const words = all.map(u => u.ch).join('').split(/\s+/)
83 const rw = words.filter(w => R.test(w)).length
84 const lw = words.filter(w => LATIN.test(w) && !R.test(w)).length
85 if (rw !== lw) return rw > lw ? 'R' : 'L'
86 return all.map(u => classify(u.ch)).find(Boolean) ?? 'R'
87}
88
89const reorder = (all: Unit[], base: Dir): Unit[] => {
90 const strong = all.map((u, i) => {
91 const own = classify(u.ch)
92 if (own) return own
93 if (u.fmt.leaf === 'code' || u.fmt.leaf === 'path') return 'L'
94 return TERMINATOR.test(u.ch) && (DIGIT.test(all[i - 1]?.ch ?? '') || DIGIT.test(all[i + 1]?.ch ?? '')) ? 'L' : null
95 })
96 const prev: (Dir | null)[] = []
97 let seen: Dir | null = null
98 strong.forEach((s, i) => {
99 seen = s ?? seen
100 prev[i] = seen
101 })
102 const next: (Dir | null)[] = []
103 seen = null
104 for (let i = strong.length - 1; i >= 0; i--) {
105 seen = strong[i] ?? seen
106 next[i] = seen
107 }
108 const dir = strong.map((s, i): Dir => s ?? ((prev[i] ?? base) === (next[i] ?? base) ? (prev[i] ?? base) : base))
109 const runs: { d: Dir; u: Unit[] }[] = []
110 all.forEach((u, i) => {
111 const last = runs.at(-1)
112 if (last && last.d === dir[i]) last.u.push(u)
113 else runs.push({ d: dir[i]!, u: [u] })
114 })
115 const out = runs.map(r => (r.d === 'R' ? r.u.map(u => ({ ...u, ch: MIRROR[u.ch] ?? u.ch })).reverse() : r.u))
116 return (base === 'R' ? out.reverse() : out).flat()
117}
118
119const wrapUnits = (all: Unit[], max: number, measure: (s: string) => number): Unit[][] => {
120 const tokens: Unit[][] = []
121 all.forEach((u, i) => {
122 if (i === 0 || (u.ch !== ' ' && all[i - 1]!.ch === ' ')) tokens.push([])
123 tokens.at(-1)!.push(u)
124 })
125 const lines: Unit[][] = [[]]
126 let used = 0
127 const trim = (line: Unit[]) => {
128 while (line.at(-1)?.ch === ' ') line.pop()
129 }
130 for (const token of tokens) {
131 let core = token.length
132 while (core > 0 && token[core - 1]!.ch === ' ') core--
133 const w = token.slice(0, core).reduce((sum, u) => sum + measure(u.ch), 0)
134 if (lines.at(-1)!.length && used + w > max) {
135 trim(lines.at(-1)!)
136 lines.push([])
137 used = 0
138 }
139 lines.at(-1)!.push(...token)
140 used += w + (token.length - core)
141 }
142 trim(lines.at(-1)!)
143 return lines
144}
145
146const clusters = (text: string): string[] => units(text, { wrap: [], leaf: 'text' }).map(u => u.ch)
147
148const RUN = new RegExp(`(?:${R.source}|\\p{M})+`, 'gu')
149
150const shapeText = (text: string, shape: Shape): string =>
151 shape === 'words' && R.test(text) ? text.replace(RUN, run => clusters(run).reverse().join('')) : text
152
153const shapeNodes = (nodes: Inline[], shape: Shape): Inline[] =>
154 nodes.map(n => {
155 if ('children' in n) return { ...n, children: shapeNodes(n.children, shape) }
156 if (n.kind === 'link') return { ...n, text: shapeText(n.text, shape), href: shapeText(n.href, shape) }
157 return { ...n, text: shapeText(n.text, shape) }
158 })
159
160const containsRtl = (nodes: Inline[]): boolean => nodes.some(n => ('children' in n ? containsRtl(n.children) : R.test(n.text)))
161
162export const flow = (nodes: Inline[], columns: number, measure: (s: string) => number, shape: Shape = 'visual'): Flow | null => {
163 if (!containsRtl(nodes)) return null
164 const all = flatten(nodes)
165 const base = baseOf(all)
166 const logical = base === 'R' ? wrapUnits(all, columns, measure) : [all]
167 const lines = logical.map(line => rebuild(shape === 'logical' ? line : reorder(line, base)))
168 return { base, lines: shape === 'words' ? lines.map(line => shapeNodes(line, shape)) : lines }
169}
170
171const visualText = (text: string): string => {
172 if (!R.test(text)) return text
173 const all = units(text, { wrap: [], leaf: 'text' })
174 return reorder(all, baseOf(all)).map(u => u.ch).join('')
175}
176
177export const commentTail = (line: string, shape: Shape = 'visual'): { head: string; marker: string; tail: string } | null => {
178 if (shape === 'logical' || !R.test(line)) return null
179 const m = COMMENT.exec(line)
180 return m && R.test(m[3]!) ? { head: m[1]!, marker: m[2]!, tail: shapeText(visualText(m[3]!), shape) } : null
181}
182
183export const commentVisual = (text: string, shape: Shape = 'visual'): string => {
184 const c = commentTail(text, shape)
185 return c ? c.head + c.marker + c.tail : text
186}
187
188export const hasRtl = (text: string): boolean => R.test(text)
189hooks/vendor/mermaid-text.js 13 lines1function normalizeBrTags(label){return(label.startsWith('"')&&label.endsWith('"')?label.slice(1,-1):label).replace(/<br\s*\/?>/gi,`
2`).replace(/\\n/g,`
3`).replace(/<\/?(?:sub|sup|small|mark)\s*>/gi,"").replace(/\*\*(.+?)\*\*/g,"<b>$1</b>").replace(/(?<!\*)\*([^\s*](?:[^*]*[^\s*])?)\*(?!\*)/g,"<i>$1</i>").replace(/~~(.+?)~~/g,"<s>$1</s>")}function parseMermaid(text){let lines=text.split(`
4`).map(l=>l.trim()).filter(l=>l.length>0&&!l.startsWith("%%"));if(lines.length===0)throw new Error("Empty mermaid diagram");let header=lines[0];return/^stateDiagram(-v2)?\s*$/i.test(header)?parseStateDiagram(lines):parseFlowchart(lines)}function parseFlowchart(lines){let headerMatch=lines[0].match(/^(?:graph|flowchart)\s+(TD|TB|LR|BT|RL)\s*$/i);if(!headerMatch)throw new Error(`Invalid mermaid header: "${lines[0]}". Expected "graph TD", "flowchart LR", "stateDiagram-v2", etc.`);let graph={direction:headerMatch[1].toUpperCase(),nodes:new Map,edges:[],subgraphs:[],classDefs:new Map,classAssignments:new Map,nodeStyles:new Map,linkStyles:new Map},subgraphStack=[];for(let i=1;i<lines.length;i++){let line=lines[i],classDefMatch=line.match(/^classDef\s+(\w+)\s+(.+)$/);if(classDefMatch){let name=classDefMatch[1],propsStr=classDefMatch[2],props=parseStyleProps(propsStr);graph.classDefs.set(name,props);continue}let classAssignMatch=line.match(/^class\s+([\w,-]+)\s+(\w+)$/);if(classAssignMatch){let nodeIds=classAssignMatch[1].split(",").map(s=>s.trim()),className=classAssignMatch[2];for(let id of nodeIds)graph.classAssignments.set(id,className);continue}let styleMatch=line.match(/^style\s+([\w,-]+)\s+(.+)$/);if(styleMatch){let nodeIds=styleMatch[1].split(",").map(s=>s.trim()),props=parseStyleProps(styleMatch[2]);for(let id of nodeIds)graph.nodeStyles.set(id,{...graph.nodeStyles.get(id),...props});continue}let linkStyleMatch=line.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);if(linkStyleMatch){let target=linkStyleMatch[1].trim(),props=parseStyleProps(linkStyleMatch[2]);if(target==="default")graph.linkStyles.set("default",{...graph.linkStyles.get("default"),...props});else{let indices=target.split(",").map(s=>parseInt(s.trim(),10));for(let idx of indices)isNaN(idx)||graph.linkStyles.set(idx,{...graph.linkStyles.get(idx),...props})}continue}let dirMatch=line.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);if(dirMatch&&subgraphStack.length>0){subgraphStack[subgraphStack.length-1].direction=dirMatch[1].toUpperCase();continue}let subgraphMatch=line.match(/^subgraph\s+(.+)$/);if(subgraphMatch){let rest=subgraphMatch[1].trim(),bracketMatch=rest.match(/^([\w-]+)\s*\[(.+)\]$/),id,label;bracketMatch?(id=bracketMatch[1],label=normalizeBrTags(bracketMatch[2])):(label=normalizeBrTags(rest),id=rest.replace(/\s+/g,"_").replace(/[^\w]/g,""));let sg={id,label,nodeIds:[],children:[]};subgraphStack.push(sg);continue}if(line==="end"){let completed=subgraphStack.pop();completed&&(subgraphStack.length>0?subgraphStack[subgraphStack.length-1].children.push(completed):graph.subgraphs.push(completed));continue}parseEdgeLine(line,graph,subgraphStack)}return graph}function parseStateDiagram(lines){let graph={direction:"TD",nodes:new Map,edges:[],subgraphs:[],classDefs:new Map,classAssignments:new Map,nodeStyles:new Map,linkStyles:new Map},compositeStack=[],compositeStateIds=new Set,startCount=0,endCount=0;for(let i=1;i<lines.length;i++){let line=lines[i],dirMatch=line.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);if(dirMatch){compositeStack.length>0?compositeStack[compositeStack.length-1].direction=dirMatch[1].toUpperCase():graph.direction=dirMatch[1].toUpperCase();continue}let linkStyleMatch=line.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);if(linkStyleMatch){let target=linkStyleMatch[1].trim(),props=parseStyleProps(linkStyleMatch[2]);if(target==="default")graph.linkStyles.set("default",{...graph.linkStyles.get("default"),...props});else{let indices=target.split(",").map(s=>parseInt(s.trim(),10));for(let idx of indices)isNaN(idx)||graph.linkStyles.set(idx,{...graph.linkStyles.get(idx),...props})}continue}let compositeMatch=line.match(/^state\s+(?:"([^"]+)"\s+as\s+)?([\w\p{L}]+)\s*\{$/u);if(compositeMatch){let label=compositeMatch[1]??compositeMatch[2],id=compositeMatch[2],sg={id,label,nodeIds:[],children:[]};compositeStack.push(sg),compositeStateIds.add(id),graph.nodes.delete(id);continue}if(line==="}"){let completed=compositeStack.pop();completed&&(compositeStack.length>0?compositeStack[compositeStack.length-1].children.push(completed):graph.subgraphs.push(completed));continue}let stateAliasMatch=line.match(/^state\s+"([^"]+)"\s+as\s+([\w\p{L}]+)\s*$/u);if(stateAliasMatch){let label=normalizeBrTags(stateAliasMatch[1]),id=stateAliasMatch[2];registerStateNode(graph,compositeStack,{id,label,shape:"rounded"});continue}let transitionMatch=line.match(/^(\[\*\]|[\w\p{L}-]+)\s*(-->)\s*(\[\*\]|[\w\p{L}-]+)(?:\s*:\s*(.+))?$/u);if(transitionMatch){let sourceId=transitionMatch[1],targetId=transitionMatch[3],rawTransitionLabel=transitionMatch[4]?.trim(),edgeLabel=rawTransitionLabel?normalizeBrTags(rawTransitionLabel):void 0;sourceId==="[*]"?(startCount++,sourceId=`_start${startCount>1?startCount:""}`,registerStateNode(graph,compositeStack,{id:sourceId,label:"",shape:"state-start"})):compositeStateIds.has(sourceId)||ensureStateNode(graph,compositeStack,sourceId),targetId==="[*]"?(endCount++,targetId=`_end${endCount>1?endCount:""}`,registerStateNode(graph,compositeStack,{id:targetId,label:"",shape:"state-end"})):compositeStateIds.has(targetId)||ensureStateNode(graph,compositeStack,targetId),graph.edges.push({source:sourceId,target:targetId,label:edgeLabel,style:"solid",hasArrowStart:!1,hasArrowEnd:!0});continue}let stateDescMatch=line.match(/^([\w\p{L}-]+)\s*:\s*(.+)$/u);if(stateDescMatch){let id=stateDescMatch[1],label=normalizeBrTags(stateDescMatch[2].trim());registerStateNode(graph,compositeStack,{id,label,shape:"rounded"});continue}}return graph}function registerStateNode(graph,compositeStack,node){if(!graph.nodes.has(node.id)&&graph.nodes.set(node.id,node),compositeStack.length>0){let current=compositeStack[compositeStack.length-1];current.nodeIds.includes(node.id)||current.nodeIds.push(node.id)}}function ensureStateNode(graph,compositeStack,id){if(!graph.nodes.has(id))registerStateNode(graph,compositeStack,{id,label:id,shape:"rounded"});else if(compositeStack.length>0){let current=compositeStack[compositeStack.length-1];current.nodeIds.includes(id)||current.nodeIds.push(id)}}function parseStyleProps(propsStr){let cleaned=propsStr.replace(/;\s*$/,""),props={};for(let pair of cleaned.split(",")){let colonIdx=pair.indexOf(":");if(colonIdx>0){let key=pair.slice(0,colonIdx).trim(),val=pair.slice(colonIdx+1).trim();key&&val&&(props[key]=val)}}return props}var ARROW_REGEX=/^(<)?(-->|-.->|==>|---|-\.-|===)(?:\|([^|]*)\|)?/,TEXT_ARROW_REGEX=/^(<)?(--|-\.|==)\s+(.+?)\s+(-->|---|\.\->|-\.\-|==>|===)/,NODE_PATTERNS=[{regex:/^([\w-]+)\(\(\((.+?)\)\)\)/,shape:"doublecircle"},{regex:/^([\w-]+)\(\[(.+?)\]\)/,shape:"stadium"},{regex:/^([\w-]+)\(\((.+?)\)\)/,shape:"circle"},{regex:/^([\w-]+)\[\[(.+?)\]\]/,shape:"subroutine"},{regex:/^([\w-]+)\[\((.+?)\)\]/,shape:"cylinder"},{regex:/^([\w-]+)\[\/(.+?)\\\]/,shape:"trapezoid"},{regex:/^([\w-]+)\[\\(.+?)\/\]/,shape:"trapezoid-alt"},{regex:/^([\w-]+)>(.+?)\]/,shape:"asymmetric"},{regex:/^([\w-]+)\{\{(.+?)\}\}/,shape:"hexagon"},{regex:/^([\w-]+)\[(.+?)\]/,shape:"rectangle"},{regex:/^([\w-]+)\((.+?)\)/,shape:"rounded"},{regex:/^([\w-]+)\{(.+?)\}/,shape:"diamond"}],BARE_NODE_REGEX=/^([\w-]+)/,CLASS_SHORTHAND_REGEX=/^:::([\w][\w-]*)/;function parseEdgeLine(line,graph,subgraphStack){let remaining=line.trim(),firstGroup=consumeNodeGroup(remaining,graph,subgraphStack);if(!firstGroup||firstGroup.ids.length===0)return;remaining=firstGroup.remaining.trim();let prevGroupIds=firstGroup.ids;for(;remaining.length>0;){let hasArrowStart,style,hasArrowEnd,edgeLabel,arrowMatch=remaining.match(ARROW_REGEX);if(arrowMatch){hasArrowStart=!!arrowMatch[1];let arrowOp=arrowMatch[2],rawEdgeLabel=arrowMatch[3]?.trim();edgeLabel=rawEdgeLabel?normalizeBrTags(rawEdgeLabel):void 0,remaining=remaining.slice(arrowMatch[0].length).trim(),style=arrowStyleFromOp(arrowOp),hasArrowEnd=arrowOp.endsWith(">")}else{let textMatch=remaining.match(TEXT_ARROW_REGEX);if(!textMatch)break;hasArrowStart=!!textMatch[1];let rawLabel=textMatch[3].trim();edgeLabel=rawLabel?normalizeBrTags(rawLabel):void 0;let openOp=textMatch[2],closeOp=textMatch[4];remaining=remaining.slice(textMatch[0].length).trim(),style=textArrowStyleFromOps(openOp,closeOp),hasArrowEnd=closeOp.endsWith(">")}let nextGroup=consumeNodeGroup(remaining,graph,subgraphStack);if(!nextGroup||nextGroup.ids.length===0)break;remaining=nextGroup.remaining.trim();for(let sourceId of prevGroupIds)for(let targetId of nextGroup.ids)graph.edges.push({source:sourceId,target:targetId,label:edgeLabel,style,hasArrowStart,hasArrowEnd});prevGroupIds=nextGroup.ids}}function consumeNodeGroup(text,graph,subgraphStack){let first=consumeNode(text,graph,subgraphStack);if(!first)return null;let ids=[first.id],remaining=first.remaining.trim();for(;remaining.startsWith("&");){remaining=remaining.slice(1).trim();let next=consumeNode(remaining,graph,subgraphStack);if(!next)break;ids.push(next.id),remaining=next.remaining.trim()}return{ids,remaining}}function consumeNode(text,graph,subgraphStack){let id=null,remaining=text;for(let{regex,shape}of NODE_PATTERNS){let match=text.match(regex);if(match){id=match[1];let label=normalizeBrTags(match[2]);registerNode(graph,subgraphStack,{id,label,shape}),remaining=text.slice(match[0].length);break}}if(id===null){let bareMatch=text.match(BARE_NODE_REGEX);bareMatch&&(id=bareMatch[1],graph.nodes.has(id)||registerNode(graph,subgraphStack,{id,label:id,shape:"rectangle"}),remaining=text.slice(bareMatch[0].length))}if(id===null)return null;let classMatch=remaining.match(CLASS_SHORTHAND_REGEX);return classMatch&&(graph.classAssignments.set(id,classMatch[1]),remaining=remaining.slice(classMatch[0].length)),{id,remaining}}function registerNode(graph,subgraphStack,node){!graph.nodes.has(node.id)&&graph.nodes.set(node.id,node),trackInSubgraph(subgraphStack,node.id)}function trackInSubgraph(subgraphStack,nodeId){if(subgraphStack.length>0){let current=subgraphStack[subgraphStack.length-1];current.nodeIds.includes(nodeId)||current.nodeIds.push(nodeId)}}function arrowStyleFromOp(op){return op==="-.->"||op==="-.-"?"dotted":op==="==>"||op==="==="?"thick":"solid"}function textArrowStyleFromOps(openOp,closeOp){return openOp==="-."||closeOp===".->"||closeOp==="-.-"?"dotted":openOp==="=="||closeOp==="==>"||closeOp==="==="?"thick":"solid"}var Up={x:1,y:0},Down={x:1,y:2},Left={x:0,y:1},Right={x:2,y:1},UpperRight={x:2,y:0},UpperLeft={x:0,y:0},LowerRight={x:2,y:2},LowerLeft={x:0,y:2},Middle={x:1,y:1};function gridCoordEquals(a,b){return a.x===b.x&&a.y===b.y}function drawingCoordEquals(a,b){return a.x===b.x&&a.y===b.y}function gridCoordDirection(c,dir){return{x:c.x+dir.x,y:c.y+dir.y}}function gridKey(c){return`${c.x},${c.y}`}var EMPTY_STYLE={name:"",styles:{}};var DEFAULT_ASCII_THEME={fg:"#27272a",border:"#a1a1aa",line:"#71717a",arrow:"#52525b",corner:"#71717a",junction:"#a1a1aa"};function detectColorMode(){let proc=globalThis.process;if(proc){if(!proc.stdout?.isTTY)return"none";let colorTerm=proc.env?.COLORTERM?.toLowerCase()??"",term=proc.env?.TERM?.toLowerCase()??"";return colorTerm==="truecolor"||colorTerm==="24bit"?"truecolor":term.includes("256color")||term.includes("256")?"ansi256":term&&term!=="dumb"?"ansi16":"none"}return typeof document<"u"?"html":"none"}function parseHex(hex){let h=hex.replace("#","");return h.length===3?{r:parseInt(h[0]+h[0],16),g:parseInt(h[1]+h[1],16),b:parseInt(h[2]+h[2],16)}:{r:parseInt(h.substring(0,2),16),g:parseInt(h.substring(2,4),16),b:parseInt(h.substring(4,6),16)}}var ESC="\x1B[",RESET=`${ESC}0m`;function truecolorFg(hex){let{r,g,b}=parseHex(hex);return`${ESC}38;2;${r};${g};${b}m`}function rgbTo256(r,g,b){let avg=(r+g+b)/3;if(Math.max(Math.abs(r-avg),Math.abs(g-avg),Math.abs(b-avg))<10){let gray=Math.round(avg/255*23);return 232+Math.min(23,Math.max(0,gray))}let toIndex=v=>v<48?0:v<115?1:Math.min(5,Math.floor((v-35)/40)),ri=toIndex(r),gi=toIndex(g),bi=toIndex(b);return 16+36*ri+6*gi+bi}function ansi256Fg(hex){let{r,g,b}=parseHex(hex),index=rgbTo256(r,g,b);return`${ESC}38;5;${index}m`}function ansi16Fg(hex){let{r,g,b}=parseHex(hex),luma=.299*r+.587*g+.114*b,bright=luma>100?0:60,code;return r>180&&g<100&&b<100?code=31:g>180&&r<100&&b<100?code=32:r>150&&g>150&&b<100?code=33:b>180&&r<100&&g<100?code=34:r>150&&b>150&&g<100?code=35:g>150&&b>150&&r<100?code=36:luma>200?code=37:luma<50?code=30:code=37,`${ESC}${code+bright}m`}function escapeHtml(text){return text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function htmlSpan(hex,text){return`<span style="color:${hex}">${escapeHtml(text)}</span>`}function getRoleColor(role,theme){switch(role){case"text":return theme.fg;case"border":return theme.border;case"line":return theme.line;case"arrow":return theme.arrow;case"corner":return theme.corner??theme.line;case"junction":return theme.junction??theme.border;default:return theme.fg}}function getAnsiColor(role,theme,mode){if(mode==="none")return"";let hex=getRoleColor(role,theme);switch(mode){case"truecolor":return truecolorFg(hex);case"ansi256":return ansi256Fg(hex);case"ansi16":return ansi16Fg(hex);default:return""}}function colorizeLine(chars,roles,theme,mode){if(mode==="none")return chars.join("");if(mode==="html")return colorizeLineHtml(chars,roles,theme);let result="",currentRole=null,buffer="";for(let i=0;i<chars.length;i++){let char=chars[i],role=roles[i]??null;if(char===" "){buffer.length>0&&(currentRole!==null?result+=getAnsiColor(currentRole,theme,mode)+buffer+RESET:result+=buffer,buffer="",currentRole=null),result+=char;continue}if(role===currentRole){buffer+=char;continue}buffer.length>0&&(currentRole!==null?result+=getAnsiColor(currentRole,theme,mode)+buffer+RESET:result+=buffer),buffer=char,currentRole=role}return buffer.length>0&¤tRole!==null?result+=getAnsiColor(currentRole,theme,mode)+buffer+RESET:buffer.length>0&&(result+=buffer),result}function colorizeLineHtml(chars,roles,theme){let result="",currentRole=null,buffer="",flush=()=>{buffer.length!==0&&(currentRole!==null?result+=htmlSpan(getRoleColor(currentRole,theme),buffer):result+=escapeHtml(buffer),buffer="",currentRole=null)};for(let i=0;i<chars.length;i++){let char=chars[i],role=roles[i]??null;if(char===" "){flush(),result+=" ";continue}if(role===currentRole){buffer+=char;continue}flush(),buffer=char,currentRole=role}return flush(),result}function colorizeText(text,hex,mode){if(mode==="none"||text.length===0)return text;if(mode==="html")return htmlSpan(hex,text);let code;switch(mode){case"truecolor":code=truecolorFg(hex);break;case"ansi256":code=ansi256Fg(hex);break;case"ansi16":code=ansi16Fg(hex);break;default:return text}return`${code}${text}${RESET}`}function mkCanvas(x,y){let canvas=[];for(let i=0;i<=x;i++){let col=[];for(let j=0;j<=y;j++)col.push(" ");canvas.push(col)}return canvas}function copyCanvas(source){let[maxX,maxY]=getCanvasSize(source);return mkCanvas(maxX,maxY)}function mkRoleCanvas(x,y){let roleCanvas=[];for(let i=0;i<=x;i++){let col=[];for(let j=0;j<=y;j++)col.push(null);roleCanvas.push(col)}return roleCanvas}function increaseRoleCanvasSize(roleCanvas,newX,newY){let currX=roleCanvas.length-1,currY=(roleCanvas[0]?.length??1)-1,targetX=Math.max(newX,currX),targetY=Math.max(newY,currY),grown=mkRoleCanvas(targetX,targetY);for(let x=0;x<grown.length;x++)for(let y=0;y<grown[0].length;y++)x<roleCanvas.length&&y<roleCanvas[0].length&&(grown[x][y]=roleCanvas[x][y]);return roleCanvas.length=0,roleCanvas.push(...grown),roleCanvas}function setRole(roleCanvas,x,y,role){(x>=roleCanvas.length||y>=(roleCanvas[0]?.length??0))&&increaseRoleCanvasSize(roleCanvas,x,y),roleCanvas[x][y]=role}function getCanvasSize(canvas){return[canvas.length-1,(canvas[0]?.length??1)-1]}function increaseSize(canvas,newX,newY){let[currX,currY]=getCanvasSize(canvas),targetX=Math.max(newX,currX),targetY=Math.max(newY,currY),grown=mkCanvas(targetX,targetY);for(let x=0;x<grown.length;x++)for(let y=0;y<grown[0].length;y++)x<canvas.length&&y<canvas[0].length&&(grown[x][y]=canvas[x][y]);return canvas.length=0,canvas.push(...grown),canvas}var JUNCTION_CHARS=new Set(["\u2500","\u2502","\u250C","\u2510","\u2514","\u2518","\u251C","\u2524","\u252C","\u2534","\u253C","\u2574","\u2575","\u2576","\u2577"]);function clearLabelGaps(canvas,label){for(let y=0;y<(label[0]?.length??0);y++){let xs=label.map((col,x)=>col[y]!==" "?x:-1).filter(x=>x>=0);if(xs.length!==0)for(let x=xs[0];x<=xs[xs.length-1];x++)label[x][y]===" "&&!isAlphanumeric(canvas[x][y])&&(canvas[x][y]=" ")}}function isJunctionChar(c){return JUNCTION_CHARS.has(c)}function isAlphanumeric(c){return/^[a-zA-Z0-9]$/.test(c)}var JUNCTION_MAP={"\u2500":{"\u2502":"\u253C","\u250C":"\u252C","\u2510":"\u252C","\u2514":"\u2534","\u2518":"\u2534","\u251C":"\u253C","\u2524":"\u253C","\u252C":"\u252C","\u2534":"\u2534"},"\u2502":{"\u2500":"\u253C","\u250C":"\u251C","\u2510":"\u2524","\u2514":"\u251C","\u2518":"\u2524","\u251C":"\u251C","\u2524":"\u2524","\u252C":"\u253C","\u2534":"\u253C"},"\u250C":{"\u2500":"\u252C","\u2502":"\u251C","\u2510":"\u252C","\u2514":"\u251C","\u2518":"\u253C","\u251C":"\u251C","\u2524":"\u253C","\u252C":"\u252C","\u2534":"\u253C"},"\u2510":{"\u2500":"\u252C","\u2502":"\u2524","\u250C":"\u252C","\u2514":"\u253C","\u2518":"\u2524","\u251C":"\u253C","\u2524":"\u2524","\u252C":"\u252C","\u2534":"\u253C"},"\u2514":{"\u2500":"\u2534","\u2502":"\u251C","\u250C":"\u251C","\u2510":"\u253C","\u2518":"\u2534","\u251C":"\u251C","\u2524":"\u253C","\u252C":"\u253C","\u2534":"\u2534"},"\u2518":{"\u2500":"\u2534","\u2502":"\u2524","\u250C":"\u253C","\u2510":"\u2524","\u2514":"\u2534","\u251C":"\u253C","\u2524":"\u2524","\u252C":"\u253C","\u2534":"\u2534"},"\u251C":{"\u2500":"\u253C","\u2502":"\u251C","\u250C":"\u251C","\u2510":"\u253C","\u2514":"\u251C","\u2518":"\u253C","\u2524":"\u253C","\u252C":"\u253C","\u2534":"\u253C"},"\u2524":{"\u2500":"\u253C","\u2502":"\u2524","\u250C":"\u253C","\u2510":"\u2524","\u2514":"\u253C","\u2518":"\u2524","\u251C":"\u253C","\u252C":"\u253C","\u2534":"\u253C"},"\u252C":{"\u2500":"\u252C","\u2502":"\u253C","\u250C":"\u252C","\u2510":"\u252C","\u2514":"\u253C","\u2518":"\u253C","\u251C":"\u253C","\u2524":"\u253C","\u2534":"\u253C"},"\u2534":{"\u2500":"\u2534","\u2502":"\u253C","\u250C":"\u253C","\u2510":"\u253C","\u2514":"\u2534","\u2518":"\u2534","\u251C":"\u253C","\u2524":"\u253C","\u252C":"\u253C"}};function mergeJunctions(c1,c2){return JUNCTION_MAP[c1]?.[c2]??c1}function mergeCanvases(base,offset,useAscii,...overlays){let[maxX,maxY]=getCanvasSize(base);for(let overlay of overlays){let[oX,oY]=getCanvasSize(overlay);maxX=Math.max(maxX,oX+offset.x),maxY=Math.max(maxY,oY+offset.y)}let merged=mkCanvas(maxX,maxY);for(let x=0;x<=maxX;x++)for(let y=0;y<=maxY;y++)x<base.length&&y<base[0].length&&(merged[x][y]=base[x][y]);for(let overlay of overlays)for(let x=0;x<overlay.length;x++)for(let y=0;y<overlay[0].length;y++){let c=overlay[x][y];if(c!==" "){let mx=x+offset.x,my=y+offset.y,current=merged[mx][my];!useAscii&&isJunctionChar(c)&&isJunctionChar(current)?merged[mx][my]=mergeJunctions(current,c):isAlphanumeric(current)&&isAlphanumeric(c)||(merged[mx][my]=c)}}return merged}function canvasToString(canvas,options){let[maxX,maxY]=getCanvasSize(canvas),lines=[],roleCanvas=options?.roleCanvas,colorMode=options?.colorMode??"none",theme=options?.theme??DEFAULT_ASCII_THEME;for(let y=0;y<=maxY;y++)if(colorMode==="none"||!roleCanvas){let line="";for(let x=0;x<=maxX;x++)line+=canvas[x][y];lines.push(line)}else{let chars=[],roles=[];for(let x=0;x<=maxX;x++)chars.push(canvas[x][y]),roles.push(roleCanvas[x]?.[y]??null);lines.push(colorizeLine(chars,roles,theme,colorMode))}return lines.join(`
5`)}var VERTICAL_FLIP_MAP={"\u25B2":"\u25BC","\u25BC":"\u25B2","\u25E4":"\u25E3","\u25E3":"\u25E4","\u25E5":"\u25E2","\u25E2":"\u25E5","^":"v",v:"^","\u250C":"\u2514","\u2514":"\u250C","\u2510":"\u2518","\u2518":"\u2510","\u252C":"\u2534","\u2534":"\u252C","\u2575":"\u2577","\u2577":"\u2575"};function flipCanvasVertically(canvas){for(let col of canvas)col.reverse();for(let col of canvas)for(let y=0;y<col.length;y++){let flipped=VERTICAL_FLIP_MAP[col[y]];flipped&&(col[y]=flipped)}return canvas}function flipRoleCanvasVertically(roleCanvas){for(let col of roleCanvas)col.reverse();return roleCanvas}function drawText(canvas,start,text,forceOverwrite=!1){increaseSize(canvas,start.x+text.length,start.y);for(let i=0;i<text.length;i++){let x=start.x+i,current=canvas[x][start.y];(forceOverwrite||current===" ")&&(canvas[x][start.y]=text[i])}}function setCanvasSizeToGrid(canvas,columnWidth,rowHeight){let maxX=0,maxY=0;for(let w of columnWidth.values())maxX+=w;for(let h of rowHeight.values())maxY+=h;increaseSize(canvas,maxX-1,maxY-1)}function setRoleCanvasSizeToGrid(roleCanvas,columnWidth,rowHeight){let maxX=0,maxY=0;for(let w of columnWidth.values())maxX+=w;for(let h of rowHeight.values())maxY+=h;increaseRoleCanvasSize(roleCanvas,maxX-1,maxY-1)}function convertToAsciiGraph(parsed,config){let nodeMap=new Map,index=0;for(let[id,mNode]of parsed.nodes){let asciiNode={name:id,displayLabel:mNode.label,shape:mNode.shape,index,gridCoord:null,drawingCoord:null,drawing:null,drawn:!1,styleClassName:"",styleClass:EMPTY_STYLE};nodeMap.set(id,asciiNode),index++}let nodes=[...nodeMap.values()],edges=[];for(let mEdge of parsed.edges){let from=nodeMap.get(mEdge.source),to=nodeMap.get(mEdge.target);!from||!to||edges.push({from,to,text:mEdge.label??"",path:[],labelLine:[],startDir:{x:0,y:0},endDir:{x:0,y:0},style:mEdge.style,hasArrowStart:mEdge.hasArrowStart,hasArrowEnd:mEdge.hasArrowEnd})}let subgraphs=[];for(let mSg of parsed.subgraphs)convertSubgraph(mSg,null,nodeMap,subgraphs);deduplicateSubgraphNodes(parsed.subgraphs,subgraphs,nodeMap,parsed);for(let[nodeId,className]of parsed.classAssignments){let node=nodeMap.get(nodeId),classDef=parsed.classDefs.get(className);node&&classDef&&(node.styleClassName=className,node.styleClass={name:className,styles:classDef})}return{nodes,edges,canvas:mkCanvas(0,0),roleCanvas:mkRoleCanvas(0,0),grid:new Map,columnWidth:new Map,rowHeight:new Map,subgraphs,config,offsetX:0,offsetY:0,bundles:[]}}function convertSubgraph(mSg,parent,nodeMap,allSubgraphs){let normalizedDirection;mSg.direction&&(normalizedDirection=mSg.direction==="LR"||mSg.direction==="RL"?"LR":"TD");let sg={name:mSg.label,nodes:[],parent,children:[],minX:0,minY:0,maxX:0,maxY:0,direction:normalizedDirection};for(let nodeId of mSg.nodeIds){let node=nodeMap.get(nodeId);node&&sg.nodes.push(node)}allSubgraphs.push(sg);for(let childMSg of mSg.children){let child=convertSubgraph(childMSg,sg,nodeMap,allSubgraphs);sg.children.push(child);for(let childNode of child.nodes)sg.nodes.includes(childNode)||sg.nodes.push(childNode)}return sg}function deduplicateSubgraphNodes(mermaidSubgraphs,asciiSubgraphs,nodeMap,parsed){let sgMap=new Map;buildSgMap(mermaidSubgraphs,asciiSubgraphs,sgMap);let nodeOwner=new Map;function claimNodes(mSg){let asciiSg=sgMap.get(mSg);if(asciiSg){for(let child of mSg.children)claimNodes(child);for(let nodeId of mSg.nodeIds)nodeOwner.has(nodeId)||nodeOwner.set(nodeId,asciiSg)}}for(let mSg of mermaidSubgraphs)claimNodes(mSg);for(let asciiSg of asciiSubgraphs)asciiSg.nodes=asciiSg.nodes.filter(node=>{let nodeId;for(let[id,n]of nodeMap)if(n===node){nodeId=id;break}if(!nodeId)return!1;let owner=nodeOwner.get(nodeId);return owner?isAncestorOrSelf(asciiSg,owner):!0})}function isAncestorOrSelf(candidate,target){let current=target;for(;current!==null;){if(current===candidate)return!0;current=current.parent}return!1}function buildSgMap(mSgs,aSgs,result){let flatMermaid=[];function flatten(sgs){for(let sg of sgs)flatMermaid.push(sg),flatten(sg.children)}flatten(mSgs);for(let i=0;i<flatMermaid.length&&i<aSgs.length;i++)result.set(flatMermaid[i],aSgs[i])}var MinHeap=class{items=[];get length(){return this.items.length}push(item){this.items.push(item),this.bubbleUp(this.items.length-1)}pop(){if(this.items.length===0)return;let top=this.items[0],last=this.items.pop();return this.items.length>0&&(this.items[0]=last,this.sinkDown(0)),top}bubbleUp(i){for(;i>0;){let parent=i-1>>1;if(this.items[i].priority<this.items[parent].priority)[this.items[i],this.items[parent]]=[this.items[parent],this.items[i]],i=parent;else break}}sinkDown(i){let n=this.items.length;for(;;){let smallest=i,left=2*i+1,right=2*i+2;if(left<n&&this.items[left].priority<this.items[smallest].priority&&(smallest=left),right<n&&this.items[right].priority<this.items[smallest].priority&&(smallest=right),smallest!==i)[this.items[i],this.items[smallest]]=[this.items[smallest],this.items[i]],i=smallest;else break}}};function heuristic(a,b){let absX=Math.abs(a.x-b.x),absY=Math.abs(a.y-b.y);return absX===0||absY===0?absX+absY:absX+absY+1}var MOVE_DIRS=[{x:1,y:0},{x:-1,y:0},{x:0,y:1},{x:0,y:-1}];function isFreeInGrid(grid,c){return c.x<0||c.y<0?!1:!grid.has(gridKey(c))}function getPath(grid,from,to){let pq=new MinHeap;pq.push({coord:from,priority:0});let costSoFar=new Map;costSoFar.set(gridKey(from),0);let cameFrom=new Map;for(cameFrom.set(gridKey(from),null);pq.length>0;){let current=pq.pop().coord;if(gridCoordEquals(current,to)){let path=[],c=current;for(;c!==null;)path.unshift(c),c=cameFrom.get(gridKey(c))??null;return path}let currentCost=costSoFar.get(gridKey(current));for(let dir of MOVE_DIRS){let next={x:current.x+dir.x,y:current.y+dir.y};if(!isFreeInGrid(grid,next)&&!gridCoordEquals(next,to))continue;let newCost=currentCost+1,nextKey=gridKey(next),existingCost=costSoFar.get(nextKey);if(existingCost===void 0||newCost<existingCost){costSoFar.set(nextKey,newCost);let priority=newCost+heuristic(next,to);pq.push({coord:next,priority}),cameFrom.set(nextKey,current)}}}return null}function mergePath(path){if(path.length<=2)return path;let toRemove=new Set,step0=path[0],step1=path[1];for(let idx=2;idx<path.length;idx++){let step2=path[idx],prevDx=step1.x-step0.x,prevDy=step1.y-step0.y,dx=step2.x-step1.x,dy=step2.y-step1.y;prevDx===dx&&prevDy===dy&&toRemove.add(idx-1),step0=step1,step1=step2}return path.filter((_,i)=>!toRemove.has(i))}function getOpposite(d){return d===Up?Down:d===Down?Up:d===Left?Right:d===Right?Left:d===UpperRight?LowerLeft:d===UpperLeft?LowerRight:d===LowerRight?UpperLeft:d===LowerLeft?UpperRight:Middle}function dirEquals(a,b){return a.x===b.x&&a.y===b.y}function determineDirection(from,to){return from.x===to.x?from.y<to.y?Down:Up:from.y===to.y?from.x<to.x?Right:Left:from.x<to.x?from.y<to.y?LowerRight:UpperRight:from.y<to.y?LowerLeft:UpperLeft}function selfReferenceDirection(graphDirection){return graphDirection==="LR"?[Right,Down,Down,Right]:[Down,Right,Right,Down]}function determineStartAndEndDir(edge,graphDirection){if(edge.from===edge.to)return selfReferenceDirection(graphDirection);let d=determineDirection(edge.from.gridCoord,edge.to.gridCoord),preferredDir,preferredOppositeDir,alternativeDir,alternativeOppositeDir,isBackwards=graphDirection==="LR"?dirEquals(d,Left)||dirEquals(d,UpperLeft)||dirEquals(d,LowerLeft):dirEquals(d,Up)||dirEquals(d,UpperLeft)||dirEquals(d,UpperRight);return dirEquals(d,LowerRight)?graphDirection==="LR"?(preferredDir=Down,preferredOppositeDir=Left,alternativeDir=Right,alternativeOppositeDir=Up):(preferredDir=Right,preferredOppositeDir=Up,alternativeDir=Down,alternativeOppositeDir=Left):dirEquals(d,UpperRight)?graphDirection==="LR"?(preferredDir=Up,preferredOppositeDir=Left,alternativeDir=Right,alternativeOppositeDir=Down):(preferredDir=Right,preferredOppositeDir=Down,alternativeDir=Up,alternativeOppositeDir=Left):dirEquals(d,LowerLeft)?graphDirection==="LR"?(preferredDir=Down,preferredOppositeDir=Down,alternativeDir=Left,alternativeOppositeDir=Up):(preferredDir=Left,preferredOppositeDir=Up,alternativeDir=Down,alternativeOppositeDir=Right):dirEquals(d,UpperLeft)?graphDirection==="LR"?(preferredDir=Down,preferredOppositeDir=Down,alternativeDir=Left,alternativeOppositeDir=Down):(preferredDir=Right,preferredOppositeDir=Right,alternativeDir=Up,alternativeOppositeDir=Right):isBackwards?graphDirection==="LR"&&dirEquals(d,Left)?(preferredDir=Down,preferredOppositeDir=Down,alternativeDir=Left,alternativeOppositeDir=Right):graphDirection==="TD"&&dirEquals(d,Up)?(preferredDir=Right,preferredOppositeDir=Right,alternativeDir=Up,alternativeOppositeDir=Down):(preferredDir=d,preferredOppositeDir=getOpposite(d),alternativeDir=d,alternativeOppositeDir=getOpposite(d)):(preferredDir=d,preferredOppositeDir=getOpposite(d),alternativeDir=d,alternativeOppositeDir=getOpposite(d)),[preferredDir,preferredOppositeDir,alternativeDir,alternativeOppositeDir]}function determinePath(graph,edge){let sourceSg=getNodeSubgraph(graph,edge.from),targetSg=getNodeSubgraph(graph,edge.to),effectiveDir=sourceSg&&sourceSg===targetSg&&sourceSg.direction?sourceSg.direction:graph.config.graphDirection,[preferredDir,preferredOppositeDir,alternativeDir,alternativeOppositeDir]=determineStartAndEndDir(edge,effectiveDir),prefFrom=gridCoordDirection(edge.from.gridCoord,preferredDir),prefTo=gridCoordDirection(edge.to.gridCoord,preferredOppositeDir),preferredPath=getPath(graph.grid,prefFrom,prefTo),altFrom=gridCoordDirection(edge.from.gridCoord,alternativeDir),altTo=gridCoordDirection(edge.to.gridCoord,alternativeOppositeDir),alternativePath=getPath(graph.grid,altFrom,altTo);if(preferredPath!==null&&alternativePath!==null){preferredPath=mergePath(preferredPath),alternativePath=mergePath(alternativePath),preferredPath.length<=alternativePath.length?(edge.startDir=preferredDir,edge.endDir=preferredOppositeDir,edge.path=preferredPath):(edge.startDir=alternativeDir,edge.endDir=alternativeOppositeDir,edge.path=alternativePath);return}if(preferredPath!==null){edge.startDir=preferredDir,edge.endDir=preferredOppositeDir,edge.path=mergePath(preferredPath);return}if(alternativePath!==null){edge.startDir=alternativeDir,edge.endDir=alternativeOppositeDir,edge.path=mergePath(alternativePath);return}edge.startDir=preferredDir,edge.endDir=preferredOppositeDir,edge.path=[prefFrom,prefTo]}function determineLabelLine(graph,edge){if(edge.text.length===0)return;let lenLabel=edge.text.length,pathLen=edge.path.length,isVerticalFlow=graph.config.graphDirection==="TD",segments=[];for(let i=1;i<pathLen;i++){let p1=edge.path[i-1],p2=edge.path[i],line=[p1,p2],width=calculateLineWidth(graph,line),isVertical=p1.x===p2.x;segments.push({line,width,index:i,isVertical})}let suitableSegments=segments.filter(s=>s.width>=lenLabel&&s.index>1),largestLine;if(suitableSegments.length>0)suitableSegments.sort((a,b)=>b.index-a.index),largestLine=suitableSegments[0].line;else{let fallbackSegments=segments.filter(s=>s.width>=lenLabel);fallbackSegments.length>0?(fallbackSegments.sort((a,b)=>b.index-a.index),largestLine=fallbackSegments[0].line):(segments.sort((a,b)=>b.width-a.width),largestLine=segments[0]?.line??[edge.path[0],edge.path[1]])}let minX=Math.min(largestLine[0].x,largestLine[1].x),maxX=Math.max(largestLine[0].x,largestLine[1].x),middleX=minX+Math.floor((maxX-minX)/2),current=graph.columnWidth.get(middleX)??0;graph.columnWidth.set(middleX,Math.max(current,lenLabel+2)),edge.labelLine=[largestLine[0],largestLine[1]]}function calculateLineWidth(graph,line){let total=0,startX=Math.min(line[0].x,line[1].x),endX=Math.max(line[0].x,line[1].x);for(let x=startX;x<=endX;x++)total+=graph.columnWidth.get(x)??0;return total}function analyzeEdgeBundles(graph){if(graph.config.graphDirection!=="TD")return[];let bundles=[],bundledEdges=new Set,edgesByTarget=new Map;for(let edge of graph.edges){if(edge.from===edge.to)continue;let existing=edgesByTarget.get(edge.to)??[];existing.push(edge),edgesByTarget.set(edge.to,existing)}for(let[target,edges]of edgesByTarget){if(edges.length<2||!canBundle(edges,graph)||edges.some(e=>bundledEdges.has(e)))continue;let bundle={type:"fan-in",edges:[...edges],sharedNode:target,otherNodes:edges.map(e=>e.from),junctionPoint:null,sharedPath:[],junctionDir:Middle,sharedNodeDir:Middle};for(let edge of edges)edge.bundle=bundle,bundledEdges.add(edge);bundles.push(bundle)}let edgesBySource=new Map;for(let edge of graph.edges){if(edge.from===edge.to||bundledEdges.has(edge))continue;let existing=edgesBySource.get(edge.from)??[];existing.push(edge),edgesBySource.set(edge.from,existing)}for(let[source,edges]of edgesBySource){if(edges.length<2||!canBundle(edges,graph))continue;let bundle={type:"fan-out",edges:[...edges],sharedNode:source,otherNodes:edges.map(e=>e.to),junctionPoint:null,sharedPath:[],junctionDir:Middle,sharedNodeDir:Middle};for(let edge of edges)edge.bundle=bundle,bundledEdges.add(edge);bundles.push(bundle)}return bundles}function canBundle(edges,graph){if(edges.length<2)return!1;let firstStyle=edges[0].style,firstFromSg=getNodeSubgraph(graph,edges[0].from),firstToSg=getNodeSubgraph(graph,edges[0].to);for(let edge of edges){if(edge.style!==firstStyle||edge.text.length>0)return!1;let fromSg=getNodeSubgraph(graph,edge.from),toSg=getNodeSubgraph(graph,edge.to);if(fromSg!==firstFromSg||toSg!==firstToSg||fromSg!==toSg)return!1}return!0}function calculateJunctionPoint(graph,bundle){let dir=graph.config.graphDirection,sharedCoord=bundle.sharedNode.gridCoord,otherCoords=bundle.otherNodes.map(n=>n.gridCoord);if(bundle.type==="fan-in"){let minX=Math.min(...otherCoords.map(c=>c.x)),maxX=Math.max(...otherCoords.map(c=>c.x)),minY=Math.min(...otherCoords.map(c=>c.y)),maxY=Math.max(...otherCoords.map(c=>c.y));if(dir==="TD"){let junctionY=sharedCoord.y-1,centerX=Math.floor((minX+maxX)/2)+1;return{x:sharedCoord.x+1,y:junctionY}}else{let junctionX=sharedCoord.x-1,junctionY=sharedCoord.y+1;return{x:junctionX,y:junctionY}}}else{let minX=Math.min(...otherCoords.map(c=>c.x)),maxX=Math.max(...otherCoords.map(c=>c.x)),minY=Math.min(...otherCoords.map(c=>c.y)),maxY=Math.max(...otherCoords.map(c=>c.y));if(dir==="TD"){let junctionY=sharedCoord.y+3;return{x:sharedCoord.x+1,y:junctionY}}else{let junctionX=sharedCoord.x+3,junctionY=sharedCoord.y+1;return{x:junctionX,y:junctionY}}}}function routeBundledEdges(graph,bundle){let dir=graph.config.graphDirection;bundle.junctionPoint=calculateJunctionPoint(graph,bundle);let junction=bundle.junctionPoint;if(bundle.type==="fan-in"){bundle.junctionDir=dir==="TD"?Up:Left,bundle.sharedNodeDir=dir==="TD"?Down:Right;let targetCoord=bundle.sharedNode.gridCoord,targetEntry=dir==="TD"?{x:targetCoord.x+1,y:targetCoord.y}:{x:targetCoord.x,y:targetCoord.y+1},sharedPath=getPath(graph.grid,junction,targetEntry);bundle.sharedPath=sharedPath?mergePath(sharedPath):[junction,targetEntry];for(let edge of bundle.edges){let sourceCoord=edge.from.gridCoord,sourceExit=dir==="TD"?{x:sourceCoord.x+1,y:sourceCoord.y+2}:{x:sourceCoord.x+2,y:sourceCoord.y+1},pathToJunction=getPath(graph.grid,sourceExit,junction);edge.pathToJunction=pathToJunction?mergePath(pathToJunction):[sourceExit,junction],edge.startDir=dir==="TD"?Down:Right,edge.endDir=dir==="TD"?Up:Left,edge.path=[...edge.pathToJunction,...bundle.sharedPath.slice(1)]}}else{bundle.junctionDir=dir==="TD"?Down:Right,bundle.sharedNodeDir=dir==="TD"?Up:Left;let sourceCoord=bundle.sharedNode.gridCoord,sourceExit=dir==="TD"?{x:sourceCoord.x+1,y:sourceCoord.y+2}:{x:sourceCoord.x+2,y:sourceCoord.y+1},sharedPath=getPath(graph.grid,sourceExit,junction);bundle.sharedPath=sharedPath?mergePath(sharedPath):[sourceExit,junction];for(let edge of bundle.edges){let targetCoord=edge.to.gridCoord,targetEntry=dir==="TD"?{x:targetCoord.x+1,y:targetCoord.y}:{x:targetCoord.x,y:targetCoord.y+1},pathToJunction=getPath(graph.grid,junction,targetEntry);edge.pathToJunction=pathToJunction?mergePath(pathToJunction):[junction,targetEntry],edge.startDir=dir==="TD"?Down:Right,edge.endDir=dir==="TD"?Up:Left,edge.path=[...bundle.sharedPath,...edge.pathToJunction.slice(1)]}}}function processBundles(graph){for(let bundle of graph.bundles)routeBundledEdges(graph,bundle)}function splitLines(label){return label.split(`
6`)}function maxLineWidth(label){let lines=splitLines(label);return Math.max(...lines.map(l=>l.length),0)}function lineCount(label){return splitLines(label).length}var SHAPE_CORNERS={rectangle:{unicode:{tl:"\u250C",tr:"\u2510",bl:"\u2514",br:"\u2518"},ascii:{tl:"+",tr:"+",bl:"+",br:"+"}},rounded:{unicode:{tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F"},ascii:{tl:".",tr:".",bl:"'",br:"'"}},circle:{unicode:{tl:"\u25EF",tr:"\u25EF",bl:"\u25EF",br:"\u25EF"},ascii:{tl:"o",tr:"o",bl:"o",br:"o"}},doublecircle:{unicode:{tl:"\u25CE",tr:"\u25CE",bl:"\u25CE",br:"\u25CE"},ascii:{tl:"@",tr:"@",bl:"@",br:"@"}},diamond:{unicode:{tl:"\u25C7",tr:"\u25C7",bl:"\u25C7",br:"\u25C7"},ascii:{tl:"<",tr:">",bl:"<",br:">"}},hexagon:{unicode:{tl:"\u231C",tr:"\u231D",bl:"\u231E",br:"\u231F"},ascii:{tl:"*",tr:"*",bl:"*",br:"*"}},stadium:{unicode:{tl:"(",tr:")",bl:"(",br:")"},ascii:{tl:"(",tr:")",bl:"(",br:")"}},subroutine:{unicode:{tl:"\u255F",tr:"\u2562",bl:"\u255F",br:"\u2562"},ascii:{tl:"|",tr:"|",bl:"|",br:"|"}},cylinder:{unicode:{tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F"},ascii:{tl:".",tr:".",bl:"'",br:"'"}},asymmetric:{unicode:{tl:"\u25B7",tr:"\u2510",bl:"\u25B7",br:"\u2518"},ascii:{tl:">",tr:"+",bl:">",br:"+"}},trapezoid:{unicode:{tl:"/",tr:"\\",bl:"\u2514",br:"\u2518"},ascii:{tl:"/",tr:"\\",bl:"+",br:"+"}},"trapezoid-alt":{unicode:{tl:"\u250C",tr:"\u2510",bl:"\\",br:"/"},ascii:{tl:"+",tr:"+",bl:"\\",br:"/"}},"state-start":{unicode:{tl:"\u25CF",tr:"\u25CF",bl:"\u25CF",br:"\u25CF"},ascii:{tl:"*",tr:"*",bl:"*",br:"*"}},"state-end":{unicode:{tl:"\u25C9",tr:"\u25C9",bl:"\u25C9",br:"\u25C9"},ascii:{tl:"@",tr:"@",bl:"@",br:"@"}}};function getCorners(shape,useAscii){let corners=SHAPE_CORNERS[shape]??SHAPE_CORNERS.rectangle;return useAscii?corners.ascii:corners.unicode}function getBoxDimensions(label,options){let lines=splitLines(label),maxLineWidth2=Math.max(...lines.map(l=>l.length),0),lineCount2=lines.length,innerWidth=2*options.padding+maxLineWidth2,width=innerWidth+2,rawInnerHeight=lineCount2+2*options.padding,innerHeight=rawInnerHeight%2===0?rawInnerHeight+1:rawInnerHeight,height=innerHeight+2;return{width,height,labelArea:{x:1+options.padding,y:1+options.padding,width:maxLineWidth2,height:lineCount2},gridColumns:[1,innerWidth,1],gridRows:[1,innerHeight,1]}}function renderBox(label,dimensions,corners,useAscii){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),from={x:0,y:0},to={x:width-1,y:height-1},hLine=useAscii?"-":"\u2500",vLine=useAscii?"|":"\u2502";for(let x=from.x+1;x<to.x;x++)canvas[x][from.y]=hLine,canvas[x][to.y]=hLine;for(let y=from.y+1;y<to.y;y++)canvas[from.x][y]=vLine,canvas[to.x][y]=vLine;canvas[from.x][from.y]=corners.tl,canvas[to.x][from.y]=corners.tr,canvas[from.x][to.y]=corners.bl,canvas[to.x][to.y]=corners.br;let lines=splitLines(label),w=width-1,h=height-1,startY=Math.floor(h/2)-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let line=lines[i],textX=Math.floor(w/2)-Math.ceil(line.length/2)+1;for(let j=0;j<line.length;j++){let x=textX+j,y=startY+i;x>=0&&x<canvas.length&&y>=0&&y<canvas[0].length&&(canvas[x][y]=line[j])}}return canvas}function getBoxAttachmentPoint(dir,dimensions,baseCoord){let{width,height}=dimensions,centerX=baseCoord.x+Math.floor(width/2),centerY=baseCoord.y+Math.floor(height/2);return dirEquals(dir,Up)?{x:centerX,y:baseCoord.y}:dirEquals(dir,Down)?{x:centerX,y:baseCoord.y+height-1}:dirEquals(dir,Left)?{x:baseCoord.x,y:centerY}:dirEquals(dir,Right)?{x:baseCoord.x+width-1,y:centerY}:dirEquals(dir,UpperLeft)?{x:baseCoord.x,y:baseCoord.y}:dirEquals(dir,UpperRight)?{x:baseCoord.x+width-1,y:baseCoord.y}:dirEquals(dir,LowerLeft)?{x:baseCoord.x,y:baseCoord.y+height-1}:dirEquals(dir,LowerRight)?{x:baseCoord.x+width-1,y:baseCoord.y+height-1}:{x:centerX,y:centerY}}var rectangleRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("rectangle",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var diamondRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("diamond",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var circleRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("circle",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var stateStartRenderer={getDimensions(_label,_options){return{width:5,height:3,labelArea:{x:2,y:1,width:1,height:1},gridColumns:[1,3,1],gridRows:[1,1,1]}},render(_label,dimensions,options){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),centerX=Math.floor(width/2);return options.useAscii?(canvas[0][0]=".",canvas[1][0]="-",canvas[2][0]="-",canvas[3][0]="-",canvas[4][0]=".",canvas[0][1]="|",canvas[centerX][1]="*",canvas[4][1]="|",canvas[0][2]="'",canvas[1][2]="-",canvas[2][2]="-",canvas[3][2]="-",canvas[4][2]="'"):(canvas[0][0]="\u256D",canvas[1][0]="\u2500",canvas[2][0]="\u2500",canvas[3][0]="\u2500",canvas[4][0]="\u256E",canvas[0][1]="\u2502",canvas[centerX][1]="\u25CF",canvas[4][1]="\u2502",canvas[0][2]="\u2570",canvas[1][2]="\u2500",canvas[2][2]="\u2500",canvas[3][2]="\u2500",canvas[4][2]="\u256F"),canvas},getAttachmentPoint(dir,dimensions,baseCoord){let{width,height}=dimensions,centerX=baseCoord.x+Math.floor(width/2),centerY=baseCoord.y+Math.floor(height/2);return dirEquals(dir,Up)?{x:centerX,y:baseCoord.y}:dirEquals(dir,Down)?{x:centerX,y:baseCoord.y+height-1}:dirEquals(dir,Left)?{x:baseCoord.x,y:centerY}:dirEquals(dir,Right)?{x:baseCoord.x+width-1,y:centerY}:{x:centerX,y:centerY}}},stateEndRenderer={getDimensions(_label,_options){return{width:5,height:3,labelArea:{x:2,y:1,width:1,height:1},gridColumns:[1,3,1],gridRows:[1,1,1]}},render(_label,dimensions,options){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),centerX=Math.floor(width/2);return options.useAscii?(canvas[0][0]="#",canvas[1][0]="=",canvas[2][0]="=",canvas[3][0]="=",canvas[4][0]="#",canvas[0][1]="#",canvas[centerX][1]="*",canvas[4][1]="#",canvas[0][2]="#",canvas[1][2]="=",canvas[2][2]="=",canvas[3][2]="=",canvas[4][2]="#"):(canvas[0][0]="\u2554",canvas[1][0]="\u2550",canvas[2][0]="\u2550",canvas[3][0]="\u2550",canvas[4][0]="\u2557",canvas[0][1]="\u2551",canvas[centerX][1]="\u25CE",canvas[4][1]="\u2551",canvas[0][2]="\u255A",canvas[1][2]="\u2550",canvas[2][2]="\u2550",canvas[3][2]="\u2550",canvas[4][2]="\u255D"),canvas},getAttachmentPoint(dir,dimensions,baseCoord){let{width,height}=dimensions,centerX=baseCoord.x+Math.floor(width/2),centerY=baseCoord.y+Math.floor(height/2);return dirEquals(dir,Up)?{x:centerX,y:baseCoord.y}:dirEquals(dir,Down)?{x:centerX,y:baseCoord.y+height-1}:dirEquals(dir,Left)?{x:baseCoord.x,y:centerY}:dirEquals(dir,Right)?{x:baseCoord.x+width-1,y:centerY}:{x:centerX,y:centerY}}};var roundedRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("rounded",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var stadiumRenderer={getDimensions(label,options){let lines=splitLines(label),maxLineWidth2=Math.max(...lines.map(l=>l.length),0),lineCount2=lines.length,innerWidth=2*options.padding+maxLineWidth2,width=innerWidth+4,innerHeight=lineCount2+2*options.padding,height=Math.max(innerHeight+2,3);return{width,height,labelArea:{x:2+options.padding,y:1+options.padding,width:maxLineWidth2,height:lineCount2},gridColumns:[2,innerWidth,2],gridRows:[1,innerHeight,1]}},render(label,dimensions,options){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),centerY=Math.floor(height/2),hChar=options.useAscii?"-":"\u2500";if(height===3)canvas[0][centerY]="(",canvas[width-1][centerY]=")";else if(options.useAscii){for(let y=0;y<height;y++)canvas[0][y]="(",canvas[width-1][y]=")";for(let x=1;x<width-1;x++)canvas[x][0]=hChar,canvas[x][height-1]=hChar}else{canvas[0][0]="\u256D";for(let x=1;x<width-1;x++)canvas[x][0]=hChar;canvas[width-1][0]="\u256E";for(let y=1;y<height-1;y++)canvas[0][y]="\u2502",canvas[width-1][y]="\u2502";canvas[0][height-1]="\u2570";for(let x=1;x<width-1;x++)canvas[x][height-1]=hChar;canvas[width-1][height-1]="\u256F"}let lines=splitLines(label),startY=centerY-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let line=lines[i],textX=Math.floor(width/2)-Math.floor(line.length/2);for(let j=0;j<line.length;j++){let x=textX+j,y=startY+i;x>0&&x<width-1&&y>=0&&y<height&&(canvas[x][y]=line[j])}}return canvas},getAttachmentPoint:getBoxAttachmentPoint};var hexagonRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("hexagon",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var subroutineRenderer={getDimensions(label,options){let lines=splitLines(label),maxLineWidth2=Math.max(...lines.map(l=>l.length),0),lineCount2=lines.length,innerWidth=2*options.padding+maxLineWidth2,width=innerWidth+4,innerHeight=lineCount2+2*options.padding,height=innerHeight+2;return{width,height,labelArea:{x:2+options.padding,y:1+options.padding,width:maxLineWidth2,height:lineCount2},gridColumns:[2,innerWidth,2],gridRows:[1,innerHeight,1]}},render(label,dimensions,options){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),hChar=options.useAscii?"-":"\u2500",vChar=options.useAscii?"|":"\u2502";canvas[0][0]=options.useAscii?"+":"\u250C",canvas[1][0]=options.useAscii?"+":"\u252C";for(let x=2;x<width-2;x++)canvas[x][0]=hChar;canvas[width-2][0]=options.useAscii?"+":"\u252C",canvas[width-1][0]=options.useAscii?"+":"\u2510";for(let y=1;y<height-1;y++)canvas[0][y]=vChar,canvas[1][y]=vChar,canvas[width-2][y]=vChar,canvas[width-1][y]=vChar;canvas[0][height-1]=options.useAscii?"+":"\u2514",canvas[1][height-1]=options.useAscii?"+":"\u2534";for(let x=2;x<width-2;x++)canvas[x][height-1]=hChar;canvas[width-2][height-1]=options.useAscii?"+":"\u2534",canvas[width-1][height-1]=options.useAscii?"+":"\u2518";let lines=splitLines(label),startY=Math.floor(height/2)-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let line=lines[i],textX=Math.floor(width/2)-Math.floor(line.length/2);for(let j=0;j<line.length;j++){let x=textX+j,y=startY+i;x>1&&x<width-2&&y>0&&y<height-1&&(canvas[x][y]=line[j])}}return canvas},getAttachmentPoint:getBoxAttachmentPoint},doublecircleRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("doublecircle",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint},cylinderRenderer={getDimensions(label,options){let lines=splitLines(label),maxLineWidth2=Math.max(...lines.map(l=>l.length),0),lineCount2=lines.length,innerWidth=2*options.padding+maxLineWidth2,width=innerWidth+2,innerHeight=lineCount2+2*options.padding+2,height=innerHeight+2;return{width,height,labelArea:{x:1+options.padding,y:2+options.padding,width:maxLineWidth2,height:lineCount2},gridColumns:[1,innerWidth,1],gridRows:[2,innerHeight-2,2]}},render(label,dimensions,options){let{width,height}=dimensions,canvas=mkCanvas(width-1,height-1),hChar=options.useAscii?"-":"\u2500",vChar=options.useAscii?"|":"\u2502";canvas[0][0]=options.useAscii?".":"\u256D";for(let x=1;x<width-1;x++)canvas[x][0]=hChar;canvas[width-1][0]=options.useAscii?".":"\u256E",canvas[0][1]=vChar;for(let x=1;x<width-1;x++)canvas[x][1]=hChar;canvas[width-1][1]=vChar;for(let y=2;y<height-2;y++)canvas[0][y]=vChar,canvas[width-1][y]=vChar;canvas[0][height-2]=vChar;for(let x=1;x<width-1;x++)canvas[x][height-2]=hChar;canvas[width-1][height-2]=vChar,canvas[0][height-1]=options.useAscii?"'":"\u2570";for(let x=1;x<width-1;x++)canvas[x][height-1]=hChar;canvas[width-1][height-1]=options.useAscii?"'":"\u256F";let lines=splitLines(label),startY=Math.floor(height/2)-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let line=lines[i],textX=Math.floor(width/2)-Math.floor(line.length/2);for(let j=0;j<line.length;j++){let x=textX+j,y=startY+i;x>0&&x<width-1&&y>1&&y<height-2&&(canvas[x][y]=line[j])}}return canvas},getAttachmentPoint:getBoxAttachmentPoint},asymmetricRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("asymmetric",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint},trapezoidRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("trapezoid",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint},trapezoidAltRenderer={getDimensions:getBoxDimensions,render(label,dimensions,options){let corners=getCorners("trapezoid-alt",options.useAscii);return renderBox(label,dimensions,corners,options.useAscii)},getAttachmentPoint:getBoxAttachmentPoint};var shapeRegistry=new Map([["rectangle",rectangleRenderer],["rounded",roundedRenderer],["diamond",diamondRenderer],["stadium",stadiumRenderer],["circle",circleRenderer],["subroutine",subroutineRenderer],["doublecircle",doublecircleRenderer],["hexagon",hexagonRenderer],["cylinder",cylinderRenderer],["asymmetric",asymmetricRenderer],["trapezoid",trapezoidRenderer],["trapezoid-alt",trapezoidAltRenderer],["state-start",stateStartRenderer],["state-end",stateEndRenderer]]);function getShapeRenderer(shape){return shapeRegistry.get(shape)??rectangleRenderer}function getShapeDimensions(shape,label,options){return getShapeRenderer(shape).getDimensions(label,options)}function getShapeAttachmentPoint(shape,dir,dimensions,baseCoord){return getShapeRenderer(shape).getAttachmentPoint(dir,dimensions,baseCoord)}function drawNode(node,graph){return drawBoxWithGridDimensions(node,graph)}function drawBoxWithGridDimensions(node,graph){let gc=node.gridCoord,useAscii=graph.config.useAscii,w=0;for(let i=0;i<2;i++)w+=graph.columnWidth.get(gc.x+i)??0;let h=0;for(let i=0;i<2;i++)h+=graph.rowHeight.get(gc.y+i)??0;let from={x:0,y:0},to={x:w,y:h},box=mkCanvas(Math.max(from.x,to.x),Math.max(from.y,to.y)),corners=getCorners(node.shape,useAscii),isDoubleBox=node.shape==="state-end",hChar=useAscii?isDoubleBox?"=":"-":isDoubleBox?"\u2550":"\u2500",vChar=useAscii?isDoubleBox?"\u2016":"|":isDoubleBox?"\u2551":"\u2502",effectiveCorners=isDoubleBox?useAscii?{tl:"#",tr:"#",bl:"#",br:"#"}:{tl:"\u2554",tr:"\u2557",bl:"\u255A",br:"\u255D"}:corners;for(let x=from.x+1;x<to.x;x++)box[x][from.y]=hChar;for(let x=from.x+1;x<to.x;x++)box[x][to.y]=hChar;for(let y=from.y+1;y<to.y;y++)box[from.x][y]=vChar;for(let y=from.y+1;y<to.y;y++)box[to.x][y]=vChar;box[from.x][from.y]=effectiveCorners.tl,box[to.x][from.y]=effectiveCorners.tr,box[from.x][to.y]=effectiveCorners.bl,box[to.x][to.y]=effectiveCorners.br;let label=node.displayLabel,lines=splitLines(label),startY=from.y+Math.floor(h/2)-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let line=lines[i],textX=from.x+Math.floor(w/2)-Math.ceil(line.length/2)+1;for(let j=0;j<line.length;j++)textX+j>=0&&textX+j<box.length&&startY+i>=0&&startY+i<box[0].length&&(box[textX+j][startY+i]=line[j])}return box}function drawBox(node,graph){return drawNode(node,graph)}function drawMultiBox(sections,useAscii,padding=1){let maxTextWidth=0;for(let section of sections)for(let line of section)maxTextWidth=Math.max(maxTextWidth,line.length);let boxWidth=maxTextWidth+2*padding+2,totalLines=0;for(let section of sections)totalLines+=Math.max(section.length,1);let numDividers=sections.length-1,boxHeight=totalLines+numDividers+2,hLine=useAscii?"-":"\u2500",vLine=useAscii?"|":"\u2502",tl=useAscii?"+":"\u250C",tr=useAscii?"+":"\u2510",bl=useAscii?"+":"\u2514",br=useAscii?"+":"\u2518",divL=useAscii?"+":"\u251C",divR=useAscii?"+":"\u2524",canvas=mkCanvas(boxWidth-1,boxHeight-1);canvas[0][0]=tl;for(let x=1;x<boxWidth-1;x++)canvas[x][0]=hLine;canvas[boxWidth-1][0]=tr,canvas[0][boxHeight-1]=bl;for(let x=1;x<boxWidth-1;x++)canvas[x][boxHeight-1]=hLine;canvas[boxWidth-1][boxHeight-1]=br;for(let y=1;y<boxHeight-1;y++)canvas[0][y]=vLine,canvas[boxWidth-1][y]=vLine;let row=1;for(let s=0;s<sections.length;s++){let section=sections[s],lines=section.length>0?section:[""];for(let line of lines){let startX=1+padding;for(let i=0;i<line.length;i++)canvas[startX+i][row]=line[i];row++}if(s<sections.length-1){canvas[0][row]=divL;for(let x=1;x<boxWidth-1;x++)canvas[x][row]=hLine;canvas[boxWidth-1][row]=divR,row++}}return canvas}var LINE_CHARS={solid:{h:{unicode:"\u2500",ascii:"-"},v:{unicode:"\u2502",ascii:"|"}},dotted:{h:{unicode:"\u2504",ascii:"."},v:{unicode:"\u2506",ascii:":"}},thick:{h:{unicode:"\u2501",ascii:"="},v:{unicode:"\u2503",ascii:"\u2016"}}};function drawLine(canvas,from,to,offsetFrom,offsetTo,useAscii,style="solid"){let dir=determineDirection(from,to),drawnCoords=[],chars=LINE_CHARS[style],hChar=useAscii?chars.h.ascii:chars.h.unicode,vChar=useAscii?chars.v.ascii:chars.v.unicode;if(dirEquals(dir,Up))for(let y=from.y-offsetFrom;y>=to.y-offsetTo;y--)drawnCoords.push({x:from.x,y}),canvas[from.x][y]=vChar;else if(dirEquals(dir,Down))for(let y=from.y+offsetFrom;y<=to.y+offsetTo;y++)drawnCoords.push({x:from.x,y}),canvas[from.x][y]=vChar;else if(dirEquals(dir,Left))for(let x=from.x-offsetFrom;x>=to.x-offsetTo;x--)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;else if(dirEquals(dir,Right))for(let x=from.x+offsetFrom;x<=to.x+offsetTo;x++)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;else if(dirEquals(dir,UpperLeft)){for(let x=from.x-offsetFrom;x>=to.x;x--)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;for(let y=from.y-1;y>=to.y-offsetTo;y--)drawnCoords.push({x:to.x,y}),canvas[to.x][y]=vChar}else if(dirEquals(dir,UpperRight)){for(let x=from.x+offsetFrom;x<=to.x;x++)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;for(let y=from.y-1;y>=to.y-offsetTo;y--)drawnCoords.push({x:to.x,y}),canvas[to.x][y]=vChar}else if(dirEquals(dir,LowerLeft)){for(let x=from.x-offsetFrom;x>=to.x;x--)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;for(let y=from.y+1;y<=to.y+offsetTo;y++)drawnCoords.push({x:to.x,y}),canvas[to.x][y]=vChar}else if(dirEquals(dir,LowerRight))if(to.x-from.x<=1)for(let y=from.y+offsetFrom;y<=to.y+offsetTo;y++)drawnCoords.push({x:from.x,y}),canvas[from.x][y]=vChar;else{for(let x=from.x+offsetFrom;x<=to.x;x++)drawnCoords.push({x,y:from.y}),canvas[x][from.y]=hChar;for(let y=from.y+1;y<=to.y+offsetTo;y++)drawnCoords.push({x:to.x,y}),canvas[to.x][y]=vChar}return drawnCoords}function drawArrow(graph,edge){if(edge.path.length===0){let empty=copyCanvas(graph.canvas);return[empty,empty,empty,empty,empty,empty]}let labelCanvas=drawArrowLabel(graph,edge),[pathCanvas,linesDrawn,lineDirs]=drawPath(graph,edge.path,edge.style),boxStartCanvas=drawBoxStart(graph,edge.path,linesDrawn[0],edge.from.shape),arrowHeadEndCanvas;edge.hasArrowEnd?arrowHeadEndCanvas=drawArrowHead(graph,linesDrawn[linesDrawn.length-1],lineDirs[lineDirs.length-1]):arrowHeadEndCanvas=copyCanvas(graph.canvas);let arrowHeadStartCanvas;if(edge.hasArrowStart&&linesDrawn.length>0){let firstPoint=linesDrawn[0][0],startDir=reverseDirection(lineDirs[0]),arrowPos={x:firstPoint.x,y:firstPoint.y};dirEquals(lineDirs[0],Right)?arrowPos.x=firstPoint.x-1:dirEquals(lineDirs[0],Left)?arrowPos.x=firstPoint.x+1:dirEquals(lineDirs[0],Down)?arrowPos.y=firstPoint.y-1:dirEquals(lineDirs[0],Up)&&(arrowPos.y=firstPoint.y+1),arrowHeadStartCanvas=drawArrowHead(graph,[firstPoint,arrowPos],startDir)}else arrowHeadStartCanvas=copyCanvas(graph.canvas);let cornersCanvas=drawCorners(graph,edge.path);return[pathCanvas,boxStartCanvas,arrowHeadEndCanvas,arrowHeadStartCanvas,cornersCanvas,labelCanvas]}function reverseDirection(dir){return dirEquals(dir,Up)?Down:dirEquals(dir,Down)?Up:dirEquals(dir,Left)?Right:dirEquals(dir,Right)?Left:dirEquals(dir,UpperLeft)?LowerRight:dirEquals(dir,UpperRight)?LowerLeft:dirEquals(dir,LowerLeft)?UpperRight:dirEquals(dir,LowerRight)?UpperLeft:Middle}function drawPath(graph,path,style="solid"){let canvas=copyCanvas(graph.canvas),previousCoord=path[0],linesDrawn=[],lineDirs=[];for(let i=1;i<path.length;i++){let nextCoord=path[i],prevDC=gridToDrawingCoord(graph,previousCoord),nextDC=gridToDrawingCoord(graph,nextCoord);if(drawingCoordEquals(prevDC,nextDC)){previousCoord=nextCoord;continue}let dir=determineDirection(previousCoord,nextCoord),segment=drawLine(canvas,prevDC,nextDC,1,-1,graph.config.useAscii,style);segment.length===0&&segment.push(prevDC),linesDrawn.push(segment),lineDirs.push(dir),previousCoord=nextCoord}return[canvas,linesDrawn,lineDirs]}function drawBoxStart(graph,path,firstLine,sourceShape){let canvas=copyCanvas(graph.canvas);if(graph.config.useAscii||sourceShape==="state-start"||sourceShape==="state-end")return canvas;let from=firstLine[0],dir=determineDirection(path[0],path[1]);return dirEquals(dir,Up)?canvas[from.x][from.y+1]="\u2534":dirEquals(dir,Down)?canvas[from.x][from.y-1]="\u252C":dirEquals(dir,Left)?canvas[from.x+1][from.y]="\u2524":dirEquals(dir,Right)&&(canvas[from.x-1][from.y]="\u251C"),canvas}function drawArrowHead(graph,lastLine,fallbackDir){let canvas=copyCanvas(graph.canvas);if(lastLine.length===0)return canvas;let from=lastLine[0],lastPos=lastLine[lastLine.length-1],dir=determineDirection(from,lastPos);(lastLine.length===1||dirEquals(dir,Middle))&&(dir=fallbackDir);let char;return graph.config.useAscii?dirEquals(dir,Up)?char="^":dirEquals(dir,Down)?char="v":dirEquals(dir,Left)?char="<":dirEquals(dir,Right)?char=">":dirEquals(fallbackDir,Up)?char="^":dirEquals(fallbackDir,Down)?char="v":dirEquals(fallbackDir,Left)?char="<":dirEquals(fallbackDir,Right)?char=">":char="*":dirEquals(dir,Up)?char="\u25B2":dirEquals(dir,Down)?char="\u25BC":dirEquals(dir,Left)?char="\u25C4":dirEquals(dir,Right)?char="\u25BA":dirEquals(dir,UpperRight)?char="\u25E5":dirEquals(dir,UpperLeft)?char="\u25E4":dirEquals(dir,LowerRight)?char="\u25E2":dirEquals(dir,LowerLeft)?char="\u25E3":dirEquals(fallbackDir,Up)?char="\u25B2":dirEquals(fallbackDir,Down)?char="\u25BC":dirEquals(fallbackDir,Left)?char="\u25C4":dirEquals(fallbackDir,Right)?char="\u25BA":dirEquals(fallbackDir,UpperRight)?char="\u25E5":dirEquals(fallbackDir,UpperLeft)?char="\u25E4":dirEquals(fallbackDir,LowerRight)?char="\u25E2":dirEquals(fallbackDir,LowerLeft)?char="\u25E3":char="\u25CF",canvas[lastPos.x][lastPos.y]=char,canvas}function drawCorners(graph,path){let canvas=copyCanvas(graph.canvas);for(let idx=1;idx<path.length-1;idx++){let coord=path[idx],dc=gridToDrawingCoord(graph,coord),prevDir=determineDirection(path[idx-1],coord),nextDir=determineDirection(coord,path[idx+1]),corner;graph.config.useAscii?corner="+":dirEquals(prevDir,Right)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Left)?corner="\u2510":dirEquals(prevDir,Right)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Left)?corner="\u2518":dirEquals(prevDir,Left)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Right)?corner="\u250C":dirEquals(prevDir,Left)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Right)?corner="\u2514":corner="+",canvas[dc.x][dc.y]=corner}return canvas}function drawArrowLabel(graph,edge){let canvas=copyCanvas(graph.canvas);if(edge.text.length===0)return canvas;let drawingLine=lineToDrawing(graph,edge.labelLine),isUpwardEdge;if(edge.path.length>=2){let startY=edge.path[0].y,endY=edge.path[edge.path.length-1].y;endY<startY?isUpwardEdge=!0:endY>startY&&(isUpwardEdge=!1)}return drawTextOnLine(canvas,drawingLine,edge.text,isUpwardEdge),canvas}function drawTextOnLine(canvas,line,label,isUpwardEdge){if(line.length<2)return;let minX=Math.min(line[0].x,line[1].x),maxX=Math.max(line[0].x,line[1].x),minY=Math.min(line[0].y,line[1].y),maxY=Math.max(line[0].y,line[1].y),middleX=minX+Math.floor((maxX-minX)/2),middleY=minY+Math.floor((maxY-minY)/2);if(isUpwardEdge!==void 0&&minX===maxX){let segmentHeight=maxY-minY,offset=Math.max(1,Math.floor(segmentHeight/4));isUpwardEdge?middleY=middleY+offset:middleY=middleY-offset}let lines=splitLines(label),startY=middleY-Math.floor((lines.length-1)/2);for(let i=0;i<lines.length;i++){let lineText=lines[i],startX=middleX-Math.floor(lineText.length/2);drawText(canvas,{x:startX,y:startY+i},lineText)}}function getNodeAttachmentPoint(graph,node,dir){let gc=node.gridCoord,w=0;for(let i=0;i<2;i++)w+=graph.columnWidth.get(gc.x+i)??0;let h=0;for(let i=0;i<2;i++)h+=graph.rowHeight.get(gc.y+i)??0;let gridDimensions={width:w+1,height:h+1,labelArea:{x:0,y:0,width:0,height:0},gridColumns:[0,0,0],gridRows:[0,0,0]},baseCoord=node.drawingCoord;return getShapeAttachmentPoint(node.shape,dir,gridDimensions,baseCoord)}function drawBundledEdgeSegment(graph,edge,bundle){let empty=copyCanvas(graph.canvas);if(!edge.pathToJunction||edge.pathToJunction.length===0)return[empty,empty,empty,empty,empty,empty];let pathCanvas=copyCanvas(graph.canvas),useAscii=graph.config.useAscii,drawingPath=edge.pathToJunction.map((gc,idx)=>bundle.type==="fan-in"&&idx===0?getNodeAttachmentPoint(graph,edge.from,edge.startDir):bundle.type==="fan-out"&&idx===edge.pathToJunction.length-1?getNodeAttachmentPoint(graph,edge.to,edge.endDir):gridToDrawingCoord(graph,gc));for(let i=1;i<drawingPath.length;i++){let from=drawingPath[i-1],to=drawingPath[i];drawingCoordEquals(from,to)||drawLine(pathCanvas,from,to,1,-1,useAscii,edge.style)}let cornersCanvas=copyCanvas(graph.canvas);for(let idx=1;idx<edge.pathToJunction.length-1;idx++){let coord=edge.pathToJunction[idx],dc=gridToDrawingCoord(graph,coord),prevDir=determineDirection(edge.pathToJunction[idx-1],coord),nextDir=determineDirection(coord,edge.pathToJunction[idx+1]),corner;useAscii?corner="+":dirEquals(prevDir,Right)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Left)?corner="\u2510":dirEquals(prevDir,Right)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Left)?corner="\u2518":dirEquals(prevDir,Left)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Right)?corner="\u250C":dirEquals(prevDir,Left)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Right)?corner="\u2514":corner="+",cornersCanvas[dc.x][dc.y]=corner}let boxStartCanvas=copyCanvas(graph.canvas);if(bundle.type==="fan-in"&&edge.pathToJunction.length>=2){let firstPoint=drawingPath[0],dir=determineDirection(edge.pathToJunction[0],edge.pathToJunction[1]);useAscii||(dirEquals(dir,Up)?boxStartCanvas[firstPoint.x][firstPoint.y]="\u2534":dirEquals(dir,Down)?boxStartCanvas[firstPoint.x][firstPoint.y]="\u252C":dirEquals(dir,Left)?boxStartCanvas[firstPoint.x][firstPoint.y]="\u2524":dirEquals(dir,Right)&&(boxStartCanvas[firstPoint.x][firstPoint.y]="\u251C"))}let labelCanvas=copyCanvas(graph.canvas);return[pathCanvas,boxStartCanvas,empty,empty,cornersCanvas,labelCanvas]}function drawBundleSharedPath(graph,bundle){let pathCanvas=copyCanvas(graph.canvas),cornersCanvas=copyCanvas(graph.canvas);if(bundle.sharedPath.length<2)return[pathCanvas,cornersCanvas];let useAscii=graph.config.useAscii,style=bundle.edges[0]?.style??"solid",graphDir=graph.config.graphDirection,drawingPath=bundle.sharedPath.map((gc,idx)=>{if(bundle.type==="fan-in"&&idx===bundle.sharedPath.length-1){let entryDir=graphDir==="TD"?Up:Left;return getNodeAttachmentPoint(graph,bundle.sharedNode,entryDir)}if(bundle.type==="fan-out"&&idx===0){let exitDir=graphDir==="TD"?Down:Right;return getNodeAttachmentPoint(graph,bundle.sharedNode,exitDir)}return gridToDrawingCoord(graph,gc)});for(let i=1;i<drawingPath.length;i++){let from=drawingPath[i-1],to=drawingPath[i];drawingCoordEquals(from,to)||drawLine(pathCanvas,from,to,1,-1,useAscii,style)}for(let idx=1;idx<bundle.sharedPath.length-1;idx++){let coord=bundle.sharedPath[idx],dc=gridToDrawingCoord(graph,coord),prevDir=determineDirection(bundle.sharedPath[idx-1],coord),nextDir=determineDirection(coord,bundle.sharedPath[idx+1]),corner;useAscii?corner="+":dirEquals(prevDir,Right)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Left)?corner="\u2510":dirEquals(prevDir,Right)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Left)?corner="\u2518":dirEquals(prevDir,Left)&&dirEquals(nextDir,Down)||dirEquals(prevDir,Up)&&dirEquals(nextDir,Right)?corner="\u250C":dirEquals(prevDir,Left)&&dirEquals(nextDir,Up)||dirEquals(prevDir,Down)&&dirEquals(nextDir,Right)?corner="\u2514":corner="+",cornersCanvas[dc.x][dc.y]=corner}return[pathCanvas,cornersCanvas]}function drawBundleArrowhead(graph,bundle){let canvas=copyCanvas(graph.canvas);if(bundle.sharedPath.length<2)return canvas;let lastIdx=bundle.sharedPath.length-1,secondLast=bundle.sharedPath[lastIdx-1],last=bundle.sharedPath[lastIdx],dir=determineDirection(secondLast,last),graphDir=graph.config.graphDirection,entryDir=graphDir==="TD"?Up:Left,dc=getNodeAttachmentPoint(graph,bundle.sharedNode,entryDir);graphDir==="TD"?dc.y-=1:dc.x-=1;let char;return graph.config.useAscii?dirEquals(dir,Up)?char="^":dirEquals(dir,Down)?char="v":dirEquals(dir,Left)?char="<":dirEquals(dir,Right)?char=">":char="v":dirEquals(dir,Up)?char="\u25B2":dirEquals(dir,Down)?char="\u25BC":dirEquals(dir,Left)?char="\u25C4":dirEquals(dir,Right)?char="\u25BA":char="\u25BC",canvas[dc.x][dc.y]=char,canvas}function drawBundledEdgeArrowhead(graph,edge){let canvas=copyCanvas(graph.canvas);if(!edge.pathToJunction||edge.pathToJunction.length<2)return canvas;let lastIdx=edge.pathToJunction.length-1,secondLast=edge.pathToJunction[lastIdx-1],last=edge.pathToJunction[lastIdx],dir=determineDirection(secondLast,last),graphDir=graph.config.graphDirection,entryDir=graphDir==="TD"?Up:Left,dc=getNodeAttachmentPoint(graph,edge.to,entryDir);graphDir==="TD"?dc.y-=1:dc.x-=1;let char;return graph.config.useAscii?dirEquals(dir,Up)?char="^":dirEquals(dir,Down)?char="v":dirEquals(dir,Left)?char="<":dirEquals(dir,Right)?char=">":char="v":dirEquals(dir,Up)?char="\u25B2":dirEquals(dir,Down)?char="\u25BC":dirEquals(dir,Left)?char="\u25C4":dirEquals(dir,Right)?char="\u25BA":char="\u25BC",canvas[dc.x][dc.y]=char,canvas}function drawJunctionCharacter(graph,bundle){let canvas=copyCanvas(graph.canvas);if(!bundle.junctionPoint)return canvas;let dc=gridToDrawingCoord(graph,bundle.junctionPoint),useAscii=graph.config.useAscii,hasUp=!1,hasDown=!1,hasLeft=!1,hasRight=!1;if(bundle.sharedPath.length>=2){let junctionIdx=bundle.type==="fan-in"?0:bundle.sharedPath.length-1,adjacentIdx=bundle.type==="fan-in"?1:bundle.sharedPath.length-2,sharedDir=determineDirection(bundle.sharedPath[junctionIdx],bundle.sharedPath[adjacentIdx]);dirEquals(sharedDir,Down)?hasDown=!0:dirEquals(sharedDir,Up)?hasUp=!0:dirEquals(sharedDir,Right)?hasRight=!0:dirEquals(sharedDir,Left)&&(hasLeft=!0)}for(let edge of bundle.edges)if(edge.pathToJunction&&edge.pathToJunction.length>=2){let junctionIdx=bundle.type==="fan-in"?edge.pathToJunction.length-1:0,adjacentIdx=bundle.type==="fan-in"?edge.pathToJunction.length-2:1,arrivalDir=determineDirection(edge.pathToJunction[adjacentIdx],edge.pathToJunction[junctionIdx]);dirEquals(arrivalDir,Down)?hasUp=!0:dirEquals(arrivalDir,Up)?hasDown=!0:dirEquals(arrivalDir,Right)?hasLeft=!0:dirEquals(arrivalDir,Left)&&(hasRight=!0)}let char;return useAscii?char="+":hasUp&&hasDown&&hasLeft&&hasRight?char="\u253C":hasDown&&hasLeft&&hasRight&&!hasUp?char="\u252C":hasUp&&hasLeft&&hasRight&&!hasDown?char="\u2534":hasUp&&hasDown&&hasRight&&!hasLeft?char="\u251C":hasUp&&hasDown&&hasLeft&&!hasRight?char="\u2524":hasLeft&&hasRight?char="\u2500":hasUp&&hasDown?char="\u2502":hasDown&&hasRight?char="\u250C":hasDown&&hasLeft?char="\u2510":hasUp&&hasRight?char="\u2514":hasUp&&hasLeft?char="\u2518":char="\u253C",canvas[dc.x][dc.y]=char,canvas}function drawSubgraphBox(sg,graph){let width=sg.maxX-sg.minX,height=sg.maxY-sg.minY;if(width<=0||height<=0)return mkCanvas(0,0);let from={x:0,y:0},to={x:width,y:height},canvas=mkCanvas(width,height);if(graph.config.useAscii){for(let x=from.x+1;x<to.x;x++)canvas[x][from.y]="-";for(let x=from.x+1;x<to.x;x++)canvas[x][to.y]="-";for(let y=from.y+1;y<to.y;y++)canvas[from.x][y]="|";for(let y=from.y+1;y<to.y;y++)canvas[to.x][y]="|";canvas[from.x][from.y]="+",canvas[to.x][from.y]="+",canvas[from.x][to.y]="+",canvas[to.x][to.y]="+"}else{for(let x=from.x+1;x<to.x;x++)canvas[x][from.y]="\u2500";for(let x=from.x+1;x<to.x;x++)canvas[x][to.y]="\u2500";for(let y=from.y+1;y<to.y;y++)canvas[from.x][y]="\u2502";for(let y=from.y+1;y<to.y;y++)canvas[to.x][y]="\u2502";canvas[from.x][from.y]="\u250C",canvas[to.x][from.y]="\u2510",canvas[from.x][to.y]="\u2514",canvas[to.x][to.y]="\u2518"}return canvas}function drawSubgraphLabel(sg,graph){let width=sg.maxX-sg.minX,height=sg.maxY-sg.minY;if(width<=0||height<=0)return[mkCanvas(0,0),{x:0,y:0}];let canvas=mkCanvas(width,height),lines=splitLines(sg.name);for(let i=0;i<lines.length;i++){let line=lines[i],labelY=1+i,labelX=Math.floor(width/2)-Math.floor(line.length/2);labelX<1&&(labelX=1);for(let j=0;j<line.length;j++)labelX+j<width&&labelY<height&&(canvas[labelX+j][labelY]=line[j])}return[canvas,{x:sg.minX,y:sg.minY}]}function sortSubgraphsByDepth(subgraphs){function getDepth(sg){return sg.parent===null?0:1+getDepth(sg.parent)}let sorted=[...subgraphs];return sorted.sort((a,b)=>getDepth(a)-getDepth(b)),sorted}function fillRolesFromCanvas(roleCanvas,canvas,offset,role){for(let x=0;x<canvas.length;x++)for(let y=0;y<(canvas[0]?.length??0);y++){let char=canvas[x]?.[y];if(char&&char!==" "){let rx=x+offset.x,ry=y+offset.y;rx>=0&&ry>=0&&setRole(roleCanvas,rx,ry,role)}}}function fillRolesFromCanvases(roleCanvas,canvases,offset,role){for(let canvas of canvases)fillRolesFromCanvas(roleCanvas,canvas,offset,role)}function fillRolesForNodeBox(roleCanvas,canvas,offset){let isBorderChar=c=>/^[┌┐└┘├┤┬┴┼│─╭╮╰╯+\-|.':]$/.test(c);for(let x=0;x<canvas.length;x++)for(let y=0;y<(canvas[0]?.length??0);y++){let char=canvas[x]?.[y];if(char&&char!==" "){let rx=x+offset.x,ry=y+offset.y;rx>=0&&ry>=0&&setRole(roleCanvas,rx,ry,isBorderChar(char)?"border":"text")}}}function drawGraph(graph){let useAscii=graph.config.useAscii,zero={x:0,y:0},sortedSgs=sortSubgraphsByDepth(graph.subgraphs);for(let sg of sortedSgs){let sgCanvas=drawSubgraphBox(sg,graph),offset={x:sg.minX,y:sg.minY};graph.canvas=mergeCanvases(graph.canvas,offset,useAscii,sgCanvas),fillRolesFromCanvas(graph.roleCanvas,sgCanvas,offset,"border")}for(let node of graph.nodes)!node.drawn&&node.drawingCoord&&node.drawing&&(graph.canvas=mergeCanvases(graph.canvas,node.drawingCoord,useAscii,node.drawing),fillRolesForNodeBox(graph.roleCanvas,node.drawing,node.drawingCoord),node.drawn=!0);let lineCanvases=[],cornerCanvases=[],arrowHeadEndCanvases=[],arrowHeadStartCanvases=[],boxStartCanvases=[],labelCanvases=[],junctionCanvases=[],processedBundles=new Set;for(let edge of graph.edges)if(edge.bundle&&edge.pathToJunction){let bundle=edge.bundle,[pathC,boxStartC,,,cornersC,labelC]=drawBundledEdgeSegment(graph,edge,bundle);if(lineCanvases.push(pathC),cornerCanvases.push(cornersC),boxStartCanvases.push(boxStartC),labelCanvases.push(labelC),!processedBundles.has(bundle)){processedBundles.add(bundle);let[sharedPathC,sharedCornersC]=drawBundleSharedPath(graph,bundle);if(lineCanvases.push(sharedPathC),cornerCanvases.push(sharedCornersC),bundle.type==="fan-in"){let arrowHeadC=drawBundleArrowhead(graph,bundle);arrowHeadEndCanvases.push(arrowHeadC)}let junctionC=drawJunctionCharacter(graph,bundle);junctionCanvases.push(junctionC)}if(bundle.type==="fan-out"&&edge.hasArrowEnd){let arrowHeadC=drawBundledEdgeArrowhead(graph,edge);arrowHeadEndCanvases.push(arrowHeadC)}}else{let[pathC,boxStartC,arrowHeadEndC,arrowHeadStartC,cornersC,labelC]=drawArrow(graph,edge);lineCanvases.push(pathC),cornerCanvases.push(cornersC),arrowHeadEndCanvases.push(arrowHeadEndC),arrowHeadStartCanvases.push(arrowHeadStartC),boxStartCanvases.push(boxStartC),labelCanvases.push(labelC)}graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...lineCanvases),fillRolesFromCanvases(graph.roleCanvas,lineCanvases,zero,"line"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...cornerCanvases),fillRolesFromCanvases(graph.roleCanvas,cornerCanvases,zero,"corner"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...junctionCanvases),fillRolesFromCanvases(graph.roleCanvas,junctionCanvases,zero,"junction"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...arrowHeadEndCanvases),fillRolesFromCanvases(graph.roleCanvas,arrowHeadEndCanvases,zero,"arrow"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...boxStartCanvases),fillRolesFromCanvases(graph.roleCanvas,boxStartCanvases,zero,"junction"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...arrowHeadStartCanvases),fillRolesFromCanvases(graph.roleCanvas,arrowHeadStartCanvases,zero,"arrow"),graph.canvas=mergeCanvases(graph.canvas,zero,useAscii,...labelCanvases);for(let label of labelCanvases)clearLabelGaps(graph.canvas,label);fillRolesFromCanvases(graph.roleCanvas,labelCanvases,zero,"text");for(let sg of graph.subgraphs){if(sg.nodes.length===0)continue;let[labelCanvas,offset]=drawSubgraphLabel(sg,graph);graph.canvas=mergeCanvases(graph.canvas,offset,useAscii,labelCanvas),fillRolesFromCanvas(graph.roleCanvas,labelCanvas,offset,"text")}return graph.canvas}function gridToDrawingCoord(graph,c,dir){let target=dir?{x:c.x+dir.x,y:c.y+dir.y}:c,x=0;for(let col=0;col<target.x;col++)x+=graph.columnWidth.get(col)??0;let y=0;for(let row=0;row<target.y;row++)y+=graph.rowHeight.get(row)??0;let colW=graph.columnWidth.get(target.x)??0,rowH=graph.rowHeight.get(target.y)??0;return{x:x+Math.floor(colW/2)+graph.offsetX,y:y+Math.floor(rowH/2)+graph.offsetY}}function lineToDrawing(graph,line){return line.map(c=>gridToDrawingCoord(graph,c))}function reserveSpotInGrid(graph,node,requested,effectiveDir){let dir=effectiveDir??getEffectiveDirection2(graph,node);if(graph.grid.has(gridKey(requested)))return dir==="LR"?reserveSpotInGrid(graph,node,{x:requested.x,y:requested.y+4},dir):reserveSpotInGrid(graph,node,{x:requested.x+4,y:requested.y},dir);for(let dx=0;dx<3;dx++)for(let dy=0;dy<3;dy++){let reserved={x:requested.x+dx,y:requested.y+dy};graph.grid.set(gridKey(reserved),node)}return node.gridCoord=requested,requested}function setColumnWidth(graph,node){let gc=node.gridCoord,padding=graph.config.boxBorderPadding,shapeDims=getShapeDimensions(node.shape,node.displayLabel,{useAscii:graph.config.useAscii,padding}),colWidths=shapeDims.gridColumns,rowHeights=shapeDims.gridRows;for(let idx=0;idx<colWidths.length;idx++){let xCoord=gc.x+idx,current=graph.columnWidth.get(xCoord)??0;graph.columnWidth.set(xCoord,Math.max(current,colWidths[idx]))}for(let idx=0;idx<rowHeights.length;idx++){let yCoord=gc.y+idx,current=graph.rowHeight.get(yCoord)??0;graph.rowHeight.set(yCoord,Math.max(current,rowHeights[idx]))}if(gc.x>0){let current=graph.columnWidth.get(gc.x-1)??0;graph.columnWidth.set(gc.x-1,Math.max(current,graph.config.paddingX))}if(gc.y>0){let basePadding=graph.config.paddingY;hasIncomingEdgeFromOutsideSubgraph(graph,node)&&(basePadding+=4);let current=graph.rowHeight.get(gc.y-1)??0;graph.rowHeight.set(gc.y-1,Math.max(current,basePadding))}}function increaseGridSizeForPath(graph,path){for(let c of path)graph.columnWidth.has(c.x)||graph.columnWidth.set(c.x,Math.floor(graph.config.paddingX/2)),graph.rowHeight.has(c.y)||graph.rowHeight.set(c.y,Math.floor(graph.config.paddingY/2))}function isNodeInAnySubgraph(graph,node){return graph.subgraphs.some(sg=>sg.nodes.includes(node))}function getNodeSubgraph(graph,node){let innermost=null;for(let sg of graph.subgraphs)sg.nodes.includes(node)&&(!innermost||isAncestorOrSelf2(innermost,sg))&&(innermost=sg);return innermost}function isAncestorOrSelf2(candidate,target){let current=target;for(;current!==null;){if(current===candidate)return!0;current=current.parent}return!1}function getEffectiveDirection2(graph,node){let sg=getNodeSubgraph(graph,node);return sg?.direction?sg.direction:graph.config.graphDirection}function hasIncomingEdgeFromOutsideSubgraph(graph,node){let nodeSg=getNodeSubgraph(graph,node);if(!nodeSg)return!1;let hasExternalEdge=!1;for(let edge of graph.edges)if(edge.to===node&&getNodeSubgraph(graph,edge.from)!==nodeSg){hasExternalEdge=!0;break}if(!hasExternalEdge)return!1;for(let otherNode of nodeSg.nodes){if(otherNode===node||!otherNode.gridCoord)continue;let otherHasExternal=!1;for(let edge of graph.edges)if(edge.to===otherNode&&getNodeSubgraph(graph,edge.from)!==nodeSg){otherHasExternal=!0;break}if(otherHasExternal&&otherNode.gridCoord.y<node.gridCoord.y)return!1}return!0}function calculateSubgraphBoundingBox(graph,sg){if(sg.nodes.length===0)return;let minX=1e6,minY=1e6,maxX=-1e6,maxY=-1e6;for(let child of sg.children)calculateSubgraphBoundingBox(graph,child),child.nodes.length>0&&(minX=Math.min(minX,child.minX),minY=Math.min(minY,child.minY),maxX=Math.max(maxX,child.maxX),maxY=Math.max(maxY,child.maxY));for(let node of sg.nodes){if(!node.drawingCoord||!node.drawing)continue;let nodeMinX=node.drawingCoord.x,nodeMinY=node.drawingCoord.y,nodeMaxX=nodeMinX+node.drawing.length-1,nodeMaxY=nodeMinY+node.drawing[0].length-1;minX=Math.min(minX,nodeMinX),minY=Math.min(minY,nodeMinY),maxX=Math.max(maxX,nodeMaxX),maxY=Math.max(maxY,nodeMaxY)}let subgraphPadding=2,subgraphLabelSpace=2;sg.minX=minX-subgraphPadding,sg.minY=minY-subgraphPadding-subgraphLabelSpace,sg.maxX=maxX+subgraphPadding,sg.maxY=maxY+subgraphPadding}function ensureSubgraphSpacing(graph){let rootSubgraphs=graph.subgraphs.filter(sg=>sg.parent===null&&sg.nodes.length>0);for(let i=0;i<rootSubgraphs.length;i++)for(let j=i+1;j<rootSubgraphs.length;j++){let sg1=rootSubgraphs[i],sg2=rootSubgraphs[j];sg1.minX<sg2.maxX&&sg1.maxX>sg2.minX&&(sg1.maxY>=sg2.minY-1&&sg1.minY<sg2.minY?sg2.minY=sg1.maxY+1+1:sg2.maxY>=sg1.minY-1&&sg2.minY<sg1.minY&&(sg1.minY=sg2.maxY+1+1)),sg1.minY<sg2.maxY&&sg1.maxY>sg2.minY&&(sg1.maxX>=sg2.minX-1&&sg1.minX<sg2.minX?sg2.minX=sg1.maxX+1+1:sg2.maxX>=sg1.minX-1&&sg2.minX<sg1.minX&&(sg1.minX=sg2.maxX+1+1))}}function calculateSubgraphBoundingBoxes(graph){for(let sg of graph.subgraphs)calculateSubgraphBoundingBox(graph,sg);ensureSubgraphSpacing(graph)}function offsetDrawingForSubgraphs(graph){if(graph.subgraphs.length===0)return;let minX=0,minY=0;for(let sg of graph.subgraphs)minX=Math.min(minX,sg.minX),minY=Math.min(minY,sg.minY);let offsetX=-minX,offsetY=-minY;if(!(offsetX===0&&offsetY===0)){graph.offsetX=offsetX,graph.offsetY=offsetY;for(let sg of graph.subgraphs)sg.minX+=offsetX,sg.minY+=offsetY,sg.maxX+=offsetX,sg.maxY+=offsetY;for(let node of graph.nodes)node.drawingCoord&&(node.drawingCoord.x+=offsetX,node.drawingCoord.y+=offsetY)}}function createMapping(graph){let dir=graph.config.graphDirection,highestPositionPerLevel=new Array(100).fill(0),nodesFound=new Set,initialRoots=[];for(let node of graph.nodes){nodesFound.has(node.name)||initialRoots.push(node),nodesFound.add(node.name);for(let child of getChildren(graph,node))nodesFound.add(child.name)}let rootNodes=initialRoots.filter(node=>{let nodeSg=getNodeSubgraph(graph,node);if(!nodeSg)return!0;for(let edge of graph.edges)if(edge.to===node&&getNodeSubgraph(graph,edge.from)!==nodeSg)return!1;return!0}),hasExternalRoots=!1,hasSubgraphRootsWithEdges=!1;for(let node of rootNodes)isNodeInAnySubgraph(graph,node)?getChildren(graph,node).length>0&&(hasSubgraphRootsWithEdges=!0):hasExternalRoots=!0;let shouldSeparate=dir==="LR"&&hasExternalRoots&&hasSubgraphRootsWithEdges,externalRootNodes,subgraphRootNodes=[];shouldSeparate?(externalRootNodes=rootNodes.filter(n=>!isNodeInAnySubgraph(graph,n)),subgraphRootNodes=rootNodes.filter(n=>isNodeInAnySubgraph(graph,n))):externalRootNodes=rootNodes;for(let node of externalRootNodes){let requested=dir==="LR"?{x:0,y:highestPositionPerLevel[0]}:{x:highestPositionPerLevel[0],y:0};reserveSpotInGrid(graph,graph.nodes[node.index],requested),highestPositionPerLevel[0]=highestPositionPerLevel[0]+4}if(shouldSeparate&&subgraphRootNodes.length>0)for(let node of subgraphRootNodes){let requested=dir==="LR"?{x:4,y:highestPositionPerLevel[4]}:{x:highestPositionPerLevel[4],y:4};reserveSpotInGrid(graph,graph.nodes[node.index],requested),highestPositionPerLevel[4]=highestPositionPerLevel[4]+4}let placedCount=externalRootNodes.length+subgraphRootNodes.length;for(;placedCount<graph.nodes.length;){let prevCount=placedCount;for(let node of graph.nodes){if(node.gridCoord===null)continue;let gc=node.gridCoord;for(let child of getChildren(graph,node)){if(child.gridCoord!==null)continue;let parentSg=getNodeSubgraph(graph,node),childSg=getNodeSubgraph(graph,child),edgeDir=parentSg&&parentSg===childSg&&parentSg.direction?parentSg.direction:graph.config.graphDirection,childLevel=edgeDir==="LR"?gc.x+4:gc.y+4,highestPosition;edgeDir!==graph.config.graphDirection?highestPosition=edgeDir==="LR"?gc.y:gc.x:highestPosition=highestPositionPerLevel[childLevel];let requested=edgeDir==="LR"?{x:childLevel,y:highestPosition}:{x:highestPosition,y:childLevel};reserveSpotInGrid(graph,graph.nodes[child.index],requested,edgeDir),edgeDir===graph.config.graphDirection&&(highestPositionPerLevel[childLevel]=highestPosition+4),placedCount++}}if(placedCount===prevCount)break}for(let node of graph.nodes)setColumnWidth(graph,node);graph.bundles=analyzeEdgeBundles(graph),processBundles(graph);for(let edge of graph.edges){if(edge.bundle&&edge.path.length>0){increaseGridSizeForPath(graph,edge.path),determineLabelLine(graph,edge);continue}determinePath(graph,edge),increaseGridSizeForPath(graph,edge.path),determineLabelLine(graph,edge)}for(let node of graph.nodes)node.drawingCoord=gridToDrawingCoord(graph,node.gridCoord),node.drawing=drawBox(node,graph);setCanvasSizeToGrid(graph.canvas,graph.columnWidth,graph.rowHeight),setRoleCanvasSizeToGrid(graph.roleCanvas,graph.columnWidth,graph.rowHeight),calculateSubgraphBoundingBoxes(graph),offsetDrawingForSubgraphs(graph)}function getEdgesFromNode(graph,node){return graph.edges.filter(e=>e.from.name===node.name)}function getChildren(graph,node){return getEdgesFromNode(graph,node).map(e=>e.to)}function parseSequenceDiagram(lines){let diagram={actors:[],messages:[],blocks:[],notes:[]},actorIds=new Set,blockStack=[];for(let i=1;i<lines.length;i++){let line=lines[i],actorMatch=line.match(/^(participant|actor)\s+(\S+?)(?:\s+as\s+(.+))?$/);if(actorMatch){let type=actorMatch[1],id=actorMatch[2],rawLabel=actorMatch[3]?.trim()??id,label=normalizeBrTags(rawLabel);actorIds.has(id)||(actorIds.add(id),diagram.actors.push({id,label,type}));continue}let noteMatch=line.match(/^Note\s+(left of|right of|over)\s+([^:]+):\s*(.+)$/i);if(noteMatch){let posStr=noteMatch[1].toLowerCase(),actorsStr=noteMatch[2].trim(),text=normalizeBrTags(noteMatch[3].trim()),noteActorIds=actorsStr.split(",").map(s=>s.trim());for(let aid of noteActorIds)ensureActor(diagram,actorIds,aid);let position="over";posStr==="left of"?position="left":posStr==="right of"&&(position="right"),diagram.notes.push({actorIds:noteActorIds,text,position,afterIndex:diagram.messages.length-1});continue}let blockMatch=line.match(/^(loop|alt|opt|par|critical|break|rect)\s*(.*)$/);if(blockMatch){let blockType=blockMatch[1],rawBlockLabel=blockMatch[2]?.trim()??"",label=normalizeBrTags(rawBlockLabel);blockStack.push({type:blockType,label,startIndex:diagram.messages.length,dividers:[]});continue}let dividerMatch=line.match(/^(else|and)\s*(.*)$/);if(dividerMatch&&blockStack.length>0){let rawDividerLabel=dividerMatch[2]?.trim()??"",label=normalizeBrTags(rawDividerLabel);blockStack[blockStack.length-1].dividers.push({index:diagram.messages.length,label});continue}if(line==="end"&&blockStack.length>0){let completed=blockStack.pop();diagram.blocks.push({type:completed.type,label:completed.label,startIndex:completed.startIndex,endIndex:Math.max(diagram.messages.length-1,completed.startIndex),dividers:completed.dividers});continue}let msgMatch=line.match(/^(\S+?)\s*(--?>?>|--?[)x]|--?>>|--?>)\s*([+-]?)(\S+?)\s*:\s*(.+)$/);if(msgMatch){let from=msgMatch[1],arrow=msgMatch[2],activationMark=msgMatch[3],to=msgMatch[4],label=normalizeBrTags(msgMatch[5].trim());ensureActor(diagram,actorIds,from),ensureActor(diagram,actorIds,to);let lineStyle=arrow.startsWith("--")?"dashed":"solid",arrowHead=arrow.includes(">>")||arrow.includes("x")?"filled":"open",msg={from,to,label,lineStyle,arrowHead};activationMark==="+"&&(msg.activate=!0),activationMark==="-"&&(msg.deactivate=!0),diagram.messages.push(msg);continue}let simpleMsgMatch=line.match(/^(\S+?)\s*(->>|-->>|-\)|--\)|-x|--x|->|-->)\s*([+-]?)(\S+?)\s*:\s*(.+)$/);if(simpleMsgMatch){let from=simpleMsgMatch[1],arrow=simpleMsgMatch[2],activationMark=simpleMsgMatch[3],to=simpleMsgMatch[4],label=normalizeBrTags(simpleMsgMatch[5].trim());ensureActor(diagram,actorIds,from),ensureActor(diagram,actorIds,to);let lineStyle=arrow.startsWith("--")?"dashed":"solid",arrowHead=arrow.includes(">>")||arrow.includes("x")?"filled":"open",msg={from,to,label,lineStyle,arrowHead};activationMark==="+"&&(msg.activate=!0),activationMark==="-"&&(msg.deactivate=!0),diagram.messages.push(msg);continue}}return diagram}function ensureActor(diagram,actorIds,id){actorIds.has(id)||(actorIds.add(id),diagram.actors.push({id,label:id,type:"participant"}))}function renderSequenceAscii(text,config,colorMode,theme){let lines=text.split(`
7`).map(l=>l.trim()).filter(l=>l.length>0&&!l.startsWith("%%")),diagram=parseSequenceDiagram(lines);if(diagram.actors.length===0)return"";let useAscii=config.useAscii,H=useAscii?"-":"\u2500",V=useAscii?"|":"\u2502",TL=useAscii?"+":"\u250C",TR=useAscii?"+":"\u2510",BL=useAscii?"+":"\u2514",BR=useAscii?"+":"\u2518",JT=useAscii?"+":"\u252C",JB=useAscii?"+":"\u2534",JL=useAscii?"+":"\u251C",JR=useAscii?"+":"\u2524",actorIdx=new Map;diagram.actors.forEach((a,i)=>actorIdx.set(a.id,i));let boxPad=1,halfBox=diagram.actors.map(a=>maxLineWidth(a.label)+2*boxPad+2).map(w=>Math.ceil(w/2)),actorBoxHeights=diagram.actors.map(a=>lineCount(a.label)+2),actorBoxH=Math.max(...actorBoxHeights,3),adjMaxWidth=new Array(Math.max(diagram.actors.length-1,0)).fill(0);for(let msg of diagram.messages){let fi=actorIdx.get(msg.from),ti=actorIdx.get(msg.to);if(fi===ti)continue;let lo=Math.min(fi,ti),hi=Math.max(fi,ti),needed=maxLineWidth(msg.label)+4,numGaps=hi-lo,perGap=Math.ceil(needed/numGaps);for(let g=lo;g<hi;g++)adjMaxWidth[g]=Math.max(adjMaxWidth[g],perGap)}let llX=[halfBox[0]];for(let i=1;i<diagram.actors.length;i++){let gap=Math.max(halfBox[i-1]+halfBox[i]+2,adjMaxWidth[i-1]+2,10);llX[i]=llX[i-1]+gap}let msgArrowY=[],msgLabelY=[],blockStartY=new Map,blockEndY=new Map,divYMap=new Map,notePositions=[],curY=actorBoxH;for(let m=0;m<diagram.messages.length;m++){for(let b=0;b<diagram.blocks.length;b++)diagram.blocks[b].startIndex===m&&(curY+=2,blockStartY.set(b,curY-1));for(let b=0;b<diagram.blocks.length;b++)for(let d=0;d<diagram.blocks[b].dividers.length;d++)diagram.blocks[b].dividers[d].index===m&&(curY+=1,divYMap.set(`${b}:${d}`,curY),curY+=1);curY+=1;let msg=diagram.messages[m],isSelf=msg.from===msg.to,msgLineCount=lineCount(msg.label);isSelf?(msgLabelY[m]=curY+1,msgArrowY[m]=curY,curY+=2+msgLineCount):(msgLabelY[m]=curY,msgArrowY[m]=curY+msgLineCount,curY+=msgLineCount+1);for(let n=0;n<diagram.notes.length;n++)if(diagram.notes[n].afterIndex===m){curY+=1;let note=diagram.notes[n],nLines=splitLines(note.text),nWidth=Math.max(...nLines.map(l=>l.length))+4,nHeight=nLines.length+2,aIdx=actorIdx.get(note.actorIds[0])??0,nx;if(note.position==="left")nx=llX[aIdx]-nWidth-1;else if(note.position==="right")nx=llX[aIdx]+2;else if(note.actorIds.length>=2){let aIdx2=actorIdx.get(note.actorIds[1])??aIdx;nx=Math.floor((llX[aIdx]+llX[aIdx2])/2)-Math.floor(nWidth/2)}else nx=llX[aIdx]-Math.floor(nWidth/2);nx=Math.max(0,nx),notePositions.push({x:nx,y:curY,width:nWidth,height:nHeight,lines:nLines}),curY+=nHeight}for(let b=0;b<diagram.blocks.length;b++)diagram.blocks[b].endIndex===m&&(curY+=1,blockEndY.set(b,curY),curY+=1)}curY+=1;let footerY=curY,totalH=footerY+actorBoxH,lastLL=llX[llX.length-1]??0,lastHalf=halfBox[halfBox.length-1]??0,totalW=lastLL+lastHalf+2;for(let m=0;m<diagram.messages.length;m++){let msg=diagram.messages[m];if(msg.from===msg.to){let fi=actorIdx.get(msg.from),selfRight=llX[fi]+6+2+msg.label.length;totalW=Math.max(totalW,selfRight+1)}}for(let np of notePositions)totalW=Math.max(totalW,np.x+np.width+1);let canvas=mkCanvas(totalW,totalH-1),rc=mkRoleCanvas(totalW,totalH-1);function setC(x,y,ch,role){x>=0&&x<canvas.length&&y>=0&&y<(canvas[0]?.length??0)&&(canvas[x][y]=ch,setRole(rc,x,y,role))}function drawActorBox(cx,topY,label){let lines2=splitLines(label),maxW=maxLineWidth(label),w=maxW+2*boxPad+2,h=lines2.length+2,left=cx-Math.floor(w/2);setC(left,topY,TL,"border");for(let x=1;x<w-1;x++)setC(left+x,topY,H,"border");setC(left+w-1,topY,TR,"border");for(let i=0;i<lines2.length;i++){let row=topY+1+i;setC(left,row,V,"border"),setC(left+w-1,row,V,"border");let line=lines2[i],ls=left+1+boxPad+Math.floor((maxW-line.length)/2);for(let j=0;j<line.length;j++)setC(ls+j,row,line[j],"text")}let bottomY=topY+h-1;setC(left,bottomY,BL,"border");for(let x=1;x<w-1;x++)setC(left+x,bottomY,H,"border");setC(left+w-1,bottomY,BR,"border")}for(let i=0;i<diagram.actors.length;i++){let x=llX[i];for(let y=actorBoxH;y<=footerY;y++)setC(x,y,V,"line")}for(let i=0;i<diagram.actors.length;i++){let actor=diagram.actors[i];drawActorBox(llX[i],0,actor.label),drawActorBox(llX[i],footerY,actor.label),useAscii||(setC(llX[i],actorBoxH-1,JT,"junction"),setC(llX[i],footerY,JB,"junction"))}for(let m=0;m<diagram.messages.length;m++){let msg=diagram.messages[m],fi=actorIdx.get(msg.from),ti=actorIdx.get(msg.to),fromX=llX[fi],toX=llX[ti],isSelf=fi===ti,isDashed=msg.lineStyle==="dashed",isFilled=msg.arrowHead==="filled",lineChar=isDashed?useAscii?".":"\u254C":H;if(isSelf){let y0=msgArrowY[m],loopW=Math.max(4,4);setC(fromX,y0,JL,"junction");for(let x=fromX+1;x<fromX+loopW;x++)setC(x,y0,lineChar,"line");setC(fromX+loopW,y0,useAscii?"+":"\u2510","corner"),setC(fromX+loopW,y0+1,V,"line");let labelX=fromX+loopW+2;for(let i=0;i<msg.label.length;i++)labelX+i<totalW&&setC(labelX+i,y0+1,msg.label[i],"text");let arrowChar=isFilled?useAscii?"<":"\u25C0":useAscii?"<":"\u25C1";setC(fromX,y0+2,arrowChar,"arrow");for(let x=fromX+1;x<fromX+loopW;x++)setC(x,y0+2,lineChar,"line");setC(fromX+loopW,y0+2,useAscii?"+":"\u2518","corner")}else{let labelY=msgLabelY[m],arrowY=msgArrowY[m],leftToRight=fromX<toX,midX=Math.floor((fromX+toX)/2),msgLines=splitLines(msg.label);for(let lineIdx=0;lineIdx<msgLines.length;lineIdx++){let line=msgLines[lineIdx],labelStart=midX-Math.floor(line.length/2),y=labelY+lineIdx;for(let i=0;i<line.length;i++){let lx=labelStart+i;lx>=0&&lx<totalW&&setC(lx,y,line[i],"text")}}if(leftToRight){for(let x=fromX+1;x<toX;x++)setC(x,arrowY,lineChar,"line");setC(toX,arrowY,isFilled?useAscii?">":"\u25B6":useAscii?">":"\u25B7","arrow")}else{for(let x=toX+1;x<fromX;x++)setC(x,arrowY,lineChar,"line");setC(toX,arrowY,isFilled?useAscii?"<":"\u25C0":useAscii?"<":"\u25C1","arrow")}}}for(let b=0;b<diagram.blocks.length;b++){let block=diagram.blocks[b],topY=blockStartY.get(b),botY=blockEndY.get(b);if(topY===void 0||botY===void 0)continue;let minLX=totalW,maxLX=0;for(let m=block.startIndex;m<=block.endIndex&&!(m>=diagram.messages.length);m++){let msg=diagram.messages[m],f=actorIdx.get(msg.from)??0,t=actorIdx.get(msg.to)??0;minLX=Math.min(minLX,llX[Math.min(f,t)]),maxLX=Math.max(maxLX,llX[Math.max(f,t)])}let bLeft=Math.max(0,minLX-4),bRight=Math.min(totalW-1,maxLX+4);setC(bLeft,topY,TL,"border");for(let x=bLeft+1;x<bRight;x++)setC(x,topY,H,"border");setC(bRight,topY,TR,"border");let hdrLabel=block.label?`${block.type} [${block.label}]`:block.type,hdrLines=splitLines(hdrLabel);for(let lineIdx=0;lineIdx<hdrLines.length&&topY+lineIdx<botY;lineIdx++){let line=hdrLines[lineIdx];for(let i=0;i<line.length&&bLeft+1+i<bRight;i++)setC(bLeft+1+i,topY+lineIdx,line[i],"text")}setC(bLeft,botY,BL,"border");for(let x=bLeft+1;x<bRight;x++)setC(x,botY,H,"border");setC(bRight,botY,BR,"border");for(let y=topY+1;y<botY;y++)setC(bLeft,y,V,"border"),setC(bRight,y,V,"border");for(let d=0;d<block.dividers.length;d++){let dY=divYMap.get(`${b}:${d}`);if(dY===void 0)continue;let dashChar=isDashedH();setC(bLeft,dY,JL,"junction");for(let x=bLeft+1;x<bRight;x++)setC(x,dY,dashChar,"line");setC(bRight,dY,JR,"junction");let dLabel=block.dividers[d].label;if(dLabel){let dStr=`[${dLabel}]`;for(let i=0;i<dStr.length&&bLeft+1+i<bRight;i++)setC(bLeft+1+i,dY,dStr[i],"text")}}}for(let np of notePositions){increaseSize(canvas,np.x+np.width,np.y+np.height),increaseRoleCanvasSize(rc,np.x+np.width,np.y+np.height),setC(np.x,np.y,TL,"border");for(let x=1;x<np.width-1;x++)setC(np.x+x,np.y,H,"border");setC(np.x+np.width-1,np.y,TR,"border");for(let l=0;l<np.lines.length;l++){let ly=np.y+1+l;setC(np.x,ly,V,"border"),setC(np.x+np.width-1,ly,V,"border");for(let i=0;i<np.lines[l].length;i++)setC(np.x+2+i,ly,np.lines[l][i],"text")}let by=np.y+np.height-1;setC(np.x,by,BL,"border");for(let x=1;x<np.width-1;x++)setC(np.x+x,by,H,"border");setC(np.x+np.width-1,by,BR,"border")}return canvasToString(canvas,{roleCanvas:rc,colorMode,theme});function isDashedH(){return useAscii?"-":"\u254C"}}function parseClassDiagram(lines){let diagram={classes:[],relationships:[],namespaces:[]},classMap=new Map,currentNamespace=null,currentClass=null,braceDepth=0;for(let i=1;i<lines.length;i++){let line=lines[i];if(currentClass&&braceDepth>0){if(line==="}"){braceDepth--,braceDepth===0&&(currentClass=null);continue}let annotMatch=line.match(/^<<(\w+)>>$/);if(annotMatch){currentClass.annotation=annotMatch[1];continue}let member=parseMember(line);member&&(member.isMethod?currentClass.methods.push(member.member):currentClass.attributes.push(member.member));continue}let nsMatch=line.match(/^namespace\s+(\S+)\s*\{$/);if(nsMatch){currentNamespace={name:nsMatch[1],classIds:[]};continue}if(line==="}"&¤tNamespace){diagram.namespaces.push(currentNamespace),currentNamespace=null;continue}let classBlockMatch=line.match(/^class\s+(\S+?)(?:\s*~(\w+)~)?\s*\{$/);if(classBlockMatch){let id=classBlockMatch[1],generic=classBlockMatch[2],cls=ensureClass(classMap,id);generic&&(cls.label=`${id}<${generic}>`),currentClass=cls,braceDepth=1,currentNamespace&¤tNamespace.classIds.push(id);continue}let classOnlyMatch=line.match(/^class\s+(\S+?)(?:\s*~(\w+)~)?\s*$/);if(classOnlyMatch){let id=classOnlyMatch[1],generic=classOnlyMatch[2],cls=ensureClass(classMap,id);generic&&(cls.label=`${id}<${generic}>`),currentNamespace&¤tNamespace.classIds.push(id);continue}let inlineAnnotMatch=line.match(/^class\s+(\S+?)\s*\{\s*<<(\w+)>>\s*\}$/);if(inlineAnnotMatch){let cls=ensureClass(classMap,inlineAnnotMatch[1]);cls.annotation=inlineAnnotMatch[2];continue}let inlineAttrMatch=line.match(/^(\S+?)\s*:\s*(.+)$/);if(inlineAttrMatch){let rest=inlineAttrMatch[2];if(!rest.match(/<\|--|--|\*--|o--|-->|\.\.>|\.\.\|>/)){let cls=ensureClass(classMap,inlineAttrMatch[1]),member=parseMember(rest);member&&(member.isMethod?cls.methods.push(member.member):cls.attributes.push(member.member));continue}}let rel=parseRelationship(line);if(rel){ensureClass(classMap,rel.from),ensureClass(classMap,rel.to),diagram.relationships.push(rel);continue}}return diagram.classes=[...classMap.values()],diagram}function ensureClass(classMap,id){let cls=classMap.get(id);return cls||(cls={id,label:id,attributes:[],methods:[]},classMap.set(id,cls)),cls}function parseMember(line){let trimmed=line.trim().replace(/;$/,"");if(!trimmed)return null;let visibility="",rest=trimmed;/^[+\-#~]/.test(rest)&&(visibility=rest[0],rest=rest.slice(1).trim());let methodMatch=rest.match(/^(.+?)\(([^)]*)\)(?:\s*(.+))?$/);if(methodMatch){let name2=methodMatch[1].trim(),params=methodMatch[2]?.trim()||void 0,type2=methodMatch[3]?.trim(),isStatic2=name2.endsWith("$")||rest.includes("$"),isAbstract2=name2.endsWith("*")||rest.includes("*");return{member:{visibility,name:name2.replace(/[$*]$/,""),type:type2||void 0,isStatic:isStatic2,isAbstract:isAbstract2,isMethod:!0,params},isMethod:!0}}let parts=rest.split(/\s+/),name,type;parts.length>=2?(type=parts[0],name=parts.slice(1).join(" ")):name=parts[0]??rest;let isStatic=name.endsWith("$"),isAbstract=name.endsWith("*");return{member:{visibility,name:name.replace(/[$*]$/,""),type:type||void 0,isStatic,isAbstract,isMethod:!1},isMethod:!1}}function parseRelationship(line){let match=line.match(/^(\S+?)\s+(?:"([^"]*?)"\s+)?(<\|--|<\|\.\.|\*--|o--|-->|--\*|--o|--\|>|\.\.>|\.\.\|>|<--|<\.\.?|--)\s+(?:"([^"]*?)"\s+)?(\S+?)(?:\s*:\s*(.+))?$/);if(!match)return null;let from=match[1],rawFromCardinality=match[2],fromCardinality=rawFromCardinality?normalizeBrTags(rawFromCardinality):void 0,arrow=match[3].trim(),rawToCardinality=match[4],toCardinality=rawToCardinality?normalizeBrTags(rawToCardinality):void 0,to=match[5],rawLabel=match[6]?.trim(),label=rawLabel?normalizeBrTags(rawLabel):void 0,parsed=parseArrow(arrow);return parsed?{from,to,type:parsed.type,markerAt:parsed.markerAt,label,fromCardinality,toCardinality}:null}function parseArrow(arrow){switch(arrow.trim()){case"<|--":return{type:"inheritance",markerAt:"from"};case"--|>":return{type:"inheritance",markerAt:"to"};case"<|..":return{type:"realization",markerAt:"from"};case"..|>":return{type:"realization",markerAt:"to"};case"*--":return{type:"composition",markerAt:"from"};case"--*":return{type:"composition",markerAt:"to"};case"o--":return{type:"aggregation",markerAt:"from"};case"--o":return{type:"aggregation",markerAt:"to"};case"-->":return{type:"association",markerAt:"to"};case"<--":return{type:"association",markerAt:"from"};case"..>":return{type:"dependency",markerAt:"to"};case"<..":return{type:"dependency",markerAt:"from"};case"--":return{type:"association",markerAt:"to"};default:return null}}function classifyBoxChar(ch){return/^[┌┐└┘├┤┬┴┼│─╭╮╰╯+\-|]$/.test(ch)?"border":"text"}function formatMember(m){let vis=m.visibility||"",type=m.type?`: ${m.type}`:"";return`${vis}${m.name}${type}`}function buildClassSections(cls){let header=[];cls.annotation&&header.push(`<<${cls.annotation}>>`);let nameLines=splitLines(cls.label);header.push(...nameLines);let attrs=cls.attributes.map(formatMember),methods=cls.methods.map(formatMember);return attrs.length===0&&methods.length===0?[header]:methods.length===0?[header,attrs]:[header,attrs,methods]}function getRelMarker(type,markerAt){return{type,markerAt,dashed:type==="dependency"||type==="realization"}}function getMarkerShape(type,useAscii,direction){switch(type){case"inheritance":case"realization":return direction==="down"?useAscii?"^":"\u25B3":direction==="up"?useAscii?"v":"\u25BD":direction==="left"?useAscii?">":"\u25C1":useAscii?"<":"\u25B7";case"composition":return useAscii?"*":"\u25C6";case"aggregation":return useAscii?"o":"\u25C7";case"association":case"dependency":return direction==="down"?useAscii?"v":"\u25BC":direction==="up"?useAscii?"^":"\u25B2":direction==="left"?useAscii?"<":"\u25C0":useAscii?">":"\u25B6"}}function renderClassAscii(text,config,colorMode,theme){let lines=text.split(`
8`).map(l=>l.trim()).filter(l=>l.length>0&&!l.startsWith("%%")),diagram=parseClassDiagram(lines);if(diagram.classes.length===0)return"";let useAscii=config.useAscii,hGap=4,vGap=3,classSections=new Map,classBoxW=new Map,classBoxH=new Map;for(let cls of diagram.classes){let sections=buildClassSections(cls);classSections.set(cls.id,sections);let maxTextW=0;for(let section of sections)for(let line of section)maxTextW=Math.max(maxTextW,line.length);let boxW=maxTextW+4,totalLines=0;for(let section of sections)totalLines+=Math.max(section.length,1);let boxH=totalLines+(sections.length-1)+2;classBoxW.set(cls.id,boxW),classBoxH.set(cls.id,boxH)}let classById=new Map;for(let cls of diagram.classes)classById.set(cls.id,cls);let parents=new Map,children=new Map;for(let rel of diagram.relationships){let isHierarchical=rel.type==="inheritance"||rel.type==="realization",parentId=isHierarchical&&rel.markerAt==="to"?rel.to:rel.from,childId=isHierarchical&&rel.markerAt==="to"?rel.from:rel.to;parents.has(childId)||parents.set(childId,new Set),parents.get(childId).add(parentId),children.has(parentId)||children.set(parentId,new Set),children.get(parentId).add(childId)}let level=new Map,queue=diagram.classes.filter(c=>!parents.has(c.id)||parents.get(c.id).size===0).map(c=>c.id);for(let id of queue)level.set(id,0);let levelCap=diagram.classes.length-1,qi=0;for(;qi<queue.length;){let id=queue[qi++],childSet=children.get(id);if(childSet)for(let childId of childSet){let newLevel=(level.get(id)??0)+1;newLevel>levelCap||(!level.has(childId)||level.get(childId)<newLevel)&&(level.set(childId,newLevel),queue.push(childId))}}for(let cls of diagram.classes)level.has(cls.id)||level.set(cls.id,0);let maxLevel=Math.max(...level.values(),0),levelGroups=Array.from({length:maxLevel+1},()=>[]);for(let cls of diagram.classes)levelGroups[level.get(cls.id)].push(cls.id);let placed=new Map,currentY=0;for(let lv=0;lv<=maxLevel;lv++){let group=levelGroups[lv];if(group.length===0)continue;let currentX=0,maxH=0;for(let id of group){let cls=classById.get(id),w=classBoxW.get(id),h=classBoxH.get(id);placed.set(id,{cls,sections:classSections.get(id),x:currentX,y:currentY,width:w,height:h}),currentX+=w+hGap,maxH=Math.max(maxH,h)}currentY+=maxH+vGap}let totalW=0,totalH=0;for(let p of placed.values())totalW=Math.max(totalW,p.x+p.width),totalH=Math.max(totalH,p.y+p.height);totalW+=4,totalH+=2;let canvas=mkCanvas(totalW-1,totalH-1),rc=mkRoleCanvas(totalW-1,totalH-1);function setC(x,y,ch,role){x>=0&&x<canvas.length&&y>=0&&y<(canvas[0]?.length??0)&&(canvas[x][y]=ch,setRole(rc,x,y,role))}for(let p of placed.values()){let boxCanvas=drawMultiBox(p.sections,useAscii);for(let bx=0;bx<boxCanvas.length;bx++)for(let by=0;by<boxCanvas[0].length;by++){let ch=boxCanvas[bx][by];if(ch!==" "){let cx=p.x+bx,cy=p.y+by;cx<totalW&&cy<totalH&&setC(cx,cy,ch,classifyBoxChar(ch))}}}let boxOccupancy=[];for(let p of placed.values())boxOccupancy.push({x1:p.x,x2:p.x+p.width-1,y1:p.y,y2:p.y+p.height-1});function isInsideBox(x,y,excludeIds){for(let[id,p]of placed.entries())if(!excludeIds?.has(id)&&x>=p.x&&x<=p.x+p.width-1&&y>=p.y&&y<=p.y+p.height-1)return!0;return!1}function findClearColumn(startX,y1,y2,excludeIds){let clear=!0;for(let y=Math.min(y1,y2);y<=Math.max(y1,y2);y++)if(isInsideBox(startX,y,excludeIds)){clear=!1;break}if(clear)return startX;for(let offset=1;offset<totalW+10;offset++){let rightX=startX+offset;clear=!0;for(let y=Math.min(y1,y2);y<=Math.max(y1,y2);y++)if(isInsideBox(rightX,y,excludeIds)){clear=!1;break}if(clear)return rightX;let leftX=startX-offset;if(leftX>=0){clear=!0;for(let y=Math.min(y1,y2);y<=Math.max(y1,y2);y++)if(isInsideBox(leftX,y,excludeIds)){clear=!1;break}if(clear)return leftX}}return totalW+2}let H=useAscii?"-":"\u2500",V=useAscii?"|":"\u2502",dashH=useAscii?".":"\u254C",dashV=useAscii?":":"\u250A";for(let rel of diagram.relationships){let fromP=placed.get(rel.from),toP=placed.get(rel.to);if(!fromP||!toP)continue;let marker=getRelMarker(rel.type,rel.markerAt),lineH=marker.dashed?dashH:H,lineV=marker.dashed?dashV:V,excludeIds=new Set([rel.from,rel.to]),fromCX=fromP.x+Math.floor(fromP.width/2),fromBY=fromP.y+fromP.height-1,toCX=toP.x+Math.floor(toP.width/2),toTY=toP.y;if(fromBY<toTY){let routeX=findClearColumn(fromCX,fromBY+1,toTY-1,excludeIds),needsDetour=routeX!==fromCX;if(routeX>=totalW&&increaseSize(canvas,routeX+2,totalH),needsDetour){let exitY=fromBY+1,entryY=toTY-1,lx1=Math.min(fromCX,routeX),rx1=Math.max(fromCX,routeX);for(let x=lx1;x<=rx1;x++)setC(x,exitY,lineH,"line");!useAscii&&exitY<(canvas[0]?.length??0)&&(fromCX<routeX?(setC(fromCX,exitY,"\u2514","corner"),setC(routeX,exitY,"\u2510","corner")):(setC(fromCX,exitY,"\u2518","corner"),setC(routeX,exitY,"\u250C","corner")));for(let y=exitY+1;y<=entryY;y++)setC(routeX,y,lineV,"line");if(routeX!==toCX){let lx2=Math.min(routeX,toCX),rx2=Math.max(routeX,toCX);for(let x=lx2;x<=rx2;x++)setC(x,entryY,lineH,"line");!useAscii&&entryY<(canvas[0]?.length??0)&&(routeX<toCX?(setC(routeX,entryY,"\u2514","corner"),setC(toCX,entryY,"\u2510","corner")):(setC(routeX,entryY,"\u2518","corner"),setC(toCX,entryY,"\u250C","corner")))}if(marker.markerAt==="to"){let markerChar=getMarkerShape(marker.type,useAscii,"down");setC(toCX,entryY,markerChar,"arrow")}if(marker.markerAt==="from"){let markerChar=getMarkerShape(marker.type,useAscii,"down");setC(fromCX,fromBY+1,markerChar,"arrow")}}else{let midY=fromBY+Math.floor((toTY-fromBY)/2);for(let y=fromBY+1;y<=midY;y++)setC(fromCX,y,lineV,"line");if(fromCX!==toCX&&midY<(canvas[0]?.length??0)){let lx=Math.min(fromCX,toCX),rx=Math.max(fromCX,toCX);for(let x=lx;x<=rx;x++)setC(x,midY,lineH,"line");useAscii||(setC(fromCX,midY,fromCX<toCX?"\u2514":"\u2518","corner"),setC(toCX,midY,fromCX<toCX?"\u2510":"\u250C","corner"))}for(let y=midY+1;y<toTY;y++)setC(toCX,y,lineV,"line");marker.markerAt==="to"&&setC(toCX,toTY-1,getMarkerShape(marker.type,useAscii,"down"),"arrow"),marker.markerAt==="from"&&setC(fromCX,fromBY+1,getMarkerShape(marker.type,useAscii,"down"),"arrow")}}else if(toP.y+toP.height-1<fromP.y){let fromTY=fromP.y,toBY=toP.y+toP.height-1,midY=toBY+Math.floor((fromTY-toBY)/2);for(let y=fromTY-1;y>=midY;y--)setC(fromCX,y,lineV,"line");if(fromCX!==toCX){let lx=Math.min(fromCX,toCX),rx=Math.max(fromCX,toCX);for(let x=lx;x<=rx;x++)setC(x,midY,lineH,"line");!useAscii&&midY>=0&&midY<totalH&&(setC(fromCX,midY,fromCX<toCX?"\u250C":"\u2510","corner"),setC(toCX,midY,fromCX<toCX?"\u2518":"\u2514","corner"))}for(let y=midY-1;y>toBY;y--)setC(toCX,y,lineV,"line");if(marker.markerAt==="from"){let markerChar=getMarkerShape(marker.type,useAscii,"up"),my=fromTY-1;for(let i=0;i<markerChar.length;i++)setC(fromCX-Math.floor(markerChar.length/2)+i,my,markerChar[i],"arrow")}if(marker.markerAt==="to"){let markerDir=marker.type==="inheritance"||marker.type==="realization"?"down":"up",markerChar=getMarkerShape(marker.type,useAscii,markerDir),my=toBY+1;for(let i=0;i<markerChar.length;i++)setC(toCX-Math.floor(markerChar.length/2)+i,my,markerChar[i],"arrow")}}else{let detourY=Math.max(fromBY,toP.y+toP.height-1)+2;increaseSize(canvas,totalW,detourY+1),increaseRoleCanvasSize(rc,totalW,detourY+1);for(let y=fromBY+1;y<=detourY;y++)setC(fromCX,y,lineV,"line");let lx=Math.min(fromCX,toCX),rx=Math.max(fromCX,toCX);for(let x=lx;x<=rx;x++)setC(x,detourY,lineH,"line");for(let y=detourY-1;y>=toP.y+toP.height;y--)setC(toCX,y,lineV,"line");if(marker.markerAt==="from"){let markerChar=getMarkerShape(marker.type,useAscii,"down"),my=fromBY+1;for(let i=0;i<markerChar.length;i++)setC(fromCX-Math.floor(markerChar.length/2)+i,my,markerChar[i],"arrow")}if(marker.markerAt==="to"){let markerChar=getMarkerShape(marker.type,useAscii,"up"),my=toP.y+toP.height;for(let i=0;i<markerChar.length;i++)setC(toCX-Math.floor(markerChar.length/2)+i,my,markerChar[i],"arrow")}}if(rel.label){let lines2=splitLines(rel.label),maxLabelWidth=Math.max(...lines2.map(l=>l.length))+2,baseMidY,idealMidX;if(fromBY<toTY)baseMidY=Math.floor((fromBY+1+toTY-1)/2),idealMidX=Math.floor((fromCX+toCX)/2);else if(toP.y+toP.height-1<fromP.y){let toBY=toP.y+toP.height-1;baseMidY=Math.floor((toBY+1+fromP.y-1)/2),idealMidX=Math.floor((fromCX+toCX)/2)}else baseMidY=Math.max(fromBY,toP.y+toP.height-1)+2,idealMidX=Math.floor((fromCX+toCX)/2);let labelY=baseMidY,halfHeight=Math.floor(lines2.length/2),labelInBox=!1;for(let i=0;i<lines2.length;i++){let y=labelY-halfHeight+i,idealLabelStart=idealMidX-Math.floor(maxLabelWidth/2),labelStart=Math.max(0,idealLabelStart);for(let x=labelStart;x<labelStart+maxLabelWidth;x++)if(isInsideBox(x,y,excludeIds)){labelInBox=!0;break}if(labelInBox)break}if(labelInBox){let gapTop=fromBY+1,gapBottom=toTY-1;for(let y=gapTop;y<=gapBottom;y++){let clearRow=!0,idealLabelStart=idealMidX-Math.floor(maxLabelWidth/2),labelStart=Math.max(0,idealLabelStart);for(let x=labelStart;x<labelStart+maxLabelWidth;x++)if(isInsideBox(x,y,excludeIds)){clearRow=!1;break}if(clearRow){labelY=y;break}}}let startY=labelY-halfHeight;for(let lineIdx=0;lineIdx<lines2.length;lineIdx++){let paddedLine=` ${lines2[lineIdx]} `,idealLabelStart=idealMidX-Math.floor(paddedLine.length/2),labelStart=Math.max(0,idealLabelStart),y=startY+lineIdx,labelEnd=labelStart+paddedLine.length;labelEnd>0&&y>=0&&(increaseSize(canvas,Math.max(labelEnd,1),Math.max(y+1,1)),increaseRoleCanvasSize(rc,Math.max(labelEnd,1),Math.max(y+1,1)));for(let i=0;i<paddedLine.length;i++){let lx=labelStart+i;lx>=0&&y>=0&&setC(lx,y,paddedLine[i],"text")}}}}return canvasToString(canvas,{roleCanvas:rc,colorMode,theme})}function parseErDiagram(lines){let diagram={entities:[],relationships:[]},entityMap=new Map,currentEntity=null;for(let i=1;i<lines.length;i++){let line=lines[i];if(currentEntity){if(line==="}"){currentEntity=null;continue}let attr=parseAttribute(line);attr&¤tEntity.attributes.push(attr);continue}let entityBlockMatch=line.match(/^(\S+)\s*\{$/);if(entityBlockMatch){let id=entityBlockMatch[1];currentEntity=ensureEntity(entityMap,id);continue}let rel=parseRelationshipLine(line);if(rel){ensureEntity(entityMap,rel.entity1),ensureEntity(entityMap,rel.entity2),diagram.relationships.push(rel);continue}}return diagram.entities=[...entityMap.values()],diagram}function ensureEntity(entityMap,id){let entity=entityMap.get(id);return entity||(entity={id,label:id,attributes:[]},entityMap.set(id,entity)),entity}function parseAttribute(line){let match=line.match(/^(\S+)\s+(\S+)(?:\s+(.+))?$/);if(!match)return null;let type=match[1],name=match[2],rest=match[3]?.trim()??"",keys=[],comment,commentMatch=rest.match(/"([^"]*)"/);commentMatch&&(comment=normalizeBrTags(commentMatch[1]));let restWithoutComment=rest.replace(/"[^"]*"/,"").trim();for(let part of restWithoutComment.split(/\s+/)){let upper=part.toUpperCase();(upper==="PK"||upper==="FK"||upper==="UK")&&keys.push(upper)}return{type,name,keys,comment}}function parseRelationshipLine(line){let match=line.match(/^(\S+)\s+([|o}{]+(?:--|\.\.)[|o}{]+)\s+(\S+)\s*:\s*(.+)$/);if(!match)return null;let entity1=match[1],cardinalityStr=match[2],entity2=match[3],rawLabel=match[4].trim().replace(/^["']|["']$/g,""),label=normalizeBrTags(rawLabel),lineMatch=cardinalityStr.match(/^([|o}{]+)(--|\.\.?)([|o}{]+)$/);if(!lineMatch)return null;let leftStr=lineMatch[1],lineStyle=lineMatch[2],rightStr=lineMatch[3],cardinality1=parseCardinality(leftStr),cardinality2=parseCardinality(rightStr),identifying=lineStyle==="--";return!cardinality1||!cardinality2?null:{entity1,entity2,cardinality1,cardinality2,label,identifying}}function parseCardinality(str){let sorted=str.split("").sort().join("");return sorted==="||"?"one":sorted==="o|"?"zero-one":sorted==="|}"||sorted==="{|"?"many":sorted==="{o"||sorted==="o{"?"zero-many":null}function classifyBoxChar2(ch){return/^[┌┐└┘├┤┬┴┼│─╭╮╰╯+\-|]$/.test(ch)?"border":"text"}function formatAttribute(attr){return`${attr.keys.length>0?attr.keys.join(",")+" ":" "}${attr.type} ${attr.name}`}function buildEntitySections(entity){let header=splitLines(entity.label),attrs=entity.attributes.map(formatAttribute);return attrs.length===0?[header]:[header,attrs]}function getCrowsFootChars(card,useAscii,isRight=!1){if(useAscii)switch(card){case"one":return"|";case"zero-one":return"o|";case"many":return isRight?"<":">";case"zero-many":return isRight?"o<":">o"}else switch(card){case"one":return"\u2502";case"zero-one":return"\u25CB\u2502";case"many":return isRight?"\u255F":"\u2562";case"zero-many":return isRight?"\u25CB\u255F":"\u2562\u25CB"}}function findConnectedComponents(diagram){let visited=new Set,components=[],neighbors=new Map;for(let ent of diagram.entities)neighbors.set(ent.id,new Set);for(let rel of diagram.relationships)neighbors.get(rel.entity1)?.add(rel.entity2),neighbors.get(rel.entity2)?.add(rel.entity1);function dfs(startId,component){let stack=[startId];for(;stack.length>0;){let nodeId=stack.pop();if(!visited.has(nodeId)){visited.add(nodeId),component.add(nodeId);for(let neighbor of neighbors.get(nodeId)??[])visited.has(neighbor)||stack.push(neighbor)}}}for(let ent of diagram.entities)if(!visited.has(ent.id)){let component=new Set;dfs(ent.id,component),component.size>0&&components.push(component)}return components}function renderErAscii(text,config,colorMode,theme){let lines=text.split(`
9`).map(l=>l.trim()).filter(l=>l.length>0&&!l.startsWith("%%")),diagram=parseErDiagram(lines);if(diagram.entities.length===0)return"";let useAscii=config.useAscii,hGap=6,vGap=4,componentGap=6,entitySections=new Map,entityBoxW=new Map,entityBoxH=new Map,entityById=new Map;for(let ent of diagram.entities){entityById.set(ent.id,ent);let sections=buildEntitySections(ent);entitySections.set(ent.id,sections);let maxTextW=0;for(let section of sections)for(let line of section)maxTextW=Math.max(maxTextW,line.length);let boxW=maxTextW+4,totalLines=0;for(let section of sections)totalLines+=Math.max(section.length,1);let boxH=totalLines+(sections.length-1)+2;entityBoxW.set(ent.id,boxW),entityBoxH.set(ent.id,boxH)}let components=findConnectedComponents(diagram),placed=new Map,currentY=0;for(let component of components){let componentEntities=diagram.entities.filter(e=>component.has(e.id)),maxPerRow=Math.max(2,Math.ceil(Math.sqrt(componentEntities.length))),currentX=0,maxRowH=0,colCount=0,componentStartY=currentY;for(let ent of componentEntities){let w=entityBoxW.get(ent.id),h=entityBoxH.get(ent.id);colCount>=maxPerRow&&(currentY+=maxRowH+vGap,currentX=0,maxRowH=0,colCount=0),placed.set(ent.id,{entity:ent,sections:entitySections.get(ent.id),x:currentX,y:currentY,width:w,height:h}),currentX+=w+hGap,maxRowH=Math.max(maxRowH,h),colCount++}currentY+=maxRowH+componentGap}let totalW=0,totalH=0;for(let p of placed.values())totalW=Math.max(totalW,p.x+p.width),totalH=Math.max(totalH,p.y+p.height);totalW+=4,totalH+=2;let canvas=mkCanvas(totalW-1,totalH-1),rc=mkRoleCanvas(totalW-1,totalH-1);function setC(x,y,ch,role){x>=0&&x<canvas.length&&y>=0&&y<(canvas[0]?.length??0)&&(canvas[x][y]=ch,setRole(rc,x,y,role))}for(let p of placed.values()){let boxCanvas=drawMultiBox(p.sections,useAscii);for(let bx=0;bx<boxCanvas.length;bx++)for(let by=0;by<boxCanvas[0].length;by++){let ch=boxCanvas[bx][by];if(ch!==" "){let cx=p.x+bx,cy=p.y+by;cx<totalW&&cy<totalH&&setC(cx,cy,ch,classifyBoxChar2(ch))}}}let H=useAscii?"-":"\u2500",V=useAscii?"|":"\u2502",dashH=useAscii?".":"\u254C",dashV=useAscii?":":"\u250A";for(let rel of diagram.relationships){let e1=placed.get(rel.entity1),e2=placed.get(rel.entity2);if(!e1||!e2)continue;let lineH=rel.identifying?H:dashH,lineV=rel.identifying?V:dashV,e1CX=e1.x+Math.floor(e1.width/2),e1CY=e1.y+Math.floor(e1.height/2),e2CX=e2.x+Math.floor(e2.width/2),e2CY=e2.y+Math.floor(e2.height/2);if(Math.abs(e1CY-e2CY)<Math.max(e1.height,e2.height)){let[left,right]=e1CX<e2CX?[e1,e2]:[e2,e1],[leftCard,rightCard]=e1CX<e2CX?[rel.cardinality1,rel.cardinality2]:[rel.cardinality2,rel.cardinality1],startX=left.x+left.width,endX=right.x-1,lineY=left.y+Math.floor(left.height/2);for(let x=startX;x<=endX;x++)setC(x,lineY,lineH,"line");let leftChars=getCrowsFootChars(leftCard,useAscii,!1);for(let i=0;i<leftChars.length;i++)setC(startX+i,lineY,leftChars[i],"arrow");let rightChars=getCrowsFootChars(rightCard,useAscii,!0);for(let i=0;i<rightChars.length;i++)setC(endX-rightChars.length+1+i,lineY,rightChars[i],"arrow");if(rel.label){let lines2=splitLines(rel.label),gapMid=Math.floor((startX+endX)/2);for(let lineIdx=0;lineIdx<lines2.length;lineIdx++){let line=lines2[lineIdx],labelStart=Math.max(startX,gapMid-Math.floor(line.length/2)),labelY=lineY+1+lineIdx;increaseSize(canvas,Math.max(labelStart+line.length,1),Math.max(labelY+1,1)),increaseRoleCanvasSize(rc,Math.max(labelStart+line.length,1),Math.max(labelY+1,1));for(let i=0;i<line.length;i++){let lx=labelStart+i;lx>=startX&&lx<=endX&&setC(lx,labelY,line[i],"text")}}}}else{let[upper,lower]=e1CY<e2CY?[e1,e2]:[e2,e1],[upperCard,lowerCard]=e1CY<e2CY?[rel.cardinality1,rel.cardinality2]:[rel.cardinality2,rel.cardinality1],startY=upper.y+upper.height,endY=lower.y-1,lineX=upper.x+Math.floor(upper.width/2);for(let y=startY;y<=endY;y++)setC(lineX,y,lineV,"line");let lowerCX=lower.x+Math.floor(lower.width/2);if(lineX!==lowerCX){let midY=Math.floor((startY+endY)/2),lx=Math.min(lineX,lowerCX),rx=Math.max(lineX,lowerCX);for(let x=lx;x<=rx;x++)setC(x,midY,lineH,"line");for(let y=midY+1;y<=endY;y++)setC(lowerCX,y,lineV,"line")}let upperChars=getCrowsFootChars(upperCard,useAscii,!1);for(let i=0;i<upperChars.length;i++)setC(lineX-Math.floor(upperChars.length/2)+i,startY,upperChars[i],"arrow");let targetX=lineX!==lowerCX?lowerCX:lineX,lowerChars=getCrowsFootChars(lowerCard,useAscii,!0);for(let i=0;i<lowerChars.length;i++)setC(targetX-Math.floor(lowerChars.length/2)+i,endY,lowerChars[i],"arrow");if(rel.label){let lines2=splitLines(rel.label),startLabelY=Math.floor((startY+endY)/2)-Math.floor((lines2.length-1)/2);for(let lineIdx=0;lineIdx<lines2.length;lineIdx++){let line=lines2[lineIdx],labelX=lineX+2,y=startLabelY+lineIdx;if(y>=0)for(let i=0;i<line.length;i++){let lx=labelX+i;lx>=0&&(increaseSize(canvas,lx+1,y+1),increaseRoleCanvasSize(rc,lx+1,y+1),setC(lx,y,line[i],"text"))}}}}}return canvasToString(canvas,{roleCanvas:rc,colorMode,theme})}function parseXYChart(lines){let xAxis={},yAxis={},series=[],title,horizontal=!1;for(let line of lines){if(/^xychart(-beta)?\b/i.test(line)){/\bhorizontal\b/i.test(line)&&(horizontal=!0);continue}let titleMatch=line.match(/^title\s+"([^"]+)"/);if(titleMatch){title=titleMatch[1];continue}let xCatMatch=line.match(/^x-axis\s+(?:"([^"]*)"\s*)?\[([^\]]+)\]/);if(xCatMatch){xCatMatch[1]&&(xAxis.title=xCatMatch[1]),xAxis.categories=xCatMatch[2].split(",").map(s=>s.trim());continue}let xRangeMatch=line.match(/^x-axis\s+(?:"([^"]*)"\s+)?(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)/);if(xRangeMatch){xRangeMatch[1]&&(xAxis.title=xRangeMatch[1]),xAxis.range={min:parseFloat(xRangeMatch[2]),max:parseFloat(xRangeMatch[3])};continue}let yRangeMatch=line.match(/^y-axis\s+(?:"([^"]*)"\s+)?(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)/);if(yRangeMatch){yRangeMatch[1]&&(yAxis.title=yRangeMatch[1]),yAxis.range={min:parseFloat(yRangeMatch[2]),max:parseFloat(yRangeMatch[3])};continue}let yTitleOnly=line.match(/^y-axis\s+"([^"]+)"\s*$/);if(yTitleOnly){yAxis.title=yTitleOnly[1];continue}let barMatch=line.match(/^bar\s+\[([^\]]+)\]/);if(barMatch){series.push({type:"bar",data:parseNumericArray(barMatch[1])});continue}let lineMatch=line.match(/^line\s+\[([^\]]+)\]/);if(lineMatch){series.push({type:"line",data:parseNumericArray(lineMatch[1])});continue}}if(!yAxis.range&&series.length>0){let allValues=series.flatMap(s=>s.data),min=Math.min(...allValues),max=Math.max(...allValues),span=max-min||1;min=min-span*.1,max=max+span*.1,min>0&&min<span*.5&&(min=0),yAxis.range={min,max}}return yAxis.range||(yAxis.range={min:0,max:100}),{title,horizontal,xAxis,yAxis,series}}function parseNumericArray(str){return str.split(",").map(s=>parseFloat(s.trim()))}var CHART_ACCENT_FALLBACK="#3b82f6";function hexToHsl(hex){let h=hex.replace("#",""),ri=parseInt(h.substring(0,2),16)/255,gi=parseInt(h.substring(2,4),16)/255,bi=parseInt(h.substring(4,6),16)/255,max=Math.max(ri,gi,bi),min=Math.min(ri,gi,bi),l=(max+min)/2;if(max===min)return[0,0,l*100];let d=max-min,s=l>.5?d/(2-max-min):d/(max+min),hue;return max===ri?hue=((gi-bi)/d+(gi<bi?6:0))/6:max===gi?hue=((bi-ri)/d+2)/6:hue=((ri-gi)/d+4)/6,[hue*360,s*100,l*100]}function hslToHex(h,s,l){let si=s/100,li=l/100,c=(1-Math.abs(2*li-1))*si,x=c*(1-Math.abs(h/60%2-1)),m=li-c/2,r,g,b;h<60?(r=c,g=x,b=0):h<120?(r=x,g=c,b=0):h<180?(r=0,g=c,b=x):h<240?(r=0,g=x,b=c):h<300?(r=x,g=0,b=c):(r=c,g=0,b=x);let toHex=v=>Math.round((v+m)*255).toString(16).padStart(2,"0");return`#${toHex(r)}${toHex(g)}${toHex(b)}`}function isValidHex(color){return/^#[0-9a-fA-F]{6}$/.test(color)}function isDarkBackground(bgHex){return hexToHsl(bgHex)[2]<50}function getSeriesColor(index,accentColor,bgColor){if(index===0)return accentColor;let safeAccent=isValidHex(accentColor)?accentColor:CHART_ACCENT_FALLBACK,safeBg=bgColor&&isValidHex(bgColor)?bgColor:void 0,[h,s]=hexToHsl(safeAccent),chartS=Math.max(55,Math.min(85,s)),tier=Math.ceil(index/2),oddIndex=index%2===1,dark=safeBg&&isDarkBackground(safeBg)?!oddIndex:oddIndex,l=dark?Math.max(25,48-tier*13):Math.min(78,55+tier*11),hShift=(dark?-8:12)*tier,newH=((h+hShift)%360+360)%360;return hslToHex(newH,chartS,l)}var PLOT_WIDTH=60,PLOT_HEIGHT=20,UNI={hLine:"\u2500",vLine:"\u2502",origin:"\u253C",yTick:"\u2524",xTick:"\u252C",bar:"\u2588",grid:"\xB7",cornerTL:"\u256D",cornerTR:"\u256E",cornerBL:"\u2570",cornerBR:"\u256F"},ASC={hLine:"-",vLine:"|",origin:"+",yTick:"+",xTick:"+",bar:"#",grid:".",cornerTL:"+",cornerTR:"+",cornerBL:"+",cornerBR:"+"};function getSeriesColors(total,theme){let accent=theme.accent??CHART_ACCENT_FALLBACK;return total<=1?[accent]:Array.from({length:total},(_,i)=>getSeriesColor(i,accent,theme.bg))}function roleToHex(role,theme){switch(role){case"text":return theme.fg;case"border":return theme.border;case"line":return theme.line;case"arrow":return theme.arrow;case"corner":return theme.corner??theme.line;case"junction":return theme.junction??theme.border;default:return theme.fg}}function renderXYChartAscii(text,config,colorMode,theme){let lines=text.split(`
10`).map(l=>l.trim()).filter(l=>l.length>0&&!l.startsWith("%%")),chart=parseXYChart(lines),ch=config.useAscii?ASC:UNI;return chart.horizontal?renderHorizontal(chart,ch,colorMode,theme):renderVertical(chart,ch,colorMode,theme)}function renderVertical(chart,ch,colorMode,theme){let dataCount=getDataCount(chart);if(dataCount===0)return"";let yRange=chart.yAxis.range,yTicks=niceTickValues(yRange.min,yRange.max),yLabels=yTicks.map(v=>formatTickValue(v)),yGutter=Math.max(...yLabels.map(l=>l.length))+1,plotW=Math.max(PLOT_WIDTH,dataCount*6),plotH=PLOT_HEIGHT,bandW=Math.floor(plotW/dataCount),catLabels=getCategoryLabels(chart,dataCount),hasTitle=!!chart.title,hasXTitle=!!chart.xAxis.title,hasLegend=chart.series.length>1,titleRow=hasTitle?0:-1,plotTop=(hasTitle?2:0)+(hasLegend?1:0),plotLeft=yGutter+1,totalW=plotLeft+bandW*dataCount+2,xAxisRow=plotTop+plotH,xLabelRow=xAxisRow+1,xTitleRow=hasXTitle?xLabelRow+1:-1,totalH=xLabelRow+1+(hasXTitle?1:0)+0,canvas=createCanvas(totalW,totalH),roles=createRoleCanvas(totalW,totalH),hexColors=createHexCanvas(totalW,totalH),seriesColors=getSeriesColors(chart.series.length,theme),valueToRow=v=>{let t=(v-yRange.min)/(yRange.max-yRange.min||1);return Math.round(t*(plotH-1))},bandCenter=i=>plotLeft+Math.floor(bandW*(i+.5));hasTitle&&titleRow>=0&&writeText(canvas,roles,titleRow,Math.floor(totalW/2-chart.title.length/2),chart.title,"text"),hasLegend&&drawLegend(canvas,roles,hexColors,chart,hasTitle?1:0,totalW,ch,seriesColors);for(let row=0;row<plotH;row++){let displayRow=plotTop+(plotH-1-row);set(canvas,roles,displayRow,plotLeft-1,ch.vLine,"border")}set(canvas,roles,xAxisRow,plotLeft-1,ch.origin,"border");for(let tick of yTicks){let row=valueToRow(tick);if(row<0||row>=plotH)continue;let displayRow=plotTop+(plotH-1-row),label=formatTickValue(tick);set(canvas,roles,displayRow,plotLeft-1,row===0?ch.origin:ch.yTick,"border");let labelStart=yGutter-label.length;writeText(canvas,roles,displayRow,Math.max(0,labelStart),label,"text")}for(let c=plotLeft;c<plotLeft+bandW*dataCount;c++)set(canvas,roles,xAxisRow,c,ch.hLine,"border");for(let i=0;i<dataCount;i++){let cx=bandCenter(i);set(canvas,roles,xAxisRow,cx,ch.xTick,"border");let label=catLabels[i],labelStart=cx-Math.floor(label.length/2);writeText(canvas,roles,xLabelRow,Math.max(0,labelStart),label,"text")}if(hasXTitle&&xTitleRow>=0){let title=chart.xAxis.title;writeText(canvas,roles,xTitleRow,Math.floor(totalW/2-title.length/2),title,"text")}for(let tick of yTicks){let row=valueToRow(tick);if(row<0||row>=plotH)continue;let displayRow=plotTop+(plotH-1-row);for(let c=plotLeft;c<plotLeft+bandW*dataCount;c++)get(canvas,displayRow,c)===" "&&set(canvas,roles,displayRow,c,ch.grid,"line")}let barEntries=[];for(let si=0;si<chart.series.length;si++)chart.series[si].type==="bar"&&barEntries.push({data:chart.series[si].data,globalIdx:si});if(barEntries.length>0){let barCount=barEntries.length,usable=Math.max(1,bandW-2),singleBarW=Math.max(1,Math.min(Math.floor(usable/barCount),8)),groupW=singleBarW*barCount+(barCount-1),baseRow=valueToRow(Math.max(0,yRange.min));for(let bIdx=0;bIdx<barEntries.length;bIdx++){let entry=barEntries[bIdx],hexColor=seriesColors[entry.globalIdx];for(let i=0;i<entry.data.length;i++){let bx=bandCenter(i)-Math.floor(groupW/2)+bIdx*(singleBarW+1),valRow=valueToRow(entry.data[i]),fromRow=Math.min(baseRow,valRow),toRow=Math.max(baseRow,valRow);for(let row=fromRow;row<=toRow;row++){let displayRow=plotTop+(plotH-1-row);for(let c=bx;c<bx+singleBarW;c++)set(canvas,roles,displayRow,c,ch.bar,"arrow",hexColors,hexColor)}}}}let lineEntries=[];for(let si=0;si<chart.series.length;si++)chart.series[si].type==="line"&&lineEntries.push({data:chart.series[si].data,globalIdx:si});for(let entry of lineEntries){if(entry.data.length===0)continue;let hexColor=seriesColors[entry.globalIdx];drawStaircaseLine(canvas,roles,entry.data,bandCenter,valueToRow,plotTop,plotH,plotLeft,bandW*dataCount,ch,hexColors,hexColor)}return canvasToString2(canvas,roles,hexColors,colorMode,theme)}function renderHorizontal(chart,ch,colorMode,theme){let dataCount=getDataCount(chart);if(dataCount===0)return"";let yRange=chart.yAxis.range,valueTicks=niceTickValues(yRange.min,yRange.max),catLabels=getCategoryLabels(chart,dataCount),catGutter=Math.max(...catLabels.map(l=>l.length))+1,plotW=Math.max(PLOT_WIDTH,40),bandH=Math.max(2,Math.floor(PLOT_HEIGHT/dataCount)),plotH=bandH*dataCount,hasTitle=!!chart.title,hasYTitle=!!chart.yAxis.title,hasLegend=chart.series.length>1,plotTop=(hasTitle?2:0)+(hasLegend?1:0),plotLeft=catGutter+1,totalW=plotLeft+plotW+2,totalH=plotTop+plotH+2+(hasYTitle?1:0),xAxisRow=plotTop+plotH,canvas=createCanvas(totalW,totalH),roles=createRoleCanvas(totalW,totalH),hexColors=createHexCanvas(totalW,totalH),seriesColors=getSeriesColors(chart.series.length,theme),valueToCol=v=>{let t=(v-yRange.min)/(yRange.max-yRange.min||1);return plotLeft+Math.round(t*(plotW-1))},bandMid=i=>plotTop+Math.floor(bandH*(i+.5));hasTitle&&writeText(canvas,roles,0,Math.floor(totalW/2-chart.title.length/2),chart.title,"text"),hasLegend&&drawLegend(canvas,roles,hexColors,chart,hasTitle?1:0,totalW,ch,seriesColors);for(let r=plotTop;r<plotTop+plotH;r++)set(canvas,roles,r,plotLeft-1,ch.vLine,"border");set(canvas,roles,xAxisRow,plotLeft-1,ch.origin,"border");for(let i=0;i<dataCount;i++){let my=bandMid(i),label=catLabels[i],labelStart=catGutter-label.length;writeText(canvas,roles,my,Math.max(0,labelStart),label,"text")}for(let c=plotLeft;c<plotLeft+plotW;c++)set(canvas,roles,xAxisRow,c,ch.hLine,"border");for(let tick of valueTicks){let cx=valueToCol(tick);if(cx<plotLeft||cx>=plotLeft+plotW)continue;set(canvas,roles,xAxisRow,cx,ch.xTick,"border");let label=formatTickValue(tick);writeText(canvas,roles,xAxisRow+1,cx-Math.floor(label.length/2),label,"text")}if(hasYTitle){let title=chart.yAxis.title;writeText(canvas,roles,totalH-1,Math.floor(totalW/2-title.length/2),title,"text")}for(let tick of valueTicks){let cx=valueToCol(tick);if(!(cx<plotLeft||cx>=plotLeft+plotW))for(let r=plotTop;r<plotTop+plotH;r++)get(canvas,r,cx)===" "&&set(canvas,roles,r,cx,ch.grid,"line")}let barEntries=[];for(let si=0;si<chart.series.length;si++)chart.series[si].type==="bar"&&barEntries.push({data:chart.series[si].data,globalIdx:si});if(barEntries.length>0){let barCount=barEntries.length,singleBarH=1,groupH=singleBarH*barCount+(barCount-1),baseCol=valueToCol(Math.max(0,yRange.min));for(let bIdx=0;bIdx<barEntries.length;bIdx++){let entry=barEntries[bIdx],hexColor=seriesColors[entry.globalIdx];for(let i=0;i<entry.data.length;i++){let by=bandMid(i)-Math.floor(groupH/2)+bIdx*(singleBarH+1),valCol=valueToCol(entry.data[i]),fromCol=Math.min(baseCol,valCol),toCol=Math.max(baseCol,valCol);for(let r=by;r<by+singleBarH;r++)for(let c=fromCol;c<=toCol;c++)set(canvas,roles,r,c,ch.bar,"arrow",hexColors,hexColor)}}}let lineEntries=[];for(let si=0;si<chart.series.length;si++)chart.series[si].type==="line"&&lineEntries.push({data:chart.series[si].data,globalIdx:si});for(let entry of lineEntries){if(entry.data.length===0)continue;let hexColor=seriesColors[entry.globalIdx];drawHorizontalStaircaseLine(canvas,roles,entry.data,bandMid,valueToCol,plotTop,plotH,plotLeft,plotW,ch,hexColors,hexColor)}return canvasToString2(canvas,roles,hexColors,colorMode,theme)}function drawStaircaseLine(canvas,roles,data,bandCenter,valueToRow,plotTop,plotH,plotLeft,plotTotalW,ch,hexCanvas,hexColor){if(data.length===0)return;let points=data.map((v,i)=>({col:bandCenter(i),row:valueToRow(v)})),drawAt=(col,row,char)=>{let displayRow=plotTop+(plotH-1-row);displayRow>=0&&col>=plotLeft&&col<plotLeft+plotTotalW&&set(canvas,roles,displayRow,col,char,"arrow",hexCanvas,hexColor)};if(points.length===1){drawAt(points[0].col,points[0].row,ch.hLine);return}for(let i=0;i<points.length-1;i++){let p1=points[i],p2=points[i+1];if(p1.row===p2.row){for(let c=p1.col;c<=p2.col;c++)drawAt(c,p1.row,ch.hLine);continue}let midCol=Math.round((p1.col+p2.col)/2),goingUp=p2.row>p1.row;for(let c=p1.col;c<midCol;c++)drawAt(c,p1.row,ch.hLine);goingUp?drawAt(midCol,p1.row,ch.cornerBR):drawAt(midCol,p1.row,ch.cornerTR);let minRow=Math.min(p1.row,p2.row),maxRow=Math.max(p1.row,p2.row);for(let row=minRow+1;row<maxRow;row++)drawAt(midCol,row,ch.vLine);goingUp?drawAt(midCol,p2.row,ch.cornerTL):drawAt(midCol,p2.row,ch.cornerBL);for(let c=midCol+1;c<=p2.col;c++)drawAt(c,p2.row,ch.hLine);if(i===0){let leadStart=Math.max(plotLeft,p1.col-Math.floor((p2.col-p1.col)/4));for(let c=leadStart;c<p1.col;c++)drawAt(c,p1.row,ch.hLine)}if(i===points.length-2){let trailEnd=Math.min(plotLeft+plotTotalW-1,p2.col+Math.floor((p2.col-p1.col)/4));for(let c=p2.col+1;c<=trailEnd;c++)drawAt(c,p2.row,ch.hLine)}}}function drawHorizontalStaircaseLine(canvas,roles,data,bandMid,valueToCol,plotTop,plotH,plotLeft,plotW,ch,hexCanvas,hexColor){if(data.length===0)return;let points=data.map((v,i)=>({row:bandMid(i),col:valueToCol(v)})),drawAt=(row,col,char)=>{row>=plotTop&&row<plotTop+plotH&&col>=plotLeft&&col<plotLeft+plotW&&set(canvas,roles,row,col,char,"arrow",hexCanvas,hexColor)};if(points.length===1){drawAt(points[0].row,points[0].col,ch.vLine);return}for(let i=0;i<points.length-1;i++){let p1=points[i],p2=points[i+1];if(p1.col===p2.col){for(let r=p1.row;r<=p2.row;r++)drawAt(r,p1.col,ch.vLine);continue}let midRow=Math.round((p1.row+p2.row)/2),goingRight=p2.col>p1.col;for(let r=p1.row;r<midRow;r++)drawAt(r,p1.col,ch.vLine);goingRight?drawAt(midRow,p1.col,ch.cornerBL):drawAt(midRow,p1.col,ch.cornerBR);let minCol=Math.min(p1.col,p2.col),maxCol=Math.max(p1.col,p2.col);for(let c=minCol+1;c<maxCol;c++)drawAt(midRow,c,ch.hLine);goingRight?drawAt(midRow,p2.col,ch.cornerTR):drawAt(midRow,p2.col,ch.cornerTL);for(let r=midRow+1;r<=p2.row;r++)drawAt(r,p2.col,ch.vLine)}}function drawLegend(canvas,roles,hexCanvas,chart,row,totalW,ch,seriesColors){let items=[],barIdx=0,lineIdx=0;for(let si=0;si<chart.series.length;si++)chart.series[si].type==="bar"?(items.push({symbol:ch.bar,label:`Bar ${barIdx+1}`,globalIdx:si}),barIdx++):(items.push({symbol:ch.hLine,label:`Line ${lineIdx+1}`,globalIdx:si}),lineIdx++);let totalLen=0;for(let i=0;i<items.length;i++)i>0&&(totalLen+=2),totalLen+=2+items[i].label.length;let col=Math.max(0,Math.floor(totalW/2-totalLen/2));for(let i=0;i<items.length;i++){i>0&&(col+=2);let item=items[i];set(canvas,roles,row,col,item.symbol,"arrow",hexCanvas,seriesColors[item.globalIdx]),col+=1,col+=1,writeText(canvas,roles,row,col,item.label,"text"),col+=item.label.length}}function createCanvas(width,height){return Array.from({length:width},()=>Array.from({length:height},()=>" "))}function createRoleCanvas(width,height){return Array.from({length:width},()=>Array.from({length:height}).fill(null))}function createHexCanvas(width,height){return Array.from({length:width},()=>Array.from({length:height}).fill(null))}function set(canvas,roles,row,col,char,role,hexCanvas,hex){col>=0&&col<canvas.length&&row>=0&&row<canvas[0].length&&(canvas[col][row]=char,roles[col][row]=role,hexCanvas&&hex&&(hexCanvas[col][row]=hex))}function get(canvas,row,col){return col>=0&&col<canvas.length&&row>=0&&row<canvas[0].length?canvas[col][row]:" "}function writeText(canvas,roles,row,startCol,text,role){for(let i=0;i<text.length;i++)set(canvas,roles,row,startCol+i,text[i],role)}function canvasToString2(canvas,roles,hexCanvas,colorMode,theme){if(canvas.length===0)return"";let height=canvas[0].length,width=canvas.length,lines=[];for(let row=0;row<height;row++){let chars=[],rowRoles=[],rowHex=[];for(let col=0;col<width;col++)chars.push(canvas[col][row]),rowRoles.push(roles[col][row]),rowHex.push(hexCanvas[col][row]);let end=chars.length-1;for(;end>=0&&chars[end]===" ";)end--;end<0?lines.push(""):lines.push(colorizeRow(chars.slice(0,end+1),rowRoles.slice(0,end+1),rowHex.slice(0,end+1),theme,colorMode))}for(;lines.length>0&&lines[lines.length-1]==="";)lines.pop();return lines.join(`
11`)}function colorizeRow(chars,roles,hexOverrides,theme,mode){if(mode==="none")return chars.join("");let result="",currentColor=null,buffer="";for(let i=0;i<chars.length;i++){let char=chars[i];if(char===" "){buffer.length>0&&(result+=currentColor?colorizeText(buffer,currentColor,mode):buffer,buffer="",currentColor=null),result+=" ";continue}let hexOvr=hexOverrides[i]??null,roleVal=roles[i]??null,color=hexOvr??(roleVal?roleToHex(roleVal,theme):null);color===currentColor?buffer+=char:(buffer.length>0&&(result+=currentColor?colorizeText(buffer,currentColor,mode):buffer),buffer=char,currentColor=color)}return buffer.length>0&&(result+=currentColor?colorizeText(buffer,currentColor,mode):buffer),result}function getDataCount(chart){if(chart.xAxis.categories)return chart.xAxis.categories.length;for(let s of chart.series)if(s.data.length>0)return s.data.length;return 0}function getCategoryLabels(chart,count){if(chart.xAxis.categories)return chart.xAxis.categories;if(chart.xAxis.range){let{min,max}=chart.xAxis.range,step=count>1?(max-min)/(count-1):0;return Array.from({length:count},(_,i)=>formatTickValue(min+step*i))}return Array.from({length:count},(_,i)=>String(i+1))}function niceTickValues(min,max){let range=max-min;if(range<=0)return[min];let rawInterval=range/6,magnitude=Math.pow(10,Math.floor(Math.log10(rawInterval))),residual=rawInterval/magnitude,niceInterval;residual<=1.5?niceInterval=magnitude:residual<=3?niceInterval=2*magnitude:residual<=7?niceInterval=5*magnitude:niceInterval=10*magnitude;let start=Math.ceil(min/niceInterval)*niceInterval,ticks=[];for(let v=start;v<=max+niceInterval*.001;v+=niceInterval)ticks.push(Math.round(v*1e10)/1e10);return ticks}function formatTickValue(v){return Number.isInteger(v)?String(v):v.toFixed(Math.abs(v)<10?1:0)}var setChartSize=(width,height)=>{PLOT_WIDTH=width,PLOT_HEIGHT=height};function detectDiagramType(text){let firstLine=text.trim().split(`
12`)[0]?.trim().toLowerCase()??"";return/^xychart(-beta)?\b/.test(firstLine)?"xychart":/^sequencediagram\s*$/.test(firstLine)?"sequence":/^classdiagram\s*$/.test(firstLine)?"class":/^erdiagram\s*$/.test(firstLine)?"er":"flowchart"}function renderMermaidASCII(text,options={}){let config={useAscii:options.useAscii??!1,paddingX:options.paddingX??5,paddingY:options.paddingY??5,boxBorderPadding:options.boxBorderPadding??1,graphDirection:"TD"},colorMode=options.colorMode==="auto"||options.colorMode===void 0?detectColorMode():options.colorMode,theme={...DEFAULT_ASCII_THEME,...options.theme};switch(detectDiagramType(text)){case"xychart":return renderXYChartAscii(text,config,colorMode,theme);case"sequence":return renderSequenceAscii(text,config,colorMode,theme);case"class":return renderClassAscii(text,config,colorMode,theme);case"er":return renderErAscii(text,config,colorMode,theme);default:{let parsed=parseMermaid(text);parsed.direction==="LR"||parsed.direction==="RL"?config.graphDirection="LR":config.graphDirection="TD";let graph=convertToAsciiGraph(parsed,config);return createMapping(graph),drawGraph(graph),parsed.direction==="BT"&&(flipCanvasVertically(graph.canvas),flipRoleCanvasVertically(graph.roleCanvas)),canvasToString(graph.canvas,{roleCanvas:graph.roleCanvas,colorMode,theme})}}}var renderMermaidAscii=renderMermaidASCII;export{renderMermaidAscii,setChartSize};
13hooks/vendor/prism.js 4 lines1var __getOwnPropNames=Object.getOwnPropertyNames;var __commonJS=(cb,mod)=>function(){try{return mod||(0,cb[__getOwnPropNames(cb)[0]])((mod={exports:{}}).exports,mod),mod.exports}catch(e){throw mod=0,e}};var require_prism_core=__commonJS({"node_modules/prismjs/components/prism-core.js"(exports,module){var _self=typeof window<"u"?window:typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope?self:{};var Prism3=(function(_self2){var lang=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,uniqueId=0,plainTextGrammar={},_={manual:_self2.Prism&&_self2.Prism.manual,disableWorkerMessageHandler:_self2.Prism&&_self2.Prism.disableWorkerMessageHandler,util:{encode:function encode(tokens){return tokens instanceof Token?new Token(tokens.type,encode(tokens.content),tokens.alias):Array.isArray(tokens)?tokens.map(encode):tokens.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(o){return Object.prototype.toString.call(o).slice(8,-1)},objId:function(obj){return obj.__id||Object.defineProperty(obj,"__id",{value:++uniqueId}),obj.__id},clone:function deepClone(o,visited){visited=visited||{};var clone,id;switch(_.util.type(o)){case"Object":if(id=_.util.objId(o),visited[id])return visited[id];clone={},visited[id]=clone;for(var key in o)o.hasOwnProperty(key)&&(clone[key]=deepClone(o[key],visited));return clone;case"Array":return id=_.util.objId(o),visited[id]?visited[id]:(clone=[],visited[id]=clone,o.forEach(function(v,i){clone[i]=deepClone(v,visited)}),clone);default:return o}},getLanguage:function(element){for(;element;){var m=lang.exec(element.className);if(m)return m[1].toLowerCase();element=element.parentElement}return"none"},setLanguage:function(element,language){element.className=element.className.replace(RegExp(lang,"gi"),""),element.classList.add("language-"+language)},currentScript:function(){if(typeof document>"u")return null;if(document.currentScript&&document.currentScript.tagName==="SCRIPT")return document.currentScript;try{throw new Error}catch(err){var src=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(err.stack)||[])[1];if(src){var scripts=document.getElementsByTagName("script");for(var i in scripts)if(scripts[i].src==src)return scripts[i]}return null}},isActive:function(element,className,defaultActivation){for(var no="no-"+className;element;){var classList=element.classList;if(classList.contains(className))return!0;if(classList.contains(no))return!1;element=element.parentElement}return!!defaultActivation}},languages:{plain:plainTextGrammar,plaintext:plainTextGrammar,text:plainTextGrammar,txt:plainTextGrammar,extend:function(id,redef){var lang2=_.util.clone(_.languages[id]);for(var key in redef)lang2[key]=redef[key];return lang2},insertBefore:function(inside,before,insert,root){root=root||_.languages;var grammar=root[inside],ret={};for(var token in grammar)if(grammar.hasOwnProperty(token)){if(token==before)for(var newToken in insert)insert.hasOwnProperty(newToken)&&(ret[newToken]=insert[newToken]);insert.hasOwnProperty(token)||(ret[token]=grammar[token])}var old=root[inside];return root[inside]=ret,_.languages.DFS(_.languages,function(key,value){value===old&&key!=inside&&(this[key]=ret)}),ret},DFS:function DFS(o,callback,type,visited){visited=visited||{};var objId=_.util.objId;for(var i in o)if(o.hasOwnProperty(i)){callback.call(o,i,o[i],type||i);var property=o[i],propertyType=_.util.type(property);propertyType==="Object"&&!visited[objId(property)]?(visited[objId(property)]=!0,DFS(property,callback,null,visited)):propertyType==="Array"&&!visited[objId(property)]&&(visited[objId(property)]=!0,DFS(property,callback,i,visited))}}},plugins:{},highlightAll:function(async,callback){_.highlightAllUnder(document,async,callback)},highlightAllUnder:function(container,async,callback){var env={callback,container,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};_.hooks.run("before-highlightall",env),env.elements=Array.prototype.slice.apply(env.container.querySelectorAll(env.selector)),_.hooks.run("before-all-elements-highlight",env);for(var i=0,element;element=env.elements[i++];)_.highlightElement(element,async===!0,env.callback)},highlightElement:function(element,async,callback){var language=_.util.getLanguage(element),grammar=_.languages[language];_.util.setLanguage(element,language);var parent=element.parentElement;parent&&parent.nodeName.toLowerCase()==="pre"&&_.util.setLanguage(parent,language);var code=element.textContent,env={element,language,grammar,code};function insertHighlightedCode(highlightedCode){env.highlightedCode=highlightedCode,_.hooks.run("before-insert",env),env.element.innerHTML=env.highlightedCode,_.hooks.run("after-highlight",env),_.hooks.run("complete",env),callback&&callback.call(env.element)}if(_.hooks.run("before-sanity-check",env),parent=env.element.parentElement,parent&&parent.nodeName.toLowerCase()==="pre"&&!parent.hasAttribute("tabindex")&&parent.setAttribute("tabindex","0"),!env.code){_.hooks.run("complete",env),callback&&callback.call(env.element);return}if(_.hooks.run("before-highlight",env),!env.grammar){insertHighlightedCode(_.util.encode(env.code));return}if(async&&_self2.Worker){var worker=new Worker(_.filename);worker.onmessage=function(evt){insertHighlightedCode(evt.data)},worker.postMessage(JSON.stringify({language:env.language,code:env.code,immediateClose:!0}))}else insertHighlightedCode(_.highlight(env.code,env.grammar,env.language))},highlight:function(text,grammar,language){var env={code:text,grammar,language};if(_.hooks.run("before-tokenize",env),!env.grammar)throw new Error('The language "'+env.language+'" has no grammar.');return env.tokens=_.tokenize(env.code,env.grammar),_.hooks.run("after-tokenize",env),Token.stringify(_.util.encode(env.tokens),env.language)},tokenize:function(text,grammar){var rest=grammar.rest;if(rest){for(var token in rest)grammar[token]=rest[token];delete grammar.rest}var tokenList=new LinkedList;return addAfter(tokenList,tokenList.head,text),matchGrammar(text,tokenList,grammar,tokenList.head,0),toArray(tokenList)},hooks:{all:{},add:function(name,callback){var hooks=_.hooks.all;hooks[name]=hooks[name]||[],hooks[name].push(callback)},run:function(name,env){var callbacks=_.hooks.all[name];if(!(!callbacks||!callbacks.length))for(var i=0,callback;callback=callbacks[i++];)callback(env)}},Token};_self2.Prism=_;function Token(type,content,alias,matchedStr){this.type=type,this.content=content,this.alias=alias,this.length=(matchedStr||"").length|0}Token.stringify=function stringify(o,language){if(typeof o=="string")return o;if(Array.isArray(o)){var s="";return o.forEach(function(e){s+=stringify(e,language)}),s}var env={type:o.type,content:stringify(o.content,language),tag:"span",classes:["token",o.type],attributes:{},language},aliases=o.alias;aliases&&(Array.isArray(aliases)?Array.prototype.push.apply(env.classes,aliases):env.classes.push(aliases)),_.hooks.run("wrap",env);var attributes="";for(var name in env.attributes)attributes+=" "+name+'="'+(env.attributes[name]||"").replace(/"/g,""")+'"';return"<"+env.tag+' class="'+env.classes.join(" ")+'"'+attributes+">"+env.content+"</"+env.tag+">"};function matchPattern(pattern,pos,text,lookbehind){pattern.lastIndex=pos;var match=pattern.exec(text);if(match&&lookbehind&&match[1]){var lookbehindLength=match[1].length;match.index+=lookbehindLength,match[0]=match[0].slice(lookbehindLength)}return match}function matchGrammar(text,tokenList,grammar,startNode,startPos,rematch){for(var token in grammar)if(!(!grammar.hasOwnProperty(token)||!grammar[token])){var patterns=grammar[token];patterns=Array.isArray(patterns)?patterns:[patterns];for(var j=0;j<patterns.length;++j){if(rematch&&rematch.cause==token+","+j)return;var patternObj=patterns[j],inside=patternObj.inside,lookbehind=!!patternObj.lookbehind,greedy=!!patternObj.greedy,alias=patternObj.alias;if(greedy&&!patternObj.pattern.global){var flags=patternObj.pattern.toString().match(/[imsuy]*$/)[0];patternObj.pattern=RegExp(patternObj.pattern.source,flags+"g")}for(var pattern=patternObj.pattern||patternObj,currentNode=startNode.next,pos=startPos;currentNode!==tokenList.tail&&!(rematch&&pos>=rematch.reach);pos+=currentNode.value.length,currentNode=currentNode.next){var str=currentNode.value;if(tokenList.length>text.length)return;if(!(str instanceof Token)){var removeCount=1,match;if(greedy){if(match=matchPattern(pattern,pos,text,lookbehind),!match||match.index>=text.length)break;var from=match.index,to=match.index+match[0].length,p=pos;for(p+=currentNode.value.length;from>=p;)currentNode=currentNode.next,p+=currentNode.value.length;if(p-=currentNode.value.length,pos=p,currentNode.value instanceof Token)continue;for(var k=currentNode;k!==tokenList.tail&&(p<to||typeof k.value=="string");k=k.next)removeCount++,p+=k.value.length;removeCount--,str=text.slice(pos,p),match.index-=pos}else if(match=matchPattern(pattern,0,str,lookbehind),!match)continue;var from=match.index,matchStr=match[0],before=str.slice(0,from),after=str.slice(from+matchStr.length),reach=pos+str.length;rematch&&reach>rematch.reach&&(rematch.reach=reach);var removeFrom=currentNode.prev;before&&(removeFrom=addAfter(tokenList,removeFrom,before),pos+=before.length),removeRange(tokenList,removeFrom,removeCount);var wrapped=new Token(token,inside?_.tokenize(matchStr,inside):matchStr,alias,matchStr);if(currentNode=addAfter(tokenList,removeFrom,wrapped),after&&addAfter(tokenList,currentNode,after),removeCount>1){var nestedRematch={cause:token+","+j,reach};matchGrammar(text,tokenList,grammar,currentNode.prev,pos,nestedRematch),rematch&&nestedRematch.reach>rematch.reach&&(rematch.reach=nestedRematch.reach)}}}}}}function LinkedList(){var head={value:null,prev:null,next:null},tail={value:null,prev:head,next:null};head.next=tail,this.head=head,this.tail=tail,this.length=0}function addAfter(list,node,value){var next=node.next,newNode={value,prev:node,next};return node.next=newNode,next.prev=newNode,list.length++,newNode}function removeRange(list,node,count){for(var next=node.next,i=0;i<count&&next!==list.tail;i++)next=next.next;node.next=next,next.prev=node,list.length-=i}function toArray(list){for(var array=[],node=list.head.next;node!==list.tail;)array.push(node.value),node=node.next;return array}if(!_self2.document)return _self2.addEventListener&&(_.disableWorkerMessageHandler||_self2.addEventListener("message",function(evt){var message=JSON.parse(evt.data),lang2=message.language,code=message.code,immediateClose=message.immediateClose;_self2.postMessage(_.highlight(code,_.languages[lang2],lang2)),immediateClose&&_self2.close()},!1)),_;var script=_.util.currentScript();script&&(_.filename=script.src,script.hasAttribute("data-manual")&&(_.manual=!0));function highlightAutomaticallyCallback(){_.manual||_.highlightAll()}if(!_.manual){var readyState=document.readyState;readyState==="loading"||readyState==="interactive"&&script&&script.defer?document.addEventListener("DOMContentLoaded",highlightAutomaticallyCallback):window.requestAnimationFrame?window.requestAnimationFrame(highlightAutomaticallyCallback):window.setTimeout(highlightAutomaticallyCallback,16)}return _})(_self);typeof module<"u"&&module.exports&&(module.exports=Prism3);typeof global<"u"&&(global.Prism=Prism3)}});var require_prism_clike=__commonJS({"node_modules/prismjs/components/prism-clike.js"(){Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}}});var require_prism_markup=__commonJS({"node_modules/prismjs/components/prism-markup.js"(){Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]};Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity;Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup;Prism.hooks.add("wrap",function(env){env.type==="entity"&&(env.attributes.title=env.content.replace(/&/,"&"))});Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(tagName,lang){var includedCdataInside={};includedCdataInside["language-"+lang]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[lang]},includedCdataInside.cdata=/^<!\[CDATA\[|\]\]>$/i;var inside={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:includedCdataInside}};inside["language-"+lang]={pattern:/[\s\S]+/,inside:Prism.languages[lang]};var def={};def[tagName]={pattern:RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g,function(){return tagName}),"i"),lookbehind:!0,greedy:!0,inside},Prism.languages.insertBefore("markup","cdata",def)}});Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(attrName,lang){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+attrName+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[lang,"language-"+lang],inside:Prism.languages[lang]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}});Prism.languages.html=Prism.languages.markup;Prism.languages.mathml=Prism.languages.markup;Prism.languages.svg=Prism.languages.markup;Prism.languages.xml=Prism.languages.extend("markup",{});Prism.languages.ssml=Prism.languages.xml;Prism.languages.atom=Prism.languages.xml;Prism.languages.rss=Prism.languages.xml}});var require_prism_css=__commonJS({"node_modules/prismjs/components/prism-css.js"(){(function(Prism3){var string=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;Prism3.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+string.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+string.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+string.source+"$"),alias:"url"}}},selector:{pattern:RegExp(`(^|[{}\\s])[^{}\\s](?:[^{};"'\\s]|\\s+(?![\\s{])|`+string.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:string,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},Prism3.languages.css.atrule.inside.rest=Prism3.languages.css;var markup=Prism3.languages.markup;markup&&(markup.tag.addInlined("style","css"),markup.tag.addAttribute("style","css"))})(Prism)}});var require_prism_javascript=__commonJS({"node_modules/prismjs/components/prism-javascript.js"(){Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+(/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source)+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/});Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/;Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/});Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}});Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}});Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript"));Prism.languages.js=Prism.languages.javascript}});var require_prism_typescript=__commonJS({"node_modules/prismjs/components/prism-typescript.js"(){(function(Prism3){Prism3.languages.typescript=Prism3.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),Prism3.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete Prism3.languages.typescript.parameter,delete Prism3.languages.typescript["literal-property"];var typeInside=Prism3.languages.extend("typescript",{});delete typeInside["class-name"],Prism3.languages.typescript["class-name"].inside=typeInside,Prism3.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:typeInside}}}}),Prism3.languages.ts=Prism3.languages.typescript})(Prism)}});var require_prism_jsx=__commonJS({"node_modules/prismjs/components/prism-jsx.js"(){(function(Prism3){var javascript=Prism3.util.clone(Prism3.languages.javascript),space=/(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source,braces=/(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source,spread=/(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;function re(source,flags){return source=source.replace(/<S>/g,function(){return space}).replace(/<BRACES>/g,function(){return braces}).replace(/<SPREAD>/g,function(){return spread}),RegExp(source,flags)}spread=re(spread).source,Prism3.languages.jsx=Prism3.languages.extend("markup",javascript),Prism3.languages.jsx.tag.pattern=re(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source),Prism3.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,Prism3.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,Prism3.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,Prism3.languages.jsx.tag.inside.comment=javascript.comment,Prism3.languages.insertBefore("inside","attr-name",{spread:{pattern:re(/<SPREAD>/.source),inside:Prism3.languages.jsx}},Prism3.languages.jsx.tag),Prism3.languages.insertBefore("inside","special-attr",{script:{pattern:re(/=<BRACES>/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:Prism3.languages.jsx}}},Prism3.languages.jsx.tag);var stringifyToken=function(token){return token?typeof token=="string"?token:typeof token.content=="string"?token.content:token.content.map(stringifyToken).join(""):""},walkTokens=function(tokens){for(var openedTags=[],i=0;i<tokens.length;i++){var token=tokens[i],notTagNorBrace=!1;if(typeof token!="string"&&(token.type==="tag"&&token.content[0]&&token.content[0].type==="tag"?token.content[0].content[0].content==="</"?openedTags.length>0&&openedTags[openedTags.length-1].tagName===stringifyToken(token.content[0].content[1])&&openedTags.pop():token.content[token.content.length-1].content==="/>"||openedTags.push({tagName:stringifyToken(token.content[0].content[1]),openedBraces:0}):openedTags.length>0&&token.type==="punctuation"&&token.content==="{"?openedTags[openedTags.length-1].openedBraces++:openedTags.length>0&&openedTags[openedTags.length-1].openedBraces>0&&token.type==="punctuation"&&token.content==="}"?openedTags[openedTags.length-1].openedBraces--:notTagNorBrace=!0),(notTagNorBrace||typeof token=="string")&&openedTags.length>0&&openedTags[openedTags.length-1].openedBraces===0){var plainText=stringifyToken(token);i<tokens.length-1&&(typeof tokens[i+1]=="string"||tokens[i+1].type==="plain-text")&&(plainText+=stringifyToken(tokens[i+1]),tokens.splice(i+1,1)),i>0&&(typeof tokens[i-1]=="string"||tokens[i-1].type==="plain-text")&&(plainText=stringifyToken(tokens[i-1])+plainText,tokens.splice(i-1,1),i--),tokens[i]=new Prism3.Token("plain-text",plainText,null,plainText)}token.content&&typeof token.content!="string"&&walkTokens(token.content)}};Prism3.hooks.add("after-tokenize",function(env){env.language!=="jsx"&&env.language!=="tsx"||walkTokens(env.tokens)})})(Prism)}});var require_prism_tsx=__commonJS({"node_modules/prismjs/components/prism-tsx.js"(){(function(Prism3){var typescript=Prism3.util.clone(Prism3.languages.typescript);Prism3.languages.tsx=Prism3.languages.extend("jsx",typescript),delete Prism3.languages.tsx.parameter,delete Prism3.languages.tsx["literal-property"];var tag=Prism3.languages.tsx.tag;tag.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+tag.pattern.source+")",tag.pattern.flags),tag.lookbehind=!0})(Prism)}});var require_prism_python=__commonJS({"node_modules/prismjs/components/prism-python.js"(){Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern://,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/};Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python;Prism.languages.py=Prism.languages.python}});var require_prism_go=__commonJS({"node_modules/prismjs/components/prism-go.js"(){Prism.languages.go=Prism.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/});Prism.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}});delete Prism.languages.go["class-name"]}});var require_prism_rust=__commonJS({"node_modules/prismjs/components/prism-rust.js"(){(function(Prism3){for(var multilineComment=/\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|<self>)*\*\//.source,i=0;i<2;i++)multilineComment=multilineComment.replace(/<self>/g,function(){return multilineComment});multilineComment=multilineComment.replace(/<self>/g,function(){return/[^\s\S]/.source}),Prism3.languages.rust={comment:[{pattern:RegExp(/(^|[^\\])/.source+multilineComment),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<<?=?|>>?=?|[@?]/},Prism3.languages.rust["closure-params"].inside.rest=Prism3.languages.rust,Prism3.languages.rust.attribute.inside.string=Prism3.languages.rust.string})(Prism)}});var require_prism_java=__commonJS({"node_modules/prismjs/components/prism-java.js"(){(function(Prism3){var keywords=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,classNamePrefix=/(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source,className={pattern:RegExp(/(^|[^\w.])/.source+classNamePrefix+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};Prism3.languages.java=Prism3.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[className,{pattern:RegExp(/(^|[^\w.])/.source+classNamePrefix+/[A-Z]\w*(?=\s+\w+\s*[;,=()]|\s*(?:\[[\s,]*\]\s*)?::\s*new\b)/.source),lookbehind:!0,inside:className.inside},{pattern:RegExp(/(\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\s+)/.source+classNamePrefix+/[A-Z]\w*\b/.source),lookbehind:!0,inside:className.inside}],keyword:keywords,function:[Prism3.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),Prism3.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),Prism3.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":className,keyword:keywords,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp(/(\bimport\s+)/.source+classNamePrefix+/(?:[A-Z]\w*|\*)(?=\s*;)/.source),lookbehind:!0,inside:{namespace:className.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp(/(\bimport\s+static\s+)/.source+classNamePrefix+/(?:\w+|\*)(?=\s*;)/.source),lookbehind:!0,alias:"static",inside:{namespace:className.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!<keyword>)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(/<keyword>/g,function(){return keywords.source})),lookbehind:!0,inside:{punctuation:/\./}}})})(Prism)}});var require_prism_kotlin=__commonJS({"node_modules/prismjs/components/prism-kotlin.js"(){(function(Prism3){Prism3.languages.kotlin=Prism3.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete Prism3.languages.kotlin["class-name"];var interpolationInside={"interpolation-punctuation":{pattern:/^\$\{?|\}$/,alias:"punctuation"},expression:{pattern:/[\s\S]+/,inside:Prism3.languages.kotlin}};Prism3.languages.insertBefore("kotlin","string",{"string-literal":[{pattern:/"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/,alias:"multiline",inside:{interpolation:{pattern:/\$(?:[a-z_]\w*|\{[^{}]*\})/i,inside:interpolationInside},string:/[\s\S]+/}},{pattern:/"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/,alias:"singleline",inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i,lookbehind:!0,inside:interpolationInside},string:/[\s\S]+/}}],char:{pattern:/'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/,greedy:!0}}),delete Prism3.languages.kotlin.string,Prism3.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),Prism3.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),Prism3.languages.kt=Prism3.languages.kotlin,Prism3.languages.kts=Prism3.languages.kotlin})(Prism)}});var require_prism_swift=__commonJS({"node_modules/prismjs/components/prism-swift.js"(){Prism.languages.swift={comment:{pattern:/(^|[^\\:])(?:\/\/.*|\/\*(?:[^/*]|\/(?!\*)|\*(?!\/)|\/\*(?:[^*]|\*(?!\/))*\*\/)*\*\/)/,lookbehind:!0,greedy:!0},"string-literal":[{pattern:RegExp(/(^|[^"#])/.source+"(?:"+/"(?:\\(?:\((?:[^()]|\([^()]*\))*\)|\r\n|[^(])|[^\\\r\n"])*"/.source+"|"+/"""(?:\\(?:\((?:[^()]|\([^()]*\))*\)|[^(])|[^\\"]|"(?!""))*"""/.source+")"+/(?!["#])/.source),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\\($/,alias:"punctuation"},punctuation:/\\(?=[\r\n])/,string:/[\s\S]+/}},{pattern:RegExp(/(^|[^"#])(#+)/.source+"(?:"+/"(?:\\(?:#+\((?:[^()]|\([^()]*\))*\)|\r\n|[^#])|[^\\\r\n])*?"/.source+"|"+/"""(?:\\(?:#+\((?:[^()]|\([^()]*\))*\)|[^#])|[^\\])*?"""/.source+")\\2"),lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/(\\#+\()(?:[^()]|\([^()]*\))*(?=\))/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/^\)|\\#+\($/,alias:"punctuation"},string:/[\s\S]+/}}],directive:{pattern:RegExp(/#/.source+"(?:"+(/(?:elseif|if)\b/.source+"(?:[ ]*"+/(?:![ \t]*)?(?:\b\w+\b(?:[ \t]*\((?:[^()]|\([^()]*\))*\))?|\((?:[^()]|\([^()]*\))*\))(?:[ \t]*(?:&&|\|\|))?/.source+")+")+"|"+/(?:else|endif)\b/.source+")"),alias:"property",inside:{"directive-name":/^#\w+/,boolean:/\b(?:false|true)\b/,number:/\b\d+(?:\.\d+)*\b/,operator:/!|&&|\|\||[<>]=?/,punctuation:/[(),]/}},literal:{pattern:/#(?:colorLiteral|column|dsohandle|file(?:ID|Literal|Path)?|function|imageLiteral|line)\b/,alias:"constant"},"other-directive":{pattern:/#\w+\b/,alias:"property"},attribute:{pattern:/@\w+/,alias:"atrule"},"function-definition":{pattern:/(\bfunc\s+)\w+/,lookbehind:!0,alias:"function"},label:{pattern:/\b(break|continue)\s+\w+|\b[a-zA-Z_]\w*(?=\s*:\s*(?:for|repeat|while)\b)/,lookbehind:!0,alias:"important"},keyword:/\b(?:Any|Protocol|Self|Type|actor|as|assignment|associatedtype|associativity|async|await|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|else|enum|extension|fallthrough|fileprivate|final|for|func|get|guard|higherThan|if|import|in|indirect|infix|init|inout|internal|is|isolated|lazy|left|let|lowerThan|mutating|none|nonisolated|nonmutating|open|operator|optional|override|postfix|precedencegroup|prefix|private|protocol|public|repeat|required|rethrows|return|right|safe|self|set|some|static|struct|subscript|super|switch|throw|throws|try|typealias|unowned|unsafe|var|weak|where|while|willSet)\b/,boolean:/\b(?:false|true)\b/,nil:{pattern:/\bnil\b/,alias:"constant"},"short-argument":/\$\d+\b/,omit:{pattern:/\b_\b/,alias:"keyword"},number:/\b(?:[\d_]+(?:\.[\de_]+)?|0x[a-f0-9_]+(?:\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,"class-name":/\b[A-Z](?:[A-Z_\d]*[a-z]\w*)?\b/,function:/\b[a-z_]\w*(?=\s*\()/i,constant:/\b(?:[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,operator:/[-+*/%=!<>&|^~?]+|\.[.\-+*/%=!<>&|^~?]+/,punctuation:/[{}[\]();,.:\\]/};Prism.languages.swift["string-literal"].forEach(function(rule){rule.inside.interpolation.inside=Prism.languages.swift})}});var require_prism_c=__commonJS({"node_modules/prismjs/components/prism-c.js"(){Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/});Prism.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}});Prism.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],char:Prism.languages.c.char,comment:Prism.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}}});Prism.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/});delete Prism.languages.c.boolean}});var require_prism_cpp=__commonJS({"node_modules/prismjs/components/prism-cpp.js"(){(function(Prism3){var keyword=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,modName=/\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g,function(){return keyword.source});Prism3.languages.cpp=Prism3.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace(/<keyword>/g,function(){return keyword.source})),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),Prism3.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g,function(){return modName})+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),Prism3.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:Prism3.languages.cpp}}}}),Prism3.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),Prism3.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:Prism3.languages.extend("cpp",{})}}),Prism3.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},Prism3.languages.cpp["base-clause"])})(Prism)}});var require_prism_csharp=__commonJS({"node_modules/prismjs/components/prism-csharp.js"(){(function(Prism3){function replace(pattern,replacements){return pattern.replace(/<<(\d+)>>/g,function(m,index){return"(?:"+replacements[+index]+")"})}function re(pattern,replacements,flags){return RegExp(replace(pattern,replacements),flags||"")}function nested(pattern,depthLog2){for(var i=0;i<depthLog2;i++)pattern=pattern.replace(/<<self>>/g,function(){return"(?:"+pattern+")"});return pattern.replace(/<<self>>/g,"[^\\s\\S]")}var keywordKinds={type:"bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",typeDeclaration:"class enum interface record struct",contextual:"add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)",other:"abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield"};function keywordsToPattern(words){return"\\b(?:"+words.trim().replace(/ /g,"|")+")\\b"}var typeDeclarationKeywords=keywordsToPattern(keywordKinds.typeDeclaration),keywords=RegExp(keywordsToPattern(keywordKinds.type+" "+keywordKinds.typeDeclaration+" "+keywordKinds.contextual+" "+keywordKinds.other)),nonTypeKeywords=keywordsToPattern(keywordKinds.typeDeclaration+" "+keywordKinds.contextual+" "+keywordKinds.other),nonContextualKeywords=keywordsToPattern(keywordKinds.type+" "+keywordKinds.typeDeclaration+" "+keywordKinds.other),generic=nested(/<(?:[^<>;=+\-*/%&|^]|<<self>>)*>/.source,2),nestedRound=nested(/\((?:[^()]|<<self>>)*\)/.source,2),name=/@?\b[A-Za-z_]\w*\b/.source,genericName=replace(/<<0>>(?:\s*<<1>>)?/.source,[name,generic]),identifier=replace(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source,[nonTypeKeywords,genericName]),array=/\[\s*(?:,\s*)*\]/.source,typeExpressionWithoutTuple=replace(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source,[identifier,array]),tupleElement=replace(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source,[generic,nestedRound,array]),tuple=replace(/\(<<0>>+(?:,<<0>>+)+\)/.source,[tupleElement]),typeExpression=replace(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source,[tuple,identifier,array]),typeInside={keyword:keywords,punctuation:/[<>()?,.:[\]]/},character=/'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source,regularString=/"(?:\\.|[^\\"\r\n])*"/.source,verbatimString=/@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;Prism3.languages.csharp=Prism3.languages.extend("clike",{string:[{pattern:re(/(^|[^$\\])<<0>>/.source,[verbatimString]),lookbehind:!0,greedy:!0},{pattern:re(/(^|[^@$\\])<<0>>/.source,[regularString]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:re(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source,[identifier]),lookbehind:!0,inside:typeInside},{pattern:re(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source,[name,typeExpression]),lookbehind:!0,inside:typeInside},{pattern:re(/(\busing\s+)<<0>>(?=\s*=)/.source,[name]),lookbehind:!0},{pattern:re(/(\b<<0>>\s+)<<1>>/.source,[typeDeclarationKeywords,genericName]),lookbehind:!0,inside:typeInside},{pattern:re(/(\bcatch\s*\(\s*)<<0>>/.source,[identifier]),lookbehind:!0,inside:typeInside},{pattern:re(/(\bwhere\s+)<<0>>/.source,[name]),lookbehind:!0},{pattern:re(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source,[typeExpressionWithoutTuple]),lookbehind:!0,inside:typeInside},{pattern:re(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source,[typeExpression,nonContextualKeywords,name]),inside:typeInside}],keyword:keywords,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),Prism3.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),Prism3.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:re(/([(,]\s*)<<0>>(?=\s*:)/.source,[name]),lookbehind:!0,alias:"punctuation"}}),Prism3.languages.insertBefore("csharp","class-name",{namespace:{pattern:re(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source,[name]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:re(/(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source,[nestedRound]),lookbehind:!0,alias:"class-name",inside:typeInside},"return-type":{pattern:re(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source,[typeExpression,identifier]),inside:typeInside,alias:"class-name"},"constructor-invocation":{pattern:re(/(\bnew\s+)<<0>>(?=\s*[[({])/.source,[typeExpression]),lookbehind:!0,inside:typeInside,alias:"class-name"},"generic-method":{pattern:re(/<<0>>\s*<<1>>(?=\s*\()/.source,[name,generic]),inside:{function:re(/^<<0>>/.source,[name]),generic:{pattern:RegExp(generic),alias:"class-name",inside:typeInside}}},"type-list":{pattern:re(/\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source,[typeDeclarationKeywords,genericName,name,typeExpression,keywords.source,nestedRound,/\bnew\s*\(\s*\)/.source]),lookbehind:!0,inside:{"record-arguments":{pattern:re(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source,[genericName,nestedRound]),lookbehind:!0,greedy:!0,inside:Prism3.languages.csharp},keyword:keywords,"class-name":{pattern:RegExp(typeExpression),greedy:!0,inside:typeInside},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var regularStringOrCharacter=regularString+"|"+character,regularStringCharacterOrComment=replace(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source,[regularStringOrCharacter]),roundExpression=nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source,[regularStringCharacterOrComment]),2),attrTarget=/\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source,attr=replace(/<<0>>(?:\s*\(<<1>>*\))?/.source,[identifier,roundExpression]);Prism3.languages.insertBefore("csharp","class-name",{attribute:{pattern:re(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source,[attrTarget,attr]),lookbehind:!0,greedy:!0,inside:{target:{pattern:re(/^<<0>>(?=\s*:)/.source,[attrTarget]),alias:"keyword"},"attribute-arguments":{pattern:re(/\(<<0>>*\)/.source,[roundExpression]),inside:Prism3.languages.csharp},"class-name":{pattern:RegExp(identifier),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var formatString=/:[^}\r\n]+/.source,mInterpolationRound=nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source,[regularStringCharacterOrComment]),2),mInterpolation=replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[mInterpolationRound,formatString]),sInterpolationRound=nested(replace(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<<self>>*\)/.source,[regularStringOrCharacter]),2),sInterpolation=replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[sInterpolationRound,formatString]);function createInterpolationInside(interpolation,interpolationRound){return{interpolation:{pattern:re(/((?:^|[^{])(?:\{\{)*)<<0>>/.source,[interpolation]),lookbehind:!0,inside:{"format-string":{pattern:re(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source,[interpolationRound,formatString]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:Prism3.languages.csharp}}},string:/[\s\S]+/}}Prism3.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:re(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source,[mInterpolation]),lookbehind:!0,greedy:!0,inside:createInterpolationInside(mInterpolation,mInterpolationRound)},{pattern:re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source,[sInterpolation]),lookbehind:!0,greedy:!0,inside:createInterpolationInside(sInterpolation,sInterpolationRound)}],char:{pattern:RegExp(character),greedy:!0}}),Prism3.languages.dotnet=Prism3.languages.cs=Prism3.languages.csharp})(Prism)}});var require_prism_ruby=__commonJS({"node_modules/prismjs/components/prism-ruby.js"(){(function(Prism3){Prism3.languages.ruby=Prism3.languages.extend("clike",{comment:{pattern:/#.*|^=begin\s[\s\S]*?^=end/m,greedy:!0},"class-name":{pattern:/(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,operator:/\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,punctuation:/[(){}[\].,;]/}),Prism3.languages.insertBefore("ruby","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}});var interpolation={pattern:/((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,lookbehind:!0,inside:{content:{pattern:/^(#\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:Prism3.languages.ruby},delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"}}};delete Prism3.languages.ruby.function;var percentExpression="(?:"+[/([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,/\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,/<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source].join("|")+")",symbolName=/(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source;Prism3.languages.insertBefore("ruby","keyword",{"regex-literal":[{pattern:RegExp(/%r/.source+percentExpression+/[egimnosux]{0,6}/.source),greedy:!0,inside:{interpolation,regex:/[\s\S]+/}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation,regex:/[\s\S]+/}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:[{pattern:RegExp(/(^|[^:]):/.source+symbolName),lookbehind:!0,greedy:!0},{pattern:RegExp(/([\r\n{(,][ \t]*)/.source+symbolName+/(?=:(?!:))/.source),lookbehind:!0,greedy:!0}],"method-definition":{pattern:/(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,lookbehind:!0,inside:{function:/\b\w+$/,keyword:/^self\b/,"class-name":/^\w+/,punctuation:/\./}}}),Prism3.languages.insertBefore("ruby","string",{"string-literal":[{pattern:RegExp(/%[qQiIwWs]?/.source+percentExpression),greedy:!0,inside:{interpolation,string:/[\s\S]+/}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation,string:/[\s\S]+/}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?/}},interpolation,string:/[\s\S]+/}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?'|'$/}},string:/[\s\S]+/}}],"command-literal":[{pattern:RegExp(/%x/.source+percentExpression),greedy:!0,inside:{interpolation,command:{pattern:/[\s\S]+/,alias:"string"}}},{pattern:/`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,greedy:!0,inside:{interpolation,command:{pattern:/[\s\S]+/,alias:"string"}}}]}),delete Prism3.languages.ruby.string,Prism3.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,constant:/\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/}),Prism3.languages.rb=Prism3.languages.ruby})(Prism)}});var require_prism_json=__commonJS({"node_modules/prismjs/components/prism-json.js"(){Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}};Prism.languages.webmanifest=Prism.languages.json}});var require_prism_yaml=__commonJS({"node_modules/prismjs/components/prism-yaml.js"(){(function(Prism3){var anchorOrAlias=/[*&][^\s[\]{},]+/,tag=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,properties="(?:"+tag.source+"(?:[ ]+"+anchorOrAlias.source+")?|"+anchorOrAlias.source+"(?:[ ]+"+tag.source+")?)",plainKey=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-]<PLAIN>)(?:[ \t]*(?:(?![#:])<PLAIN>|:<PLAIN>))*/.source.replace(/<PLAIN>/g,function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source}),string=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function createValuePattern(value,flags){flags=(flags||"").replace(/m/g,"")+"m";var pattern=/([:\-,[{]\s*(?:\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<<prop>>/g,function(){return properties}).replace(/<<value>>/g,function(){return value});return RegExp(pattern,flags)}Prism3.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<<prop>>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<<prop>>/g,function(){return properties})),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<<prop>>[ \t]+)?)<<key>>(?=\s*:\s)/.source.replace(/<<prop>>/g,function(){return properties}).replace(/<<key>>/g,function(){return"(?:"+plainKey+"|"+string+")"})),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:createValuePattern(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:createValuePattern(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:createValuePattern(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:createValuePattern(string),lookbehind:!0,greedy:!0},number:{pattern:createValuePattern(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag,important:anchorOrAlias,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},Prism3.languages.yml=Prism3.languages.yaml})(Prism)}});var require_prism_toml=__commonJS({"node_modules/prismjs/components/prism-toml.js"(){(function(Prism3){var key=/(?:[\w-]+|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*")/.source;function insertKey(pattern){return pattern.replace(/__/g,function(){return key})}Prism3.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(insertKey(/(^[\t ]*\[\s*(?:\[\s*)?)__(?:\s*\.\s*__)*(?=\s*\])/.source),"m"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp(insertKey(/(^[\t ]*|[{,]\s*)__(?:\s*\.\s*__)*(?=\s*=)/.source),"m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?\b/i,alias:"number"},{pattern:/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?\b(?:inf|nan)\b/,boolean:/\b(?:false|true)\b/,punctuation:/[.,=[\]{}]/}})(Prism)}});var require_prism_sql=__commonJS({"node_modules/prismjs/components/prism-sql.js"(){Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}}});var require_prism_diff=__commonJS({"node_modules/prismjs/components/prism-diff.js"(){(function(Prism3){Prism3.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var PREFIXES={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(PREFIXES).forEach(function(name){var prefix=PREFIXES[name],alias=[];/^\w+$/.test(name)||alias.push(/\w+/.exec(name)[0]),name==="diff"&&alias.push("bold"),Prism3.languages.diff[name]={pattern:RegExp("^(?:["+prefix+`].*(?:\r
2?|
3|(?![\\s\\S])))+`,"m"),alias,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(name)[0]}}}}),Object.defineProperty(Prism3.languages.diff,"PREFIXES",{value:PREFIXES})})(Prism)}});var require_prism_docker=__commonJS({"node_modules/prismjs/components/prism-docker.js"(){(function(Prism3){var spaceAfterBackSlash=/\\[\r\n](?:\s|\\[\r\n]|#.*(?!.))*(?![\s#]|\\[\r\n])/.source,space=/(?:[ \t]+(?![ \t])(?:<SP_BS>)?|<SP_BS>)/.source.replace(/<SP_BS>/g,function(){return spaceAfterBackSlash}),string=/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"|'(?:[^'\\\r\n]|\\(?:\r\n|[\s\S]))*'/.source,option=/--[\w-]+=(?:<STR>|(?!["'])(?:[^\s\\]|\\.)+)/.source.replace(/<STR>/g,function(){return string}),stringRule={pattern:RegExp(string),greedy:!0},commentRule={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function re(source,flags){return source=source.replace(/<OPT>/g,function(){return option}).replace(/<SP>/g,function(){return space}),RegExp(source,flags)}Prism3.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:re(/(^(?:ONBUILD<SP>)?\w+<SP>)<OPT>(?:<SP><OPT>)*/.source,"i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[stringRule,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:re(/(^(?:ONBUILD<SP>)?HEALTHCHECK<SP>(?:<OPT><SP>)*)(?:CMD|NONE)\b/.source,"i"),lookbehind:!0,greedy:!0},{pattern:re(/(^(?:ONBUILD<SP>)?FROM<SP>(?:<OPT><SP>)*(?!--)[^ \t\\]+<SP>)AS/.source,"i"),lookbehind:!0,greedy:!0},{pattern:re(/(^ONBUILD<SP>)\w+/.source,"i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:commentRule,string:stringRule,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,operator:/\\$/m}},comment:commentRule},Prism3.languages.dockerfile=Prism3.languages.docker})(Prism)}});var require_prism_hcl=__commonJS({"node_modules/prismjs/components/prism-hcl.js"(){Prism.languages.hcl={comment:/(?:\/\/|#).*|\/\*[\s\S]*?(?:\*\/|$)/,heredoc:{pattern:/<<-?(\w+\b)[\s\S]*?^[ \t]*\1/m,greedy:!0,alias:"string"},keyword:[{pattern:/(?:data|resource)\s+(?:"(?:\\[\s\S]|[^\\"])*")(?=\s+"[\w-]+"\s+\{)/i,inside:{type:{pattern:/(resource|data|\s+)(?:"(?:\\[\s\S]|[^\\"])*")/i,lookbehind:!0,alias:"variable"}}},{pattern:/(?:backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+(?=\{)/i,inside:{type:{pattern:/(backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+/i,lookbehind:!0,alias:"variable"}}},/[\w-]+(?=\s+\{)/],property:[/[-\w\.]+(?=\s*=(?!=))/,/"(?:\\[\s\S]|[^\\"])+"(?=\s*[:=])/],string:{pattern:/"(?:[^\\$"]|\\[\s\S]|\$(?:(?=")|\$+(?!\$)|[^"${])|\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\})*"/,greedy:!0,inside:{interpolation:{pattern:/(^|[^$])\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\}/,lookbehind:!0,inside:{type:{pattern:/(\b(?:count|data|local|module|path|self|terraform|var)\b\.)[\w\*]+/i,lookbehind:!0,alias:"variable"},keyword:/\b(?:count|data|local|module|path|self|terraform|var)\b/i,function:/\w+(?=\()/,string:{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,punctuation:/[!\$#%&'()*+,.\/;<=>@\[\\\]^`{|}~?:]/}}}},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,boolean:/\b(?:false|true)\b/i,punctuation:/[=\[\]{}]/}}});var Prism2=require_prism_core();globalThis.Prism=Prism2;require_prism_clike();require_prism_markup();require_prism_css();require_prism_javascript();require_prism_typescript();require_prism_jsx();require_prism_tsx();require_prism_python();require_prism_go();require_prism_rust();require_prism_java();require_prism_kotlin();require_prism_swift();require_prism_c();require_prism_cpp();require_prism_csharp();require_prism_ruby();require_prism_json();require_prism_yaml();require_prism_toml();require_prism_sql();require_prism_diff();require_prism_docker();require_prism_hcl();var languages=Prism2.languages,tokenize=(code,grammar)=>Prism2.tokenize(code,grammar);export{languages,tokenize};
4types/index.d.ts 16 lines1export type PrismantisMarkdownArgs<Surface> = {
2 surface: Surface
3 text: string
4 columns: number
5}
6
7export type Prismantis<Surface, Drawing> = {
8 markdown: (args: PrismantisMarkdownArgs<Surface>) => Promise<Drawing | undefined>
9}
10
11declare module 'claude-code' {
12 interface EngineInterface {
13 prismantis: Prismantis<RenderSurface, RenderElement>
14 }
15}
16