SLOPSHOPPER

prismantis

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

newspinnerrowscommandtoastprompt
★ 3v0.11.0MITupdated 2026-10-11ahmadhammoud56/MOAA/plugins/prismantis
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · prismantis
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ fix the failing auth test and add an audit log call │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ⏺ Read(src/auth.ts) ⎿ Read 6 lines Edited /work/app/src/auth.ts ● ⏺ Bash(bun test) ⎿ 3 pass, 1 fail Done. I made refresh reject expired claims and added an audit call. ✻ Worked for 42s › /prismantis Commands ━━━━━━━━ [ ⧉ copy ] [ ⧉ art ] [ ▣ png ] ┌──────────────────────────┬───────────────────────────────────────────────────────────┐ │ Command │ Does │ ╞══════════════════════════╪═══════════════════════════════════════════════════════════╡ │ /prismantis theme <name> │ Switch theme on the spot │ ├──────────────────────────┼───────────────────────────────────────────────────────────┤ │ /prismantis copy │ Copy the last reply, or copy code for its last code block │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Turn line
✻ Worked for 42s
Your message
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │ fix the failing auth test and add an audit log call │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Claude's reply
Done. I made refresh reject expired claims and added an audit call.
Tool row
Edited /work/app/src/auth.ts ●
Command output
Commands ━━━━━━━━ [ ⧉ copy ] [ ⧉ art ] [ ▣ png ] ┌──────────────────────────┬───────────────────────────────────────────────────────────┐ │ Command │ Does │ ╞══════════════════════════╪═══════════════════════════════════════════════════════════╡ │ /prismantis theme <name> │ Switch theme on the spot │ ├──────────────────────────┼───────────────────────────────────────────────────────────┤ │ /prismantis copy │ Copy the last reply, or copy code for its last code block │ ├──────────────────────────┼───────────────────────────────────────────────────────────┤ │ /prismantis demo │ Full showcase, every element and diagram │ ├──────────────────────────┼───────────────────────────────────────────────────────────┤ │ /prismantis demo-rtl │ Hebrew right-to-left showcase │ └──────────────────────────┴───────────────────────────────────────────────────────────┘ 16 themes [ ⧉ copy ] • Dark: catppuccin-mocha, 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 • Plain: mono, no color, only bold and dim Docs and issues: https://github.com/NahumLitvin/prismantis ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Tip │ │ Any color slot beats the theme. Set headingColor or numberColor to a hex value in /config. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ Task lists [ ⧉ copy ] [✓] Tables, diagrams and charts drawn in the terminal [✓] A copy button on every block [ ] Pick a taskStyle in /config: checks, ticks, box or progress [ ⧉ source ] [ ⧉ art ] [ ▣ png ] ┌───────┐ ┌───────┐ ┌──────┐ ┌────────┐ │ Reply ├──►│ Parse ├──►│ Draw ├──►│ Screen │ └───────┘ └───────┘ └──────┘ └────────┘ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Caution │ │ Claude Code refuses trees over 20000 nodes. A code block past about 500 highlighted lines falls │ │ back to plain text. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
README

<h1 align="center"><img src="docs/brand/banner.png" alt="prismantis" width="640"></h1>

ci

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.

prismantis in a real Claude Code session: tool calls dimmed on the right and Claude on the left, your prompt in a bubble, then headings, an alert, a table, a bar chart and a flowchart

prismantis on the default Catppuccin Mocha theme: a boxed title, a section heading, a table, a nested list, a flowchart, a sequence diagram, a bar chart with values and its tallest bar highlighted, a line chart, highlighted TypeScript and shell blocks, a tip alert and copy buttons

Features

FeatureWhat 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
Tablescolored headers, rules, column alignment, colored numbers, sized to the terminal
Codea language header and copy button, Prism highlighting in 24 languages, shell lines colored like a prompt
Diagrams and chartsflowcharts, sequence, state, class and ER diagrams, bar and line charts, one color per box, participant and bar
Layoutback-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 rowsRan 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 commandscommand 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 hintsa short model-only note on each prompt so Claude reaches for diagrams and charts when they help
Textbold, italic, strikethrough, inline code, links, versions, durations, percentages and paths in their own colors
Headings, lists, quotes4 heading styles, nested lists, task lists, quotes with an accent bar
Your promptswhat 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 leftHebrew 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.

Install

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.

Themes

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.

Tables

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

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.

  • Prism highlights JavaScript, TypeScript, JSX/TSX, Python, Go, Rust, Java, Kotlin, Swift, C, C++, C#, Ruby, JSON, YAML, TOML, SQL, HTML, CSS, Dockerfile, HCL and diff: keywords, strings, numbers, comments, keys, functions and properties each get a theme color.
  • Shell blocks (bash, sh, zsh, unlabeled) color the command word, --flags, quoted strings and # comments, and restart after |, && and ;.

Diagrams and charts

Code blocks tagged mermaid draw as colored text art:

  • flowcharts (graph LR, graph TD, decisions), sequence, state, class and ER diagrams
  • bar and line charts with xychart-beta, sized to the terminal width
  • each box and participant gets its own theme color, the same at both ends of a sequence diagram
  • each bar gets its own color, gridlines stay dim, axis numbers use the number color

Diagrams too wide for the window, or over 80 lines, stay as code. mermaidAscii swaps box-drawing characters for + - |. Pie charts are not supported.

Layout

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 buttons

[ ⧉ 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.

Tool rows

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.

chat: tool rows dimmed on the right

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

tree-dim: tool rows tucked under with ⎿

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

tree-bold: one-line sentences in bold

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

classic: bold verb and status dot

Turn footer

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.

Slash commands

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.

Other mods

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.

  • It answers the tree to draw, or undefined when prismantis is disabled or the text holds nothing to draw.
  • It throws when prismantis isn't installed, so call it in a try.
  • columns is the width of your content. Prismantis draws at least 20 columns wide.
  • Theme, RTL and number and path highlighting follow the user's prismantis settings.
  • Copy buttons are left out, because a button can't cross from one mod to another.
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>
    }
  }
}

Diagram hints

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.

Text

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.

Headings, lists, quotes

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.

A task list with done items struck through

Your prompts

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)barchevron
a prompt in a rounded boxa prompt with an accent bara prompt with a bold chevron

Right to left

Hebrew drawn right to left

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.

Configure

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"
    }
  }
}
OptionValuesDefault
enabledtrue, falsetrue
themesee Themescatppuccin-mocha
tableStylebox, rules, grid, minimalbox
taskStylechecks ([ ] [✓], done struck through), ticks (○ ✓), box (□ ✓), progress (ticks with a done-count bar)checks
promptStylebubble, bar, chevron, offbubble
headingStylebanner, bold, underline, uppercasebanner
highlightNumberstrue, falsetrue
highlightPathstrue, falsetrue
toolRowstrue, falsetrue
toolStylechat, tree-dim, tree-bold, classicchat
copyButtonstrue, falsetrue
diagramHintstrue, falsetrue
rtlauto, a terminal (warp, kitty, apple-terminal, iterm, ghostty, wezterm, vscode, alacritty, windows-terminal, gnome, konsole), offauto
mermaidtrue, falsetrue
mermaidAsciitrue, falsefalse
<token>Colorany color, see belowtheme

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:

TokenColors
accentreply bullet, H3+ headings, quote bar, running tool dots
headingH1 and H2
strongbold text
emphasisitalic text, variables, attribute names
inlineCodeinline code
codeTextcode block text
codeCommandshell commands, functions, class names, keys
codeFlag--flags, keywords, failures
codeStringstrings
codeCommentcomments, the code block's language label
linklinks, URLs, properties, tags
pathfile paths, regexes
numbernumbers, versions, durations, done dots
quotequote text
rulerules, chart gridlines
tableHeadertable header cells
tableRuletable rules
bulletlist bullets and numbers
diagramdiagram lines
diagramTextdiagram labels

Limits

  • The parser covers what Claude writes (headings, lists, tables, fences, quotes, emphasis, links). It's not full CommonMark: nested quotes and HTML draw as plain text.
  • Widths count CJK and emoji as two columns. Terminals disagree on a few emoji, so those can still be off by one.- Languages outside the 24 above draw in codeText.

Roadmap

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.

Develop

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.

Author

Built by Nahum Litvin, who writes about running untrusted code in production at catchkill9.dev.

License

MIT. Bundled third-party code is listed in THIRD_PARTY_NOTICES.md.

Source 11 files
hooks/register.tsx 214 lines
1import 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}
214
hooks/markdown.ts 208 lines
1export 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}
208
hooks/mermaid.tsx 138 lines
1import 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}
138
hooks/render.tsx 728 lines
1import 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}
728
hooks/help.ts 175 lines
1export 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`
175
hooks/presets.ts 99 lines
1import 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[]
99
hooks/theme.ts 66 lines
1import 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}
66
hooks/rtl.ts 189 lines
1import 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)
189
hooks/vendor/mermaid-text.js 13 lines
1function 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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}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&&currentRole!==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==="}"&&currentNamespace){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&&currentNamespace.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&&currentNamespace.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&&currentEntity.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};
13
hooks/vendor/prism.js 4 lines
1var __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,"&amp;").replace(/</g,"&lt;").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,"&quot;")+'"';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(/&amp;/,"&"))});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:/![sra](?=[:}]$)/,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};
4
types/index.d.ts 16 lines
1export 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